tag:blogger.com,1999:blog-74734261296610641952021-10-25T20:39:52.230+03:00My Engineering WorldA blog about various engineering subjects, as well as office tips. Many articles, office tips and freeware tools.Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comBlogger163125tag:blogger.com,1999:blog-7473426129661064195.post-15669092737079444552018-12-27T19:42:00.000+02:002019-01-05T21:05:54.797+02:00Get Email Information Into Google Sheets Using GAS<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;">Last updated: 05/01/2019, 1 min read (without the code)<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Get Email Information Into Google Sheets Using GAS" border="0" data-original-height="523" data-original-width="550" src="https://2.bp.blogspot.com/-Ac2JrhR7hjM/XDDoCI86KgI/AAAAAAAAD-0/4ZlRuViQrnk6Dr6xl7vm2Njhxx2ZXH1BACLcBGAs/s1600/Get%2BEmail%2BInformation%2BInto%2BGoogle%2BSheets%2BUsing%2BGAS.jpg" title="Get Email Information Into Google Sheets Using GAS" /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div>We have already seen how to <a href="https://www.myengineeringworld.net/2018/11/gas-send-emails-google-sheets.html">send emails directly from Google Sheets</a>. In this post, we will learn the opposite: <span style="color: red;">how to retrieve email information using Google Apps Script</span>. We will assume that you have a valid Google account hence, the information you want to retrieve comes from your Gmail.<br /><br /><span style="color: red;">The GAS code relies on a search pattern to perform the searching in your mailbox.</span> There are several options you can use in this pattern: email subject/title, the sender (name or email address), the recipient (name or email address), label, file type and several others. <span style="color: orange;">In other words, you can perform a powerful query in your mailbox directly from Google Sheets.</span><br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">The process</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div>There are several steps involved to get the necessary email data. However, all of them have been presented in previous posts so we will avoid duplication here. Let’s see them in brief:<br /><br /><span style="color: red;">Step 1:</span> Following the instructions described <a href="https://www.myengineeringworld.net/2018/08/create-use-custom-gas-function.html">here</a> (steps 1 to 4) <span style="color: orange;">create a new empty spreadsheet</span> on your Google Drive.<br /><br /><span style="color: red;">Step 2:</span> Then, follow <a href="https://www.myengineeringworld.net/2018/11/gas-send-emails-google-sheets.html">these instructions</a> (steps 2 to 5), so as to <span style="color: orange;">switch to the Script Editor</span>. In there, you have to paste the code that you will find below.<br /><br /><span style="color: red;">Step 3:</span> Finally, if you perform the steps 7 to 14 described <a href="https://www.myengineeringworld.net/2018/11/gas-send-emails-google-sheets.html">in this post</a>, you will be able to <span style="color: orange;">run the GAS code</span> and get the results in your spreadsheet.<br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3 id="gas-code"><span style="color: #3d85c6;">GAS code</span></h3><hr color="#3d85c6" size="1" width="100%" /><br />Here is the GAS code that performs the email search. Read the code comments for more information, especially on the <span style="color: orange;">getEmailsUsingPattern</span> function that contains many examples of how you can use the main one (<span style="color: orange;">getEmailInfo</span>).<br /><br /><pre> <span style="color: green;">/*</span><br /><span style="color: green;"> ------------------------------------------------------------------------------</span><br /><span style="color: green;"> Written By: Christos Samaras</span><br /><span style="color: green;"> Date: 27/12/2018</span><br /><span style="color: green;"> Last Updated: 05/01/2019</span><br /><span style="color: green;"> E-mail: xristos.samaras@gmail.com</span><br /><span style="color: green;"> Site: https://www.myengineeringworld.net</span><br /><span style="color: green;"> ------------------------------------------------------------------------------</span><br /><span style="color: green;"> */</span><br /><br /><span style="color: blue;">function</span> onOpen() <br />{<br /> <span style="color: green;">/*</span><br /><span style="color: green;"> ------------------------------------------</span><br /><span style="color: green;"> Creates a custom menu at the spreadsheet.</span><br /><span style="color: green;"> ------------------------------------------</span><br /><span style="color: green;"> */</span><br /> <br /> SpreadsheetApp.getUi()<br /> .createMenu(<span style="color: #a31515;">'Get Emails'</span>)<br /> .addItem(<span style="color: #a31515;">'Get Emails Using Pattern...'</span>, <span style="color: #a31515;">'getEmailsUsingPattern'</span>)<br /> .addToUi();<br />}<br /><br /><span style="color: blue;">function</span> getEmailsUsingPattern()<br />{<br /> <span style="color: green;">/*</span><br /><span style="color: green;"> ----------------------------------------------------</span><br /><span style="color: green;"> Retrieves email information using a search pattern.</span><br /><span style="color: green;"> ----------------------------------------------------</span><br /><span style="color: green;"> */</span><br /> <br /> <span style="color: green;">// Set the search pattern. </span><br /> <span style="color: green;">// Subject/title example (one word search):</span><br /> <span style="color: blue;">const</span> pattern = <span style="color: #a31515;">'subject: geocoding'</span>;<br /> <br /> <span style="color: green;">// Note, when you searching for the exact phrase (e.g. two words) the pattern should be like this:</span><br /> <span style="color: green;">// Subject/title example (exact phrase):</span><br /> <span style="color: green;">// const pattern = 'subject: "VBA help"';</span><br /> <br /> <span style="color: green;">// Specific email or name of the sender:</span><br /> <span style="color: green;">// const pattern = 'from: notifications@disqus.net';</span><br /> <span style="color: green;">// const pattern = 'from: Disqus';</span><br /> <br /> <span style="color: green;">// Specific email or name of the recipient:</span><br /> <span style="color: green;">// const pattern = 'to: xristos.samaras@gmail.com';</span><br /> <span style="color: green;">// const pattern = 'to: "Christos Samaras"';</span><br /> <br /> <span style="color: green;">// Label example:</span><br /> <span style="color: green;">// const pattern = 'label: My Engineering World';</span><br /> <br /> <span style="color: green;">// File type example:</span><br /> <span style="color: green;">// const pattern = 'has:spreadsheet'; </span><br /> <br /> <span style="color: green;">// All emails (not recommended - it will take a lot of time):</span><br /> <span style="color: green;">// const pattern = 'in: anywhere'; </span><br /> <br /> <span style="color: green;">// For more options, check the next link:</span><br /> <span style="color: green;">// https://support.google.com/mail/answer/7190</span><br /> <br /> <span style="color: green;">// Finally, call the main function using the preferred pattern and the sheet name.</span><br /> getEmailInfo(pattern, <span style="color: #a31515;">'Sheet3'</span>);<br /> <br />}<br /><br /><span style="color: blue;">function</span> getEmailInfo(searchPattern, sheetName) <br />{<br /> <span style="color: green;">/*</span><br /><span style="color: green;"> ---------------------------------------------------------------------------</span><br /><span style="color: green;"> Retrieves email information from the associated Gmail account based on the</span><br /><span style="color: green;"> input search pattern. The results are written to the preferred sheet.</span><br /><span style="color: green;"> ---------------------------------------------------------------------------</span><br /><span style="color: green;"> */</span><br /> <br /> <span style="color: green;">// Check if the search pattern parameter is empty.</span><br /> <span style="color: blue;">if</span>(searchPattern === <span style="color: #a31515;">''</span>) <br /> {<br /> Browser.msgBox(<span style="color: #a31515;">'Please provide a search pattern!'</span>);<br /> <span style="color: blue;">return</span>;<br /> }<br /> <br /> <span style="color: green;">// Check if the sheet name parameter is empty.</span><br /> <span style="color: blue;">if</span>(sheetName === <span style="color: #a31515;">''</span>) <br /> {<br /> Browser.msgBox(<span style="color: #a31515;">'Please provide a sheet name!'</span>);<br /> <span style="color: blue;">return</span>;<br /> }<br /> <br /> <span style="color: green;">// Get the sheet that will contain the data.</span><br /> <span style="color: blue;">var</span> sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName); <br /><br /> <span style="color: green;">// Check that the sheet object is not null (i.e. the sheet name is correct).</span><br /> <span style="color: blue;">if</span>(sheet == <span style="color: blue;">null</span>)<br /> {<br /> Browser.msgBox(<span style="color: #a31515;">'Invalid sheet name!'</span>);<br /> <span style="color: blue;">return</span>;<br /> }<br /> <br /> <span style="color: green;">// Write the headers.</span><br /> sheet.getRange(<span style="color: #a31515;">'A1:D1'</span>).setValues([[<span style="color: #a31515;">'Date'</span>, <span style="color: #a31515;">'Name'</span>, <span style="color: #a31515;">'Email'</span>, <span style="color: #a31515;">'Subject'</span>]]);<br /> <br /> <span style="color: green;">// Make the headers bold.</span><br /> sheet.getRange(<span style="color: #a31515;">'A1:D1'</span>).setFontWeight(<span style="color: #a31515;">'bold'</span>);<br /> <br /> <span style="color: green;">// Set the first row that will contain the data (after headings).</span><br /> <span style="color: blue;">var</span> startRow = 2;<br /> <br /> <span style="color: green;">// Get the last row containing data (in column A).</span><br /> <span style="color: blue;">var</span> lastRow = sheet.getRange(<span style="color: #a31515;">"A1:A"</span>).getValues().filter(String).length;<br /> <br /> <span style="color: green;">// Clear any existing data bellow the headers.</span><br /> <span style="color: blue;">if</span>(lastRow > startRow)<br /> sheet.getRange(startRow, 1, lastRow, 4).clearContent();<br /> <br /> <span style="color: green;">// Get all the threads for the specified search pattern.</span><br /> <span style="color: blue;">var</span> threads = GmailApp.search (searchPattern);<br /> <br /> <span style="color: green;">// Check if there are threads for the particular search pattern.</span><br /> <span style="color: blue;">if</span>(threads.length == 0)<br /> {<br /> Browser.msgBox(<span style="color: #a31515;">'There are no threads for the specified pattern!'</span>);<br /> <span style="color: blue;">return</span>;<br /> }<br /> <br /> <span style="color: green;">// An empty array that will hold the email data.</span><br /> <span style="color: blue;">var</span> emailInfo = [];<br /> <br /> <span style="color: green;">// Loop through all the threads.</span><br /> <span style="color: blue;">for</span> (<span style="color: blue;">var</span> i = 0; i < threads.length; i++) <br /> { <br /> <span style="color: green;">// Get all the email messages from the thread.</span><br /> <span style="color: blue;">var</span> messages = threads[i].getMessages();<br /> <br /> <span style="color: green;">// Loop through all the email messages.</span><br /> <span style="color: blue;">for</span> (<span style="color: blue;">var</span> k = 0; k < messages.length; k++) <br /> { <br /> <span style="color: green;">// Try to get the name from the email (if possible).</span><br /> <span style="color: blue;">var</span> matchesPattern = messages[k].getFrom().match(<span style="color: #a31515;">/\s*"?([^"]*)"?\s+<(.+)>/</span>);<br /> <br /> <span style="color: green;">// Temporary variables.</span><br /> <span style="color: blue;">var</span> name;<br /> <span style="color: blue;">var</span> email;<br /> <br /> <span style="color: blue;">if</span>(matchesPattern) <br /> {<br /> <span style="color: green;">// Success, get the name and the email address.</span><br /> name = matchesPattern[1]; <br /> email = matchesPattern[2]; <br /> }<br /> <span style="color: blue;">else</span> <br /> {<br /> <span style="color: green;">// Fail, get the name/email as one.</span><br /> name = <span style="color: #a31515;">'N/A'</span>; <br /> email = messages[k].getFrom(); <br /> }<br /><br /> <span style="color: green;">// Push the necessary information into the array (date, name, email, title).</span><br /> emailInfo.push([messages[k].getDate(), name, email, messages[k].getSubject()]);<br /> }<br /> }<br /> <br /> <span style="color: green;">// Write the array data into the sheet.</span><br /> <span style="color: blue;">if</span>(emailInfo.length > 0) <br /> sheet.getRange(startRow, 1, emailInfo.length, 4).setValues(emailInfo);<br /> <br /> <span style="color: green;">// Fit the width of the columns.</span><br /> sheet.autoResizeColumns(1, 4);<br /> <br /> <span style="color: green;">// Inform the user about the process.</span><br /> Browser.msgBox(<span style="color: #a31515;">'Information from '</span> + (emailInfo.length == 1 ? <span style="color: #a31515;">"1 email"</span> : emailInfo.length + <span style="color: #a31515;">" emails"</span>) + <span style="color: #a31515;">" was successfully retrieved!"</span>);<br />}<br /></pre><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Demonstration video</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div>In less than 20 seconds, you can see how I got information from 91 emails when searching my Gmail account for emails that have the subject “geocoding”.<br /><div style="text-align: center;"><br /></div><div style="text-align: center;"></div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Read also</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><a href="https://www.myengineeringworld.net/2018/11/gas-send-emails-google-sheets.html">Send Multiple Emails From Google Sheets Using GAS</a></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-51211359197554887982018-11-28T20:06:00.000+02:002019-01-05T19:53:30.078+02:00Send Multiple Emails From Google Sheets Using GAS<div dir="ltr" style="text-align: left;" trbidi="on">Last updated: 05/01/2019, 3 min read (without the code)<br /><br /><div style="text-align: justify;"><div class="separator" style="clear: both; text-align: center;"><img alt="Send Multiple Emails From Google Sheets Using GAS" border="0" data-original-height="247" data-original-width="550" src="https://1.bp.blogspot.com/-L8ge0NTtNnM/XAv4JPoz3AI/AAAAAAAAD8g/EBJ9Hq1Zow4dExvs5ZkGidgET6rKLdG1gCLcBGAs/s1600/Send%2BMultiple%2BEmails%2BFrom%2BGoogle%2BSheets%2BUsing%2BGAS.jpg" title="Send Multiple Emails From Google Sheets Using GAS" /></div><br /></div><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><span style="color: red;">Have you ever wondered how is it possible to send multiple emails to different recipients, without counting on external services, like MailChimp, AWeber, Mailerlite and others?</span> If the answer to this question is yes, then you would probably be surprised to learn that you can achieve this by simply using Google Sheets and some custom code written in Apps Script.<br /><br />In this post, we will assume that you already have the email addresses to which you want to send emails. <a href="https://www.myengineeringworld.net/2018/12/get-sheets-get-emails.html">In another post</a>, we will see how to retrieve your Gmail contacts in a Google Sheets spreadsheet. <span style="color: orange;">Warning: the process below was not written to make you a spammer! The fair usage of the script that follows is up to you!</span><br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">The process</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div>The entire process involves 14 steps. However, once you set up the spreadsheet, then it is as easy as clicking a button on the menu bar.<br /><br /><span style="color: red;">Step 1:</span> Following the instructions described <a href="https://www.myengineeringworld.net/2018/08/create-use-custom-gas-function.html">here</a> (steps 1 to 4) <span style="color: orange;">create a new empty spreadsheet</span> on your Google Drive.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="New Empty Spreadsheet" border="0" data-original-height="335" data-original-width="510" src="https://1.bp.blogspot.com/-BeLzBw2cGkQ/XAv-IDRb7xI/AAAAAAAAD8s/N-VSG3wLAbMXIdA8qJvVi_fTTRxq-1kCwCLcBGAs/s1600/New%2BEmpty%2BSpreadsheet.jpg" title="New Empty Spreadsheet" /></div><br /><span style="color: red;">Step 2:</span> <span style="color: orange;">Open </span>the new spreadsheet in Google Sheets.<br /><br /><span style="color: red;">Step 3:</span> <span style="color: orange;">Rename the first sheet to Emails </span>by double-clicking upon the Sheet1 name and entering the suggested name (Emails). Of course, you can enter another name, if you want. You just have to be careful to put the same name in the code as well.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Rename Sheet" border="0" data-original-height="55" data-original-width="550" src="https://4.bp.blogspot.com/--zntGUev6No/XAv-cSXsxOI/AAAAAAAAD80/_v7_JX_GMQ4kyZFWxjfBA6MkNl7oLFppgCLcBGAs/s1600/Rename%2BSheet.jpg" title="Rename Sheet" /></div><br /><span style="color: red;">Step 4:</span> <span style="color: orange;">Add the headers </span>to the sheet so as to look like the image below. I think that the headers are self-explanatory.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Adding the Headers In The Spreadsheet" border="0" data-original-height="127" data-original-width="550" src="https://1.bp.blogspot.com/-N46kBOVZKNs/XAv_TeOJDeI/AAAAAAAAD9A/at_0PbjZUpwrQDhOLDK6owq1U9jBQEMYQCLcBGAs/s1600/Adding%2Bthe%2BHeaders%2BIn%2BThe%2BSpreadsheet.jpg" title="Adding the Headers In The Spreadsheet" /></div><br /><span style="color: red;">Step 5:</span> <span style="color: orange;">Switch to the Script Editor </span>by selecting the <span style="color: orange;">Tools </span>category from the menu and then clicking on the <span style="color: orange;">Script editor </span>option.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Open The Script Editor" border="0" data-original-height="271" data-original-width="550" src="https://1.bp.blogspot.com/-PYz2g6casrU/XAv_mJWgcoI/AAAAAAAAD9I/I45OBgCfrf48BAljJSv4YygK2kjcn6M6gCLcBGAs/s1600/Open%2BThe%2BScript%2BEditor.jpg" title="Open The Script Editor" /></div><br /><span style="color: red;">Step 6:</span> In the Script Editor, <span style="color: orange;">paste the code </span>that you will find <a href="https://www.myengineeringworld.net/2018/11/gas-send-emails-google-sheets.html#gas-code">below</a>.<br /><br /><span style="color: red;">Step 7:</span> <span style="color: orange;">Click the Save button </span>or select the <span style="color: orange;">File </span>category from the menu and then click on the <span style="color: orange;">Save</span> option. Alternatively, you can use the <span style="color: orange;">CTRL + S </span>shortcut.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Save The Project" border="0" data-original-height="173" data-original-width="435" src="https://3.bp.blogspot.com/-WEuX8wNwfXA/XAwADFlzppI/AAAAAAAAD9Q/2XDLa9HpclkO-rpwcwzP1iwQvGtazj4zACLcBGAs/s1600/Save%2BThe%2BProject.jpg" title="Save The Project" /></div><br /><span style="color: red;">Step 8:</span> In the <span style="color: orange;">Edit Project Name </span>form that will pop-up, <span style="color: orange;">enter a name </span>and click the <span style="color: orange;">OK button</span>.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Editing The Project Name" border="0" data-original-height="166" data-original-width="446" src="https://2.bp.blogspot.com/-_ArbBH-Cd-U/XAwAQX3KolI/AAAAAAAAD9U/PFNegta_1C4kiSHZP0GY0fDSupAw3V5XACLcBGAs/s1600/Editing%2BThe%2BProject%2BName.jpg" title="Editing The Project Name" /></div><br /><span style="color: red;">Step 9:</span> <span style="color: orange;">Switch back to the spreadsheet and refresh the page </span>(note that when you refresh the spreadsheet page, the script editor’s page will automatically close). You will see a new option on the menu called <span style="color: orange;">Custom Code</span>.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Additional Menu" border="0" data-original-height="99" data-original-width="550" src="https://4.bp.blogspot.com/-SqZfWtJILDQ/XAwAoDfrn7I/AAAAAAAAD9g/WyOW1RHMUxAkxhcmlg7-3PxdouL_KkcbQCLcBGAs/s1600/Additional%2BMenu.jpg" title="Additional Menu" /></div><br /><span style="color: red;">Step 10:</span> <span style="color: orange;">Fill the rows below the headers </span>with the appropriate information (email address, email subject and email main message). The other four columns (D through G) are optional and can be used for customizing, even more, your emails.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Filling The Rows With Information" border="0" data-original-height="72" data-original-width="550" src="https://2.bp.blogspot.com/-MxJEnbQ1svo/XAwA-FDn8SI/AAAAAAAAD9o/gVnT6_0-PPsyEXi617SoJVCXY0ReuG7qwCLcBGAs/s1600/Filling%2BThe%2BRows%2BWith%2BInformation.jpg" title="Filling The Rows With Information" /></div><br /><span style="color: red;">Step 11:</span> Then, select the <span style="color: orange;">Custom Code </span>category from the menu and click on the <span style="color: orange;">Send Emails </span>option.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Send Emails Option" border="0" data-original-height="181" data-original-width="550" src="https://4.bp.blogspot.com/-YRZB-5gBBjk/XAwBNQd5aNI/AAAAAAAAD9s/x3PY637KCkQNqmmzZLmiBYL5GDgCpBWMwCLcBGAs/s1600/Send%2BEmails%2BOption.jpg" title="Send Emails Option" /></div><br /><span style="color: red;">Step 12:</span> In the <span style="color: orange;">Authorization Required form</span> that pop-ups, click on the <span style="color: orange;">Continue button</span>.<br /><div class="separator" style="clear: both; text-align: center;"><img alt="Authorization Required Form" border="0" data-original-height="151" data-original-width="476" src="https://4.bp.blogspot.com/-plIRgYpUyvs/XAwBYNME2qI/AAAAAAAAD90/u8NVBNVpp88WOGsUWf2V-tf4u7uZv0gmQCLcBGAs/s1600/Authorization%2BRequired%2BForm.jpg" title="Authorization Required Form" /></div><br /><span style="color: red;">Step 13:</span> In the Sign-in form that pop-ups, <span style="color: orange;">click on your Google account</span>.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Sign In Form" border="0" data-original-height="503" data-original-width="550" src="https://2.bp.blogspot.com/-4zZ9QiMXcKw/XAwBidPfFXI/AAAAAAAAD94/PDfqpUbGdb4uBBgVAUaJprxWWIKgsIemwCLcBGAs/s1600/Sign%2BIn%2BForm.jpg" title="Sign In Form" /></div><br /><span style="color: red;">Step 14:</span> Finally, in the next form that appears, click the <span style="color: orange;">Allow button</span>.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Allow Access To Google Account" border="0" data-original-height="722" data-original-width="550" src="https://4.bp.blogspot.com/-x-o-7bbacR0/XAwCX7PSKcI/AAAAAAAAD-M/NFk1qcTF2pcf56CKUnNQc4y1bfGXH9wQQCLcBGAs/s1600/Allow%2BAccess%2BTo%2BGoogle%2BAccount.jpg" title="Allow Access To Google Account" /></div><br />If all the previous steps performed successfully, you will probably see something like this:<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Emails Were Sent Successfully" border="0" data-original-height="151" data-original-width="396" src="https://4.bp.blogspot.com/-U6VsaHmZtTA/XAwCnuGZhnI/AAAAAAAAD-Q/3q58wGjqBGIl36g6vm9NE_re0dv5XCM4wCLcBGAs/s1600/Emails%2BWere%2BSent%2BSuccessfully.jpg" title="Emails Were Sent Successfully" /></div><br />And here are the emails that were sent (in the inbox of the recipient):<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Emails Received" border="0" data-original-height="73" data-original-width="535" src="https://3.bp.blogspot.com/-meJYOymwDm4/XAwDcYcZd1I/AAAAAAAAD-c/Iz7HE9HrSGAyQOLjjIkahLo6VR2zUt3jgCLcBGAs/s1600/Emails%2BReceived.jpg" title="Emails Received" /></div><br /><span style="color: red;">Bonus Tip:</span> To create a <span style="color: orange;">line break </span>within a cell that contains your main message, simply use <span style="color: orange;">CTRL + ENTER</span> instead of ENTER.</div><br /><br /><div style="text-align: justify;"><hr color="#3d85c6" size="1" width="100%" /><h3 id="gas-code"><span style="color: #3d85c6;">GAS code</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div>Here is the GAS code that loops from row 3 till the last row and automatically sends the emails to the appropriate recipients. Read the code comments for more information.<br /><br /><pre> <span style="color: green;">/*</span><br /><span style="color: green;"> ------------------------------------------------------------------------------</span><br /><span style="color: green;"> Written By: Christos Samaras</span><br /><span style="color: green;"> Date: 28/11/2018</span><br /><span style="color: green;"> Last Updated: 08/12/2018</span><br /><span style="color: green;"> E-mail: xristos.samaras@gmail.com</span><br /><span style="color: green;"> Site: https://www.myengineeringworld.net</span><br /><span style="color: green;"> ------------------------------------------------------------------------------</span><br /><span style="color: green;"> */</span><br /><br /><span style="color: blue;">function</span> onOpen(e) <br />{<br /> <span style="color: green;">/*</span><br /><span style="color: green;"> ------------------------------------------</span><br /><span style="color: green;"> Creates a custom menu at the spreadsheet.</span><br /><span style="color: green;"> ------------------------------------------</span><br /><span style="color: green;"> */</span><br /> <br /> SpreadsheetApp.getUi()<br /> .createMenu(<span style="color: #a31515;">'Custom Code'</span>)<br /> .addItem(<span style="color: #a31515;">'Send Emails'</span>, <span style="color: #a31515;">'sendMultipleEmails'</span>)<br /> .addToUi();<br />}<br /><br /><span style="color: blue;">function</span> sendMultipleEmails() <br />{<br /> <span style="color: green;">/*</span><br /><span style="color: green;"> -------------------------------</span><br /><span style="color: green;"> Sends multiple emails at once.</span><br /><span style="color: green;"> -------------------------------</span><br /><span style="color: green;"> */</span><br /> <br /> <span style="color: green;">// Ge the sheet containing the data by its name.</span><br /> <span style="color: blue;">var</span> sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(<span style="color: #a31515;">"Emails"</span>); <br /> <br /> <span style="color: green;">// The name you want to be displayed in the recipient's inbox.</span><br /> <span style="color: blue;">var</span> displayedName = <span style="color: #a31515;">"Name Surname"</span><br /> <br /> <span style="color: green;">// Check that the sheet object is not null (i.e. the sheet name is correct).</span><br /> <span style="color: blue;">if</span>(sheet == <span style="color: blue;">null</span>)<br /> {<br /> Browser.msgBox(<span style="color: #a31515;">"Invalid sheet name!"</span>);<br /> <span style="color: blue;">return</span>;<br /> }<br /> <br /> <span style="color: green;">// Set the first row that containing the data (after headings).</span><br /> <span style="color: blue;">var</span> startRow = 3;<br /> <br /> <span style="color: green;">// Get the last row containing data (in column A).</span><br /> <span style="color: blue;">var</span> lastRow = sheet.getRange(<span style="color: #a31515;">"A1:A"</span>).getValues().filter(String).length;<br /> <br /> <span style="color: green;">// Check that the last row is greater than the start row.</span><br /> <span style="color: blue;">if</span>(lastRow < startRow)<br /> {<br /> Browser.msgBox(<span style="color: #a31515;">"There are no emails to send!"</span>);<br /> <span style="color: blue;">return</span>;<br /> }<br /> <br /> <span style="color: green;">// Loop through all the rows and send the emails.</span><br /> <span style="color: blue;">for</span> (<span style="color: blue;">var</span> i = startRow; i <= lastRow; i++)<br /> {<br /> <span style="color: green;">// Get the email address.</span><br /> <span style="color: blue;">var</span> address = sheet.getRange(i, 1).getValue();<br /> <br /> <span style="color: green;">// Get the email subject.</span><br /> <span style="color: blue;">var</span> subject = sheet.getRange(i, 2).getValue();<br /> <br /> <span style="color: green;">// Check if the welcome message (i.e. the optional parameter) is not empty.</span><br /> <span style="color: blue;">if</span>(sheet.getRange(i, 4).getValue() != <span style="color: #a31515;">""</span>)<br /> {<br /> <span style="color: green;">// Create the custom message with the names, along with the welcome and the goodbye message.</span><br /> <span style="color: blue;">var</span> message = sheet.getRange(i, 4).getValue() + <span style="color: #a31515;">" "</span> + sheet.getRange(i, 5).getValue() + <span style="color: #a31515;">",\n\n"</span>; <span style="color: green;">// Welcome greeting.</span><br /> message+= sheet.getRange(i, 3).getValue() + <span style="color: #a31515;">"\n\n"</span>; <span style="color: green;">// Main message.</span><br /> message+= sheet.getRange(i, 6).getValue() + <span style="color: #a31515;">"\n"</span> + sheet.getRange(i, 7).getValue(); <span style="color: green;">// Goodbye greeting.</span><br /> }<br /> <span style="color: blue;">else</span><br /> {<br /> <span style="color: green;">// This is the standard message (no optional parameters are provided).</span><br /> <span style="color: blue;">var</span> message = sheet.getRange(i, 3).getValue();<br /> }<br /> <br /> <span style="color: green;">// Send the email using the Gmail service (typical parameters).</span><br /> <span style="color: green;">// GmailApp.sendEmail(address, subject, message);</span><br /> <br /> <span style="color: green;">// To include your name, call the Gmail service like this:</span><br /> GmailApp.sendEmail(address, subject, message, {<span style="color: #a31515;">"name"</span>: displayedName});<br /> <br /> <span style="color: green;">// Other options include bcc, cc, attachments, noReply, replyTo etc.</span><br /> <span style="color: green;">// See here for more information:</span><br /> <span style="color: green;">// https://developers.google.com/apps-script/reference/gmail/gmail-app</span><br /> <br /> <span style="color: green;">// An alternative method that has some issues, is this:</span><br /> <span style="color: green;">// MailApp.sendEmail(address, subject, message);</span><br /> }<br /> <br /> <span style="color: green;">// Inform the user about the process.</span><br /> Browser.msgBox(((lastRow - startRow + 1) == 0 ? <span style="color: #a31515;">"An email was"</span> : (lastRow - startRow + 1) + <span style="color: #a31515;">" emails were"</span>) + <span style="color: #a31515;">" successfully sent!"</span>);<br />}<br /></pre><br />More information about the GmailApp object can be found on the <a href="https://developers.google.com/apps-script/reference/gmail/gmail-app">developer's page</a>.<br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Read also</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><a href="https://www.myengineeringworld.net/2018/12/get-sheets-get-emails.html">Get Email Information Into Google Sheets Using GAS</a></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-17494312108846743732018-10-30T23:42:00.000+02:002018-11-07T22:59:02.546+02:00Creating & Customizing Column Charts In Google Sheets<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;">Last updated: 07/11/2018, 2 min read (without the code)</div><br /><div style="text-align: justify;"><div class="separator" style="clear: both; text-align: center;"><img alt="Creating & Customizing Column Charts In Google Sheets" border="0" data-original-height="233" data-original-width="550" src="https://4.bp.blogspot.com/-YIl4dnMutNc/W-NLePhpYPI/AAAAAAAAD7M/c0o38lZY8R8FDZNsoaGwEIK48-fwsTa8wCLcBGAs/s1600/Creating%2B%2526%2BCustomizing%2BColumn%2BCharts%2BIn%2BGoogle%2BSheets.jpg" title="Creating & Customizing Column Charts In Google Sheets" /></div><br /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;">Google Sheets, much like Microsoft Excel, incorporate many different chart types, which can help the user to visualize his/her data. <span style="color: red;">In this post, we will learn how to create and customize a simple column chart in Google Sheets.</span> There will be the “manual way”, the “coding way” – using Google Apps Script – as well a bonus tip for selecting chart colors.</div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">The manual way</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;">To create a column chart manually just follow these 2 simple steps:<br /><br /><span style="color: red;">Step 1:</span> First, <span style="color: orange;">select the range</span> containing the data, e.g. A1:C4.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Range Selection" border="0" data-original-height="159" data-original-width="398" src="https://2.bp.blogspot.com/-CZNnbg3uXSc/W-NLsEX-OHI/AAAAAAAAD7Q/3sfyywqZ1UAcNlzG9pv_FGV_xp6DIm4aQCLcBGAs/s1600/Range%2BSelection.jpg" title="Range Selection" /></div><br /><span style="color: red;">Step 2:</span> Then, go to the menu <span style="color: orange;">Insert</span> and click the <span style="color: orange;">Chart</span> option.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Insert Chart" border="0" data-original-height="595" data-original-width="449" src="https://4.bp.blogspot.com/-BtUObcCxi7E/W-NL0o9YryI/AAAAAAAAD7Y/_OxmuUSJfXwTDjjXsBM83JzfRwZyHxGZwCLcBGAs/s1600/Insert%2BChart.jpg" title="Insert Chart" /></div><br />The generated chart would look like this:<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Default Column Chart" border="0" data-original-height="341" data-original-width="550" src="https://3.bp.blogspot.com/-lE2ca7AlrEI/W-NL87en8GI/AAAAAAAAD7g/zJfI7JwOwN47yD9VFLVF3v2xi7hkN2QKQCLcBGAs/s1600/Default%2BColumn%2BChart.jpg" title="Default Column Chart" /></div><br />To customize the chart, <span style="color: orange;">right-click</span> upon the chart, and on the context menu that pop-ups, select the appropriate option. For example, to customize the chart title, you have to select the <span style="color: orange;">Chart & axis titles</span> option and then the <span style="color: orange;">Chart title</span> option.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Customizing Chart Title" border="0" data-original-height="381" data-original-width="550" src="https://1.bp.blogspot.com/-aC9HcW3XcCQ/W-NMFphmzqI/AAAAAAAAD7o/zCWHxv2ovi0vBHIaVCyDaVbLbIbr-uBGACLcBGAs/s1600/Customizing%2BChart%2BTitle.jpg" title="Customizing Chart Title" /></div><br />The <span style="color: orange;">Chart editor</span> form appears, where you can customize several options. Here, for example, we have changed the text, font, font size, format and the text color of the title.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Chart Editor" border="0" data-original-height="457" data-original-width="550" src="https://4.bp.blogspot.com/-4rYjdTsXGjk/W-NMOlKIyLI/AAAAAAAAD7w/batVQolWLOMloZZyZaHS6Yhzj9tS-rgXgCLcBGAs/s1600/Chart%2BEditor.jpg" title="Chart Editor" /></div><br />In a similar way, you can customize every available chart option (e.g. series, legend, axes and gridlines).</div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">The coding way</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;"><span style="color: red;">The manual way can be quite daunting and time-consuming, especially if you have to customize several chart options. Thanks to Apps Script, though, the same process can be automated, allowing you to create beautiful charts in a few seconds.</span> The code below contains probably the most common options that someone will need to customize when creating a column chart. It has the following options:<br /><ul><li>Generic chart options: chart position and size.</li><li>Chart title options: text and text style.</li><li>Legend options: position and text style.</li><li>X-axis options: title and text style.</li><li>Y-axis options: title, gridlines and text style.</li><li>Series options: color, data labels, error bars and text style.</li><li>Trendline options: type and line style.</li></ul><span style="color: orange;">Most of these options are set using mainly the <a href="https://developers.google.com/apps-script/reference/spreadsheet/embedded-column-chart-builder#setOption(String,Object)">setOption method</a> of the chart object.</span> The various options are passed using a <a href="https://www.w3schools.com/css/css_syntax.asp">CSS syntax</a>. Additional options can be found on <a href="https://developers.google.com/chart/interactive/docs/gallery/columnchart">this page</a>.<br /><br /><pre><span style="color: blue;">function</span> createColumnChart() {<br /><br /> <span style="color: green;">/*</span><br /><span style="color: green;"> ------------------------------------------------------------------------------</span><br /><span style="color: green;"> The function creates a custom column chart in the active sheet using the data</span><br /><span style="color: green;"> that exist in the range that is specified in the dataRange variable.</span><br /><span style="color: green;"> </span><br /><span style="color: green;"> Written By: Christos Samaras</span><br /><span style="color: green;"> Date: 30/10/2018</span><br /><span style="color: green;"> Last Updated: 07/11/2018</span><br /><span style="color: green;"> E-mail: xristos.samaras@gmail.com</span><br /><span style="color: green;"> Site: https://www.myengineeringworld.net</span><br /><span style="color: green;"> ------------------------------------------------------------------------------</span><br /><span style="color: green;"> */</span><br /> <br /> <span style="color: green;">// Range to get the data.</span><br /> <span style="color: blue;">var</span> dataRange = <span style="color: #a31515;">'A1:C4'</span>;<br /> <br /> <span style="color: green;">// Variables for customizing colors.</span><br /> <span style="color: green;">// Use this tool to generate "good" colors:</span><br /> <span style="color: green;">// http://paletton.com</span><br /> <span style="color: blue;">var</span> color1 = <span style="color: #a31515;">'#EE295A'</span>; <span style="color: green;">// Fill and data label color for the first series.</span><br /> <span style="color: blue;">var</span> color2 = <span style="color: #a31515;">'#FFD42C'</span>; <span style="color: green;">// Fill and data label color for the second series.</span><br /> <span style="color: blue;">var</span> color3 = <span style="color: #a31515;">'#8D7CEE'</span>; <span style="color: green;">// Trendline and axis titles color.</span><br /> <span style="color: blue;">var</span> color4 = <span style="color: #a31515;">'#A6F870'</span>; <span style="color: green;">// Gridlines, axis labels and chart title color.</span><br /> <br /> <span style="color: green;">// Text for titles (chart and both axes).</span><br /> <span style="color: blue;">var</span> chartTitle = <span style="color: #a31515;">'Vehicles Comparisons'</span>;<br /> <span style="color: blue;">var</span> xAxisTitle = <span style="color: #a31515;">'Vehicle Category'</span>;<br /> <span style="color: blue;">var</span> yAxisTitle = <span style="color: #a31515;">'Speed & Power'</span>;<br /> <br /> <span style="color: green;">// Get the active sheet.</span><br /> <span style="color: blue;">var</span> spreadsheet = SpreadsheetApp.getActive(); <br /> <span style="color: blue;">var</span> sheet = spreadsheet.getActiveSheet();<br /> <br /> <span style="color: green;">// Create a chart.</span><br /> <span style="color: blue;">var</span> chart = sheet.newChart()<br /> .asColumnChart()<br /> .addRange(spreadsheet.getRange(dataRange))<br /> <br /> <span style="color: green;">// Set the generic options.</span><br /> .setNumHeaders(1)<br /> .setBackgroundColor(<span style="color: #a31515;">'#333333'</span>) <br /> <span style="color: green;">// .setOption('is3D', true) // For a 3D chart.</span><br /> <span style="color: green;">// .setOption('height', 400) // Custom height. </span><br /> <span style="color: green;">// .setOption('width', 600) // Custom width.</span><br /> <br /> <span style="color: green;">// Set the chart title options.</span><br /> .setOption(<span style="color: #a31515;">'title'</span>, chartTitle)<br /> .setOption(<span style="color: #a31515;">'titleTextStyle'</span>, {<br /> color: color4, <br /> alignment: <span style="color: #a31515;">'center'</span>,<br /> fontName: <span style="color: #a31515;">'Verdana'</span>,<br /> fontSize: 24, <br /> bold: <span style="color: blue;">true</span> <br /> })<br /> <br /> <span style="color: green;">// Set the legend options.</span><br /> .setOption(<span style="color: #a31515;">'legend'</span>, {<br /> position: <span style="color: #a31515;">'top'</span>,<br /> alignment: <span style="color: #a31515;">'center'</span>,<br /> textStyle: {<br /> color: color3,<br /> fontName: <span style="color: #a31515;">'Verdana'</span>,<br /> fontSize: 12,<br /> bold: <span style="color: blue;">true</span><br /> }<br /> })<br /> <br /> <span style="color: green;">// Set the X-axis options.</span><br /> .setOption(<span style="color: #a31515;">'hAxis'</span>, {<br /> title: xAxisTitle,<br /> titleTextStyle: {<br /> color: color3,<br /> alignment: <span style="color: #a31515;">'center'</span>,<br /> fontName: <span style="color: #a31515;">'Verdana'</span>,<br /> fontSize: 16,<br /> bold: <span style="color: blue;">true</span><br /> },<br /> textStyle: {<br /> color: color4,<br /> fontName: <span style="color: #a31515;">'Verdana'</span>,<br /> fontSize: 14,<br /> bold: <span style="color: blue;">true</span><br /> }<br /> })<br /><br /> <span style="color: green;">// Set the Y-axis options.</span><br /> .setOption(<span style="color: #a31515;">'vAxes'</span>, {<br /> 0: {<br /> title: yAxisTitle,<br /> format: <span style="color: #a31515;">'none'</span>,<br /> titleTextStyle: {<br /> color: color3,<br /> alignment: <span style="color: #a31515;">'center'</span>,<br /> fontName: <span style="color: #a31515;">'Verdana'</span>,<br /> fontSize: 16,<br /> bold: <span style="color: blue;">true</span><br /> },<br /> textStyle: {<br /> color: color4,<br /> fontName: <span style="color: #a31515;">'Verdana'</span>,<br /> fontSize: 14,<br /> bold: <span style="color: blue;">true</span><br /> },<br /> gridlines: {<br /> count: -1,<br /> color: color4,<br /> width: 5<br /> }<br /> }<br /> })<br /><br /> <span style="color: green;">// Set the series options.</span><br /> .setOption(<span style="color: #a31515;">"series"</span>, {<br /> 0: {<br /> labelInLegend: <span style="color: #a31515;">'Speed [km/h]'</span>,<br /> color: color1, <br /> hasAnnotations: <span style="color: blue;">true</span>,<br /> dataLabel: <span style="color: #a31515;">'value'</span>,<br /> dataLabelPlacement: <span style="color: #a31515;">'outsideEnd'</span>,<br /> errorBars: {<br /> magnitude: 20,<br /> errorType: <span style="color: #a31515;">"percent"</span><br /> },<br /> dataLabel: <span style="color: #a31515;">"value"</span>,<br /> textStyle: {<br /> color: color1,<br /> fontSize: 12,<br /> fontName: <span style="color: #a31515;">'Verdana'</span>,<br /> bold: <span style="color: blue;">true</span><br /> }<br /> },<br /> 1: {<br /> labelInLegend: <span style="color: #a31515;">'Power [kW]'</span>,<br /> color: color2,<br /> hasAnnotations: <span style="color: blue;">true</span>,<br /> dataLabel: <span style="color: #a31515;">'value'</span>,<br /> dataLabelPlacement: <span style="color: #a31515;">'outsideEnd'</span>,<br /> errorBars: {<br /> magnitude: 20,<br /> errorType: <span style="color: #a31515;">"percent"</span><br /> },<br /> dataLabel: <span style="color: #a31515;">"value"</span>,<br /> textStyle: {<br /> color: color2,<br /> fontSize: 12,<br /> fontName: <span style="color: #a31515;">'Verdana'</span>,<br /> bold: <span style="color: blue;">true</span><br /> }<br /> }<br /> })<br /> <br /> <span style="color: green;">// Set the trendline options (here only for the first series).</span><br /> .setOption(<span style="color: #a31515;">'trendlines'</span>, {<br /> 0: {<br /> color: color3,<br /> type: <span style="color: #a31515;">'linear'</span>,<br /> lineWidth: 4,<br /> opacity: 0.8,<br /> labelInLegend: <span style="color: #a31515;">'Trendline'</span>,<br /> visibleInLegend: <span style="color: blue;">true</span><br /> }<br /> })<br /> <br /> <span style="color: green;">// Position: row and column of top left corner (cell E2 here).</span><br /> <span style="color: green;">// Optional: offset X and Y in pixels.</span><br /> .setPosition(2, 5, 0, 0)<br /> <br /> <span style="color: green;">// Finally, build the chart.</span><br /> .build();<br /> sheet.insertChart(chart);<br />};<br /></pre><br />If you run the above script on the same dataset (A1:C4), the final chart will look like this:</div><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Column Chart With Code" border="0" data-original-height="344" data-original-width="550" src="https://3.bp.blogspot.com/-uLQRdEPJd9I/W-NMYlUPH3I/AAAAAAAAD74/DTN57Asyl_MxMLYAqWz1NwVc9l1GdmBrACLcBGAs/s1600/Column%2BChart%2BWith%2BCode.jpg" title="Column Chart With Code" /></div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">How to select “good” chart colors</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;">If you saw the chart that was generated from the previous Apps Script code you would probably notice that the color combination is quite “pleasing to the eye”. The particular set of colors was selected using <a href="http://paletton.com/">this tool</a>. If you play with it for a few minutes, you will discover that it has an extensive set of options for generating color palettes and harmonies. If you don’t know where to start, simply click the <span style="color: orange;">randomize button</span> at the top of the screen to start your color scheme.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Palleton" border="0" data-original-height="386" data-original-width="550" src="https://3.bp.blogspot.com/-CnJmVrsst9Y/W-NMqqV5SDI/AAAAAAAAD8E/qyybPft4VQ4Qn2tgLjfPdCxX9Adgis7WgCLcBGAs/s1600/Palleton.jpg" title="Palleton" /></div><br /><span style="color: red;">Unlike Excel, the chart colors in Google Sheets can be customized more extensively.</span> Therefore, you can create more impressive charts if you are willing to experiment a little bit with the color combinations.</div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-62322774796827249872018-09-30T23:58:00.000+03:002018-11-07T22:14:06.446+02:00Triggers & Events In Google Sheets<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;"><div style="text-align: justify;">Last updated: 05/10/2018, 3 min read (without the code)<br /><br /></div></div><div style="text-align: justify;"><div class="separator" style="clear: both; text-align: center;"><img alt="Triggers & Events In Google Sheets" border="0" data-original-height="282" data-original-width="550" src="https://4.bp.blogspot.com/-skC3fdduNUk/W7U-rjxDeOI/AAAAAAAAD6o/QJD4zA2tmjEUJiums1sGve2DI63W4OU9wCLcBGAs/s1600/Triggers%2B%2526%2BEvents%2BIn%2BGoogle%2BSheets.jpg" title="Triggers & Events In Google Sheets" /></div><br /></div><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br />According to the <a href="https://developers.google.com/apps-script/guides/triggers">documentation</a>, <span style="color: red;">triggers let Google Apps Script run a function automatically when a certain event, like opening a document, occurs.</span> Apps Script supports two types of triggers, simple and installable. <a href="https://developers.google.com/apps-script/guides/triggers">Simple triggers</a> are a set of reserved functions built into Apps Script, like the function onOpen(e), which is executed when a user opens a Google Docs, Sheets, Slides, or Forms file. <a href="https://developers.google.com/apps-script/guides/triggers/installable">Installable triggers</a>, on the other hand, offer more capabilities than simple triggers but must be activated before use. They can call services that require authorization, they offer several additional types of events including time-driven (clock) triggers, and they can be controlled programmatically.<br /><br /><span style="color: red;">Both types of triggers let Apps Script run a function automatically if a certain event occurs. </span><span style="color: red;">When a trigger fires, Apps Script passes in the function an event object as an argument, typically called “e”. The event object contains information about the context that caused the trigger to fire.</span> For example, the simple onEdit(e) trigger for a Google Sheets script that we will see below, uses the event object to determine which cell was edited.<br /><br />Unlike VBA, <span style="color: orange;">in Google Apps Script there are only a few events</span> that can be used to detect changes within an application. In this post, we will analyze two of the most useful ones, the onEdit and the onOpen. Examples will be given on Google Sheets application. We will also learn how to install a trigger, both manually and programmatically.</div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">GAS code</span></h3><hr color="#3d85c6" size="1" width="100%" /><br />In the code below there are seven GAS functions:<br /><ul style="text-align: justify;"><li><span style="color: orange;">onEdit</span>: A simple trigger that fires when the contents of a cell change. In the particular case, the range that is checked for changes is the "A1:A10" in the active sheet.</li><li><span style="color: orange;">onOpen</span>: A simple trigger that is actually a logger, which runs every time that someone opens the spreadsheet. It fills the current date/time in the last row of column B in the active sheet.</li><li><span style="color: orange;">createSpreadsheetOpenTrigger</span>: It creates an open trigger programmatically.</li><li><span style="color: orange;">createTimeDrivenTrigger</span>: It creates a time-driven trigger programmatically that runs every 5 minutes.</li><li><span style="color: orange;">rangeIntersect</span>: A helping function that returns true when two ranges intersect. This is the GAS implementation of the Application.Intersect method that exists in VBA/Excel.</li><li><span style="color: orange;">greeting</span>: A function that displays a message box in the browser.</li><li><span style="color: orange;">myCounter</span>: A function that finds the last row in column D of the active sheet and adds +1 to the previous value.</li></ul><br /><pre><span style="color: green;">/*</span><br /><span style="color: green;"> --------------------------------------------------------------------------------</span><br /><span style="color: green;"> The next set of functions show how to use triggers and events in Google Sheets.</span><br /><br /><span style="color: green;"> Written By: Christos Samaras</span><br /><span style="color: green;"> Date: 29/09/2018</span><br /><span style="color: green;"> Last Updated: 05/10/2018</span><br /><span style="color: green;"> E-mail: xristos.samaras@gmail.com</span><br /><span style="color: green;"> Site: https://www.myengineeringworld.net</span><br /><span style="color: green;"> --------------------------------------------------------------------------------</span><br /><span style="color: green;">*/</span><br /><br /><span style="color: green;">/*</span><br /><span style="color: green;"> ----------------</span><br /><span style="color: green;"> Simple Triggers</span><br /><span style="color: green;"> ----------------</span><br /><span style="color: green;">*/</span><br /><span style="color: blue;">function</span> onEdit(e)<br />{ <br /> <span style="color: green;">/*</span><br /><span style="color: green;"> -----------------------------------------------------------------------------------------</span><br /><span style="color: green;"> A simple trigger that fires after a cell change in the range A1:A10 of the active sheet.</span><br /><span style="color: green;"> -----------------------------------------------------------------------------------------</span><br /><span style="color: green;"> */</span><br /> <br /> <span style="color: green;">// Get the active sheet.</span><br /> <span style="color: blue;">var</span> sheet = SpreadsheetApp.getActiveSheet();<br /> <br /> <span style="color: green;">// Set the range that has to be chekced for changes.</span><br /> <span style="color: blue;">var</span> selectedRange = sheet.getRange(<span style="color: #a31515;">"A1:A10"</span>);<br /> <br /> <span style="color: green;">// Get the active cell from the event obejct.</span><br /> <span style="color: blue;">var</span> activeCell = e.range;<br /> <br /> <span style="color: green;">// Check if the active cell belongs to the range of interest. </span><br /> <span style="color: green;">// If yes, show a message box in the browser.</span><br /> <span style="color: blue;">if</span>(rangeIntersect(activeCell, selectedRange)) <br /> Browser.msgBox(<span style="color: #a31515;">"Triggered after the cell "</span> + activeCell.getA1Notation() + <span style="color: #a31515;">" changed!"</span>); <br />}<br /><br /><span style="color: blue;">function</span> onOpen(e)<br />{<br /> <span style="color: green;">/*</span><br /><span style="color: green;"> -------------------------------------------------------------------</span><br /><span style="color: green;"> A simple trigger that acts as a logger, filling the date/time that</span><br /><span style="color: green;"> the spreadsheet was openned in the last row of the column B.</span><br /><span style="color: green;"> -------------------------------------------------------------------</span><br /><span style="color: green;"> */</span><br /> <br /> <span style="color: green;">// Get the active sheet.</span><br /> <span style="color: blue;">var</span> sheet = SpreadsheetApp.getActiveSheet();<br /> <br /> <span style="color: green;">// Get the last row in column B that contains values.</span><br /> <span style="color: blue;">var</span> lastRow = sheet.getRange(<span style="color: #a31515;">"B1:B"</span>).getValues().filter(String).length;<br /> <br /> <span style="color: green;">// Set the current date/time in the next available row in column B.</span><br /> sheet.getRange(lastRow + 1, 2).setValue((<span style="color: blue;">new</span> Date()).toLocaleString()); <br />}<br /><br /><br /><span style="color: green;">/*</span><br /><span style="color: green;"> ---------------------</span><br /><span style="color: green;"> Installable Triggers</span><br /><span style="color: green;"> ---------------------</span><br /><span style="color: green;">*/</span><br /><span style="color: blue;">function</span> createSpreadsheetOpenTrigger() <br />{<br /> <span style="color: green;">/*</span><br /><span style="color: green;"> --------------------------------------------------------------------------</span><br /><span style="color: green;"> Creates a trigger programmatically that fires when the spreadsheet opens.</span><br /><span style="color: green;"> --------------------------------------------------------------------------</span><br /><span style="color: green;"> */</span><br /> <br /> <span style="color: green;">// Get the active spreadsheet (workbook in Excel terminology).</span><br /> <span style="color: blue;">var</span> ss = SpreadsheetApp.getActiveSpreadsheet();<br /> <br /> <span style="color: green;">// Create a new trigger that will run when the spreadsheet opens.</span><br /> <span style="color: green;">// The trigger will fire the greeting function.</span><br /> ScriptApp.newTrigger(<span style="color: #a31515;">'greeting'</span>).forSpreadsheet(ss).onOpen().create();<br />}<br /><br /><span style="color: blue;">function</span> createTimeDrivenTrigger() <br />{ <br /> <span style="color: green;">/*</span><br /><span style="color: green;"> ---------------------------------------------------------------</span><br /><span style="color: green;"> Creates a trigger programmatically that fires every 5 minutes.</span><br /><span style="color: green;"> ---------------------------------------------------------------</span><br /><span style="color: green;"> */</span><br /> <br /> <span style="color: green;">// Create a new trigger that will run every 5 minutes.</span><br /> <span style="color: green;">// The trigger will run the myCounter function.</span><br /> ScriptApp.newTrigger(<span style="color: #a31515;">'myCounter'</span>).timeBased().everyMinutes(5).create();<br />}<br /><br /><br /><span style="color: green;">/*</span><br /><span style="color: green;"> ------------------</span><br /><span style="color: green;"> Helping Functions</span><br /><span style="color: green;"> ------------------</span><br /><span style="color: green;">*/</span><br /><span style="color: blue;">function</span> rangeIntersect (rng1, rng2) <br />{<br /> <span style="color: green;">/*</span><br /><span style="color: green;"> ------------------------------------------</span><br /><span style="color: green;"> Returns true if the two ranges intersect.</span><br /><span style="color: green;"> ------------------------------------------</span><br /><span style="color: green;"> */</span><br /> <br /> <span style="color: green;">// Check for empty objects.</span><br /> <span style="color: blue;">if</span>(rng1 == <span style="color: blue;">null</span> || rng2 == <span style="color: blue;">null</span>)<br /> <span style="color: blue;">return</span> <span style="color: blue;">false</span>;<br /> <br /> <span style="color: green;">// Compare the rows and columns of the two ranges.</span><br /> <span style="color: blue;">return</span> (rng1.getLastRow() >= rng2.getRow()) && (rng2.getLastRow() >= rng1.getRow()) <br /> && (rng1.getLastColumn() >= rng2.getColumn()) && (rng2.getLastColumn() >= rng1.getColumn());<br />}<br /><br /><span style="color: blue;">function</span> greeting()<br />{<br /> <span style="color: green;">/*</span><br /><span style="color: green;"> ---------------------------------</span><br /><span style="color: green;"> It displays a hello message box.</span><br /><span style="color: green;"> ---------------------------------</span><br /><span style="color: green;"> */</span> <br /> <br /> <span style="color: green;">// Display hello in the browser.</span><br /> Browser.msgBox(<span style="color: #a31515;">"Hello!"</span>);<br />}<br /><br /><span style="color: blue;">function</span> myCounter()<br />{<br /> <span style="color: green;">/*</span><br /><span style="color: green;"> ----------------------------------------------------------------</span><br /><span style="color: green;"> It adds +1 in the last row of the column D of the active sheet.</span><br /><span style="color: green;"> ----------------------------------------------------------------</span><br /><span style="color: green;"> */</span><br /> <br /> <span style="color: green;">// Get the active sheet.</span><br /> <span style="color: blue;">var</span> sheet = SpreadsheetApp.getActiveSheet();<br /> <br /> <span style="color: green;">// Get the last row in column D that contains values. </span><br /> <span style="color: blue;">var</span> lastRow = sheet.getRange(<span style="color: #a31515;">"D1:D"</span>).getValues().filter(String).length;<br /><br /> <span style="color: green;">// Check if the last row is 0. If yes, fill the number 1 in the cell D1.</span><br /> <span style="color: blue;">if</span>(lastRow == 0)<br /> {<br /> sheet.getRange(lastRow + 1, 4).setValue(1);<br /> <span style="color: blue;">return</span>;<br /> }<br /> <br /> <span style="color: green;">// Add +1 in the next available row in column D.</span><br /> sheet.getRange(lastRow + 1, 4).setValue(sheet.getRange(lastRow, 4).getValue() + 1);<br />}<br /></pre><div style="text-align: justify;"><br /><b><span style="color: red;"><u>onEdit example</u></span></b><br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="OnEdit Event Example" border="0" data-original-height="380" data-original-width="550" src="https://1.bp.blogspot.com/-7nlKMumKFRw/W7PudZ715pI/AAAAAAAAD48/6mUjVEJ46kAchgH2S2akvdAIbI5K3uhXQCLcBGAs/s1600/OnEdit%2BEvent%2BExample.jpg" title="OnEdit Event Example" /></div><br />The above image shows what happens when the <span style="color: orange;">onEdit </span>trigger runs. If the user changes the contents of the cell A6, a message box will pop-up in the browser. Of course, this simple event was written only for demonstration purposes.<br /><br /><br /><b><span style="color: red;"><u>onOpen example</u></span></b><br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="OnOpen Event Example" border="0" data-original-height="203" data-original-width="550" src="https://1.bp.blogspot.com/-U3wlmwmzQCw/W7Pv124IW8I/AAAAAAAAD5I/kHZdESBhw6sLowkBHnYrTWHDDdsfOc9jgCLcBGAs/s1600/OnOpen%2BEvent%2BExample.jpg" title="OnOpen Event Example" /></div><br />When the user double-clicks to open the selected spreadsheet, the current date/time will be filled in the last row of column B of the active sheet. This event can be very useful for logging purposes.</div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">How to install a trigger</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /><b><span style="color: red;"><u>The manual way</u></span></b><br /><div style="text-align: justify;"><br /></div></div><div style="text-align: justify;">To manually install a trigger, you will have to go to the <span style="color: orange;">Script editor</span>. There, from the menu, select <span style="color: orange;">Edit</span> and then <span style="color: orange;">Current's project triggers</span>.</div><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; text-align: center;"><img alt="Current Project Triggers" border="0" data-original-height="388" data-original-width="412" src="https://1.bp.blogspot.com/-288ok2NQ3Es/W7SnQFVJJsI/AAAAAAAAD5U/n_kBFdFsEBA7QXoPKtO1Aj8FoDFypSnZwCLcBGAs/s1600/Current%2BProject%2BTriggers.jpg" title="Current Project Triggers" /></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">In the window that will pop up, click on the <span style="color: orange;">No triggers set up. Click here to add one now.</span> hyperlink.</div><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; text-align: center;"><img alt="Add New Trigger" border="0" data-original-height="106" data-original-width="550" src="https://2.bp.blogspot.com/-vuwYrDtUmpc/W7SxvTFRwDI/AAAAAAAAD5g/qOZ1UHKp6LMkb-cReorXX2eQRu4q3TuzACLcBGAs/s1600/Add%2BNew%2BTrigger.jpg" title="Add New Trigger" /></div><div style="text-align: justify;">In the form that pop-ups, select the function you want to run and the appropriate event. In this example, the <span style="color: orange;">greeting</span> function was selected from the dropdown list, along with the <span style="color: orange;">From spreadsheet</span> and <span style="color: orange;">On open</span> event. To save the trigger, click on the <span style="color: orange;">Save</span> button. That's it!</div><div style="text-align: justify;"></div><div style="text-align: justify;"><div class="separator" style="clear: both; text-align: center;"><img alt="Set An Open Trigger" border="0" data-original-height="147" data-original-width="550" src="https://4.bp.blogspot.com/-oIDDkg8EUsM/W7U7yJ5-N5I/AAAAAAAAD6c/NLHmLYsMKaACXoIB5GP4V-syc-mitw6cACLcBGAs/s1600/Set%2BAn%2BOpen%2BTrigger.jpg" title="Set An Open Trigger" /></div></div><div style="text-align: justify;"><br /><br /><div style="text-align: justify;"><b><span style="color: red;"><u>The programmatic way</u></span></b><br /><div style="text-align: justify;"><br /></div></div>The programmatic way is probably as easy as the manual one. You just have to use the <span style="color: orange;">createSpreadsheetOpenTrigger</span> function and adjust it to your needs (e.g. change the function that will be executed when the spreadsheet opens).<br /><br />To set the trigger, simply click the <span style="color: orange;">Run </span>option on the menu of Script editor and select the <span style="color: orange;">createSpreadsheetOpenTrigger </span>function from the sub-menu. The function will run and the trigger will be set.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Run Function On GAS Editor" border="0" data-original-height="221" data-original-width="550" src="https://1.bp.blogspot.com/-qtqnwA6OvdI/W7UlHDrNQOI/AAAAAAAAD6Q/4haU9zGssWIMGZqi_OOVkJW5nTSYxJLkgCLcBGAs/s1600/Run%2BFunction%2BOn%2BGAS%2BEditor.jpg" title="Run Function On GAS Editor" /></div></div><div style="text-align: justify;"><br /></div><br /><b><span style="color: red;"><u>Authorization procedure</u></span></b><br /><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><b><span style="color: red;"><u>Note</u></span></b>: the first time that you will try to install a trigger, either manually or programmatically, you will have to set authorization permissions. So, a message box will pop-up asking you to authorize the trigger. Just click the <span style="color: orange;">Review Permissions</span> button.</div><div style="text-align: justify;"><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Trigger Authorization" border="0" data-original-height="160" data-original-width="468" src="https://2.bp.blogspot.com/-m8jIBhrETHQ/W7S7OL50MnI/AAAAAAAAD50/EBnY8HWXlPchwEaxqMLICcKbdhLIbClsACLcBGAs/s1600/Trigger%2BAuthorization.jpg" title="Trigger Authorization" /></div><br /><span style="color: orange;">Select your Google account</span> to continue.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Select Google Account" border="0" data-original-height="500" data-original-width="550" src="https://4.bp.blogspot.com/-1T9CMCTkTc4/W7S7oaFliCI/AAAAAAAAD58/A9OqxCvZgw8Z_Qnmp8q_6MmCI_MSrON2ACLcBGAs/s1600/Select%2BGoogle%2BAccount.jpg" title="Select Google Account" /></div><br />Finally, in the next form, click on the <span style="color: orange;">Allow </span>button.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Allow Access To Google Account" border="0" data-original-height="797" data-original-width="472" src="https://2.bp.blogspot.com/-rNbHbDJWNzo/W7S8K3YKK1I/AAAAAAAAD6E/yqvO-aSIaQE7eRwtRnY8OAYvTgYK1uaEACLcBGAs/s1600/Allow%2BAccess%2BTo%2BGoogle%2BAccount.jpg" title="Allow Access To Google Account" /></div><br />Congratulations! You have successfully installed a trigger! Read <a href="https://developers.google.com/apps-script/guides/triggers/installable#restrictions">here</a> about the restrictions that installable triggers have.<br /><br /><div style="text-align: justify;"><br /></div><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3 style="text-align: justify;"><span style="color: #3d85c6;"><b>Read also</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><a href="https://www.myengineeringworld.net/2018/08/create-use-custom-gas-function.html">How To Create & Use A Custom Function In Google Sheets</a></div></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-30389284633806346302018-08-28T00:22:00.000+03:002018-08-28T00:39:16.749+03:00Geocoding & Reverse Geocoding Functions In Google Sheets<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;">Last updated: 28/08/2018, 1 min read (without the code)<br /><br /></div><div style="text-align: justify;"><div class="separator" style="clear: both; text-align: center;"><img alt="Geocoding & Reverse Geocoding Functions In Google Sheets" border="0" data-original-height="264" data-original-width="550" src="https://3.bp.blogspot.com/-CrRogjpVArg/W4RihCaPV0I/AAAAAAAAD3E/-O-sy0Xwd9o_Pe0Lb-V-7hSXTM9ZMSrpwCLcBGAs/s1600/Geocoding%2B%2526%2BReverse%2BGeocoding%2BFunctions%2BIn%2BGoogle%2BSheets.jpg" title="Geocoding & Reverse Geocoding Functions In Google Sheets" /></div><br /></div><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div><div style="text-align: justify;">In the <a href="https://www.myengineeringworld.net/2018/08/create-use-custom-gas-function.html">previous post</a>, we learned the basics about developing and using a custom function in Google Sheets. It is time now to switch to something more advanced compared to our first example. We have seen in the past how to use <a href="https://www.myengineeringworld.net/2014/06/geocoding-using-vba-google-api.html">Google’s geocoding services</a> from VBA. <span style="color: red;">Here we will see how to call these services using Google Apps Script (GAS).</span> The main advantage when developing a GAS function is that, unlike VBA, you don’t need to explicitly <a href="https://www.myengineeringworld.net/2018/02/how-to-get-free-google-api-key.html">get an API key</a>. The overall approach is slightly different than in the case of VBA since there is a <a href="https://developers.google.com/apps-script/reference/maps/geocoder">Geocoder class</a> available in GAS.</div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">GAS code</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div><div style="text-align: justify;">Below you will find the code for five different GAS functions:</div><ul style="text-align: left;"><li><span style="color: orange;">getCoordinates</span></li><span style="color: orange;"> </span><li><span style="color: orange;">getCoordinatesArray</span></li><span style="color: orange;"> </span><li><span style="color: orange;">getLatitude</span></li><span style="color: orange;"> </span><li><span style="color: orange;">getLongitude</span></li><span style="color: orange;"> </span><li><span style="color: orange;">getAddress</span></li></ul><div style="text-align: justify;">I think that the function names are self-explanatory. It should be noted that the getCoordinatesArray is identical to getCoordinates, but it returns the latitude and longitude as an array, not as a single string. In addition, the getAddress function is performing actually <a href="https://www.myengineeringworld.net/2018/03/reverse-geocoding-vba-google-api.html">reverse geocoding</a>. The idea behind these functions is similar to VBA, so after reading the request status, then if it is "OK", the corresponding node is read and returned.</div><br /><pre><span style="color: green;">/*</span><br /><span style="color: green;"> -------------------------------------------------------------------------------</span><br /><span style="color: green;"> The next set of functions are used to perform geocoding and reverse geocoding.</span><br /><br /><span style="color: green;"> Written By: Christos Samaras</span><br /><span style="color: green;"> Date: 18/08/2018</span><br /><span style="color: green;"> E-mail: xristos.samaras@gmail.com</span><br /><span style="color: green;"> Site: https://www.myengineeringworld.net</span><br /><span style="color: green;"> -------------------------------------------------------------------------------</span><br /><span style="color: green;">*/</span><br /><br /><span style="color: green;">/**</span><br /><span style="color: green;"> * This function returns the latitude and longitude of a given address using the Geocoder class. </span><br /><span style="color: green;"> *</span><br /><span style="color: green;"> * @param {A2} address A cell that contains an address.</span><br /><span style="color: green;"> * @return The latitude and the longitude of the given address.</span><br /><span style="color: green;"> * @customfunction</span><br /><span style="color: green;"> */</span><br /><span style="color: blue;">function</span> getCoordinates(address)<br />{ <br /> <span style="color: green;">// Initialize the geocoder object.</span><br /> <span style="color: blue;">var</span> geocoder = Maps.newGeocoder().geocode(address);<br /> <br /> <span style="color: green;">// Initialize the latitue/longitude variables.</span><br /> <span style="color: blue;">var</span> lat = 0;<br /> <span style="color: blue;">var</span> <span style="color: blue;">long</span> = 0<br /> <br /> <span style="color: green;">// Check if the response returned without a problem.</span><br /> <span style="color: blue;">if</span> (geocoder.status == <span style="color: #a31515;">'OK'</span>) <br /> {<br /> <span style="color: green;">// Retrieve the latitue/longitude information.</span><br /> lat = geocoder[<span style="color: #a31515;">"results"</span>][0][<span style="color: #a31515;">"geometry"</span>][<span style="color: #a31515;">"location"</span>][<span style="color: #a31515;">"lat"</span>];<br /> <span style="color: blue;">long</span> = geocoder[<span style="color: #a31515;">"results"</span>][0][<span style="color: #a31515;">"geometry"</span>][<span style="color: #a31515;">"location"</span>][<span style="color: #a31515;">"lng"</span>];<br /> }<br /> <br /> <span style="color: green;">// Return the latitue/longitude information as string. </span><br /> <span style="color: blue;">return</span> lat + <span style="color: #a31515;">", "</span> + <span style="color: blue;">long</span>;<br />}<br /><br /><span style="color: green;">// --------------------------------------------------------------------------------------------------------------</span><br /><br /><span style="color: green;">/**</span><br /><span style="color: green;"> * This function returns the latitude and longitude of a given address as an array using the Geocoder class. </span><br /><span style="color: green;"> *</span><br /><span style="color: green;"> * @param {A2} address A cell that contains an address.</span><br /><span style="color: green;"> * @return The latitude and the longitude of the given address.</span><br /><span style="color: green;"> * @customfunction</span><br /><span style="color: green;"> */</span><br /><span style="color: blue;">function</span> getCoordinatesArray(address)<br />{ <br /> <span style="color: green;">// Initialize the geocoder object.</span><br /> <span style="color: blue;">var</span> geocoder = Maps.newGeocoder().geocode(address);<br /> <br /> <span style="color: green;">// Initialize the latitue/longitude variables.</span><br /> <span style="color: blue;">var</span> lat = 0;<br /> <span style="color: blue;">var</span> <span style="color: blue;">long</span> = 0<br /> <br /> <span style="color: green;">// Check if the response returned without a problem.</span><br /> <span style="color: blue;">if</span> (geocoder.status == <span style="color: #a31515;">'OK'</span>) <br /> {<br /> <span style="color: green;">// Retrieve the latitue/longitude information.</span><br /> lat = geocoder[<span style="color: #a31515;">"results"</span>][0][<span style="color: #a31515;">"geometry"</span>][<span style="color: #a31515;">"location"</span>][<span style="color: #a31515;">"lat"</span>];<br /> <span style="color: blue;">long</span> = geocoder[<span style="color: #a31515;">"results"</span>][0][<span style="color: #a31515;">"geometry"</span>][<span style="color: #a31515;">"location"</span>][<span style="color: #a31515;">"lng"</span>];<br /> }<br /> <br /> <span style="color: green;">// Return the latitue/longitude information as an array. </span><br /> <span style="color: blue;">return</span> [lat, <span style="color: blue;">long</span>];<br />}<br /><br /><span style="color: green;">// --------------------------------------------------------------------------------------------------------------</span><br /><br /><span style="color: green;">/**</span><br /><span style="color: green;"> * This function returns the latitude of the given address using the Geocoder class.</span><br /><span style="color: green;"> *</span><br /><span style="color: green;"> * @param {A2} address A cell that contains an address.</span><br /><span style="color: green;"> * @return The latitude of the given address.</span><br /><span style="color: green;"> * @customfunction</span><br /><span style="color: green;"> */</span><br /><span style="color: blue;">function</span> getLatitude(address)<br />{ <br /> <span style="color: green;">// Initialize the geocoder object.</span><br /> <span style="color: blue;">var</span> geocoder = Maps.newGeocoder().geocode(address);<br /> <br /> <span style="color: green;">// Initialize the latitude variable.</span><br /> <span style="color: blue;">var</span> lat = 0;<br /> <br /> <span style="color: green;">// Get the latitude if the response returned without a problem.</span><br /> <span style="color: blue;">if</span> (geocoder.status == <span style="color: #a31515;">'OK'</span>) <br /> lat = geocoder[<span style="color: #a31515;">"results"</span>][0][<span style="color: #a31515;">"geometry"</span>][<span style="color: #a31515;">"location"</span>][<span style="color: #a31515;">"lat"</span>]; <br /> <br /> <span style="color: green;">// Return the latitude as double.</span><br /> <span style="color: blue;">return</span> lat;<br />}<br /><br /><span style="color: green;">// --------------------------------------------------------------------------------------------------------------</span><br /><br /><span style="color: green;">/**</span><br /><span style="color: green;"> * This function returns the longitude of the given address using the Geocoder class.</span><br /><span style="color: green;"> *</span><br /><span style="color: green;"> * @param {A2} address A cell that contains an address.</span><br /><span style="color: green;"> * @return The longitude of the given address.</span><br /><span style="color: green;"> * @customfunction</span><br /><span style="color: green;"> */</span><br /><span style="color: blue;">function</span> getLongitude(address)<br />{ <br /> <span style="color: green;">// Initialize the geocoder object.</span><br /> <span style="color: blue;">var</span> geocoder = Maps.newGeocoder().geocode(address);<br /> <br /> <span style="color: green;">// Initialize the longitude variable.</span><br /> <span style="color: blue;">var</span> <span style="color: blue;">long</span> = 0;<br /> <br /> <span style="color: green;">// Get the latitude if the response returned without a problem.</span><br /> <span style="color: blue;">if</span> (geocoder.status == <span style="color: #a31515;">'OK'</span>) <br /> <span style="color: blue;">long</span> = geocoder[<span style="color: #a31515;">"results"</span>][0][<span style="color: #a31515;">"geometry"</span>][<span style="color: #a31515;">"location"</span>][<span style="color: #a31515;">"lng"</span>];<br /> <br /> <span style="color: green;">// Return the longitude as double. </span><br /> <span style="color: blue;">return</span> <span style="color: blue;">long</span>;<br />}<br /><br /><span style="color: green;">// --------------------------------------------------------------------------------------------------------------</span><br /><br /><span style="color: green;">/**</span><br /><span style="color: green;"> * This function returns the address from a given pair of latitude and longitude using the Geocoder class.</span><br /><span style="color: green;"> * In other words, it performs reverse geocoding.</span><br /><span style="color: green;"> *</span><br /><span style="color: green;"> * @param {A2} lat A cell that contains a latitude value between -90 and +90 degrees.</span><br /><span style="color: green;"> * @param {B2} long A cell that contains a longitude value between -180 and +180 degrees.</span><br /><span style="color: green;"> * @return The address from a given pair of latitude and longitude.</span><br /><span style="color: green;"> * @customfunction</span><br /><span style="color: green;"> */</span><br /> <span style="color: blue;">function</span> getAddress(lat, <span style="color: blue;">long</span>)<br />{<br /> <br /> <span style="color: green;">// Checking the input variables:</span><br /> <span style="color: green;">// The valid range of latitude in degrees is -90 and +90 for the Southern and Northern hemisphere respectively. </span><br /> <span style="color: blue;">if</span>(lat < -90 || lat > 90)<br /> <span style="color: blue;">return</span> <span style="color: #a31515;">"Invalid Latitude"</span>;<br /><br /> <span style="color: green;">// Longitude is in the range -180 and +180 specifying coordinates West and East of the Prime Meridian, respectively.</span><br /> <span style="color: blue;">if</span>(<span style="color: blue;">long</span> < -180 || <span style="color: blue;">long</span> > 180)<br /> <span style="color: blue;">return</span> <span style="color: #a31515;">"Invalid Longitude"</span>;<br /> <br /> <span style="color: blue;">var</span> response = Maps.newGeocoder().reverseGeocode(lat, <span style="color: blue;">long</span>);<br /> <span style="color: blue;">var</span> address = <span style="color: #a31515;">""</span>;<br /> <br /> <span style="color: blue;">if</span> (response.status == <span style="color: #a31515;">'OK'</span>)<br /> address = response[<span style="color: #a31515;">"results"</span>][0][<span style="color: #a31515;">"formatted_address"</span>];<br /> <br /> <span style="color: blue;">return</span> address;<br />}<br /></pre><br /><div style="text-align: justify;">All functions containing descriptions following the approach that was <a href="https://www.myengineeringworld.net/2018/08/create-use-custom-gas-function.html">analyzed here</a>. The getCoordinatesArray function, though, needs "special treatment" to get its results. Here is how you can call it:</div><div style="text-align: justify;">a. Select two adjacent cells.</div><div style="text-align: justify;">b. In one of the two cells, enter the formula:</div><div style="text-align: justify;"><span style="color: orange;"> =TRANSPOSE(ARRAYFORMULA(getCoordinatesArray(A5))) </span></div><div style="text-align: justify;">Where A5 is the cell containing the address you need to geocode.</div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><span style="color: red;"><u>NOTE:</u></span> In the case of multiple results (for example two cities sharing the same name), <span style="color: orange;">the above functions return the first occurrence</span>, so be careful with your input.</div><div style="text-align: justify;"><br /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Online examples</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div>You can find a few examples online in this <a href="https://docs.google.com/spreadsheets/d/12CYzTYXbRqCBEXa8no1eIbYPEOxRZlLaZVgMZahO07k">Google Sheets file</a>.<br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Read also</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><a href="https://www.myengineeringworld.net/2018/08/create-use-custom-gas-function.html">How To Create & Use A Custom Function In Google Sheets</a></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-58315042724768635092018-08-19T21:08:00.000+03:002018-09-08T16:23:12.465+03:00How To Create & Use A Custom Function In Google Sheets<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;">Last updated:08/09/2018, 5 min read<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="How To Create & Use A Custom Function In Google Sheets" border="0" data-original-height="436" data-original-width="456" src="https://3.bp.blogspot.com/-mog57Ed1sBU/W5PMsKbw_OI/AAAAAAAAD4c/2F35TEvNRqYcbV5mKM1MWsR0TycyS8BIwCLcBGAs/s1600/How%2BTo%2BCreate%2B%2526%2BUse%2BA%2BCustom%2BFunction%2BIn%2BGoogle%2BSheets.jpg" title="How To Create & Use A Custom Function In Google Sheets" /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div>Google Sheets, along with Google Docs and Google Slides are a spreadsheet, a word processor, and a presentation program respectively, all part of a free, web-based software office suite offered by Google within its Google Drive service. The three applications are available as web apps, mobile apps and desktop apps (on ChromeOS only). The applications are compatible with the corresponding Microsoft Office file formats, therefore, the user can save an “online file” to his desktop and then open it with the appropriate Office app.<br /><br /><span style="color: orange;">To be more precise, the Google Sheets app is very similar to Excel. If you have ever used Excel, especially some pre-Ribbon version (Office 2003 and back), you will definitely understand how the Google Sheets app works.</span> The similarities don’t stop only on the environment and the user interface. The Google Sheets app contains an internal script editor where the user can create his/her own custom functions, similar to VBA functions in Excel. <span style="color: red;">The language that is used to create these custom functions in Google Sheets is called Apps Script.</span> The Apps Script is also used in other Google apps, so it reminds a lot of the universal usage of VBA in the entire Office suite.<br /><br />The Apps Script is actually the Google’s version of ECMAScript, which runs on its servers (not in the browser). Consequently, Apps Script can be considered as a browser-independent language. If you haven’t heard the ECMAScript in the past, don’t worry! You probably already know another “dialect” of it, called JavaScript. In short, Apps Script is based on JavaScript 1.6 with some portions of 1.7 and 1.8 and provides a subset of ECMAScript 5 API. <a href="https://en.wikipedia.org/wiki/Google_Apps_Script">According to Google</a>, the Apps Script "provides easy ways to automate tasks across Google products and third party services”. This is the language that we will use in this tutorial.<br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Create a new spreadsheet on Google Sheets</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br />Before we write our first custom function using Apps Script, we will learn first how to create a new blank spreadsheet on Google Sheets. So, please follow the next simple steps:<br /><br /><span style="color: red;">Step 1:</span> First of all, ensure that you are logged in to your Google account.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Log In To Google Account" border="0" data-original-height="296" data-original-width="428" src="https://1.bp.blogspot.com/-0oeGAdfnmXk/W3mlXmvQNoI/AAAAAAAAD0o/R11MhpGZMrkJ4Jlv7-WXZcV8WSbyn_M7wCLcBGAs/s1600/Log%2BIn%2BTo%2BGoogle%2BAccount.jpg" title="Log In To Google Account" /></div><br /><span style="color: red;">Step 2:</span> Go to the <a href="https://drive.google.com/">Google Drive</a> page. <br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Logged To Google Drive" border="0" data-original-height="307" data-original-width="550" src="https://4.bp.blogspot.com/-UEvOARV6C-g/W3ml0IUDV3I/AAAAAAAAD0w/WjvWkB6oduA04UxouZqNElIYSM6YFQc9wCLcBGAs/s1600/Logged%2BTo%2BGoogle%2BDrive.jpg" title="Logged To Google Drive" /></div><br /><span style="color: red;">Step 3:</span> In this page, you should click on the <span style="color: orange;">New button</span>. Then, on the drop-down menu select the <span style="color: orange;">Google Sheets</span> option by clicking the small arrow. Finally, click on the <span style="color: orange;">Blank spreadsheet</span> option.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Create A New Spreadsheet In Google Sheets" border="0" data-original-height="420" data-original-width="550" src="https://2.bp.blogspot.com/-8z1mdNiubsg/W3ml_l42xKI/AAAAAAAAD00/99i9TvIWliEhabYEpw5qrhyubaW-wrE1ACLcBGAs/s1600/Create%2BA%2BNew%2BSpreadsheet%2BIn%2BGoogle%2BSheets.jpg" title="Create A New Spreadsheet In Google Sheets" /></div><br /><span style="color: red;">Step 4:</span> A new spreadsheet is created and you are redirected to a new page. To rename and this spreadsheet, <span style="color: orange;">click on the Untitled spreadsheet title</span> and enter your preferred name.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Rename The Spreadsheet" border="0" data-original-height="400" data-original-width="543" src="https://4.bp.blogspot.com/-gLL5bmEvBL8/W3mmcmls3yI/AAAAAAAAD1A/D8M5YWs0KqwFdo3GbhQrP8e7_JZNu0jDACLcBGAs/s1600/Rename%2BThe%2BSpreadsheet.jpg" title="Rename The Spreadsheet" /></div><br />If you <span style="color: orange;">hit enter</span>, the spreadsheet will look like the image below.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Renamed Spreadsheet" border="0" data-original-height="239" data-original-width="550" src="https://4.bp.blogspot.com/-I5b15-bMulE/W3mms0xbyDI/AAAAAAAAD1I/LI1g1ZBp9tsr6-Zz8e_GxlNXlC6qB-sAgCLcBGAs/s1600/Renamed%2BSpreadsheet.jpg" title="Renamed Spreadsheet" /></div><br />Note, that all the changes are automatically saved in Google Drive, so unlike Excel, <span style="color: orange;">you don’t have to manually save it</span>. Here is what you will see on your Google Drive:<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Renamed File On Google Drive" border="0" data-original-height="228" data-original-width="550" src="https://3.bp.blogspot.com/-2ITOuZ13iw0/W3mm8QZGjWI/AAAAAAAAD1M/Uoko2zIXDggA59ZgzmjLLagYqMailQqbgCLcBGAs/s1600/Renamed%2BFile%2BOn%2BGoogle%2BDrive.jpg" title="Renamed File On Google Drive" /></div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>The Apps Script editor</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br />Now that we have created and renamed our spreadsheet, we are ready to switch to our Apps Script editor. Select the <span style="color: orange;">Tools</span> category from the menu and then click on the <span style="color: orange;">Script editor</span> option.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Switch To Script Editor" border="0" data-original-height="292" data-original-width="550" src="https://3.bp.blogspot.com/-2ignI7ovlMw/W3mng6-VpMI/AAAAAAAAD1Y/RB5qb16XPhYpf_Lt0xWEW3mfa62haprfgCLcBGAs/s1600/Switch%2BTo%2BScript%2BEditor.jpg" title="Switch To Script Editor" /></div><br />A new page pop-up; that’s our Apps Script editor or if you prefer our Integrated Development Environment (IDE). <span style="color: orange;">Unlike the VBA IDE, it is relatively simpler, nevertheless, it includes sufficient functionality. </span><br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="The Script Editor" border="0" data-original-height="413" data-original-width="550" src="https://2.bp.blogspot.com/-d42x-6Z6F8s/W3moBnSxSNI/AAAAAAAAD1g/J3ymXCVLG2U1qxxIxKc4CxXXq7_3fxVEgCLcBGAs/s1600/The%2BScript%2BEditor.jpg" title="The Script Editor" /></div><br />The default project contains a single script file (Code.gs) and an empty function (myFunction), which we will modify in a little bit. Script files are similar to VBA modules. Multiple script files can be created and organized inside a single project (i.e. as it is in the case of a VB 6.0 project). Within each script file, you can write code that is accessible from all the script files included in the project. Unlike VBA, there is no option to restrict access using the Option Private Module.<br /><br />If you need to rename the Untitled project, simply <span style="color: orange;">click on the title</span>. An input box will pop-up, prompting you to give a name. <span style="color: orange;">Type</span> your preferred name and click the <span style="color: orange;">OK button</span>.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Rename The Project" border="0" data-original-height="175" data-original-width="437" src="https://1.bp.blogspot.com/-x9F8CsOmZNE/W3moQFAEdJI/AAAAAAAAD1k/TZUf45qEVbcZouQNM1gsPaqlhzasn0RtgCLcBGAs/s1600/Rename%2BThe%2BProject.jpg" title="Rename The Project" /></div>Here is how the renamed project would look like.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="The Renamed Apps Script Project" border="0" data-original-height="217" data-original-width="447" src="https://4.bp.blogspot.com/-O6Ej1dCUXmc/W3mo73MyzXI/AAAAAAAAD10/m6K0ianGdLAyzzg6HkhhcSalOTT6qQq7gCLcBGAs/s1600/The%2BRenamed%2BApps%2BScript%2BProject.jpg" title="The Renamed Apps Script Project" /></div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Creating your first Apps Script function</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><span style="color: red;"><br /></span><span style="color: red;">In this point, it should be highlighted that the custom function will be created inside a container-bound script that is part of our spreadsheet.</span> The container-bound functions are equivalent to VBA functions that are part of an Excel spreadsheet. Bear in mind, though, that <span style="color: orange;">there are also standalone scripts</span>, which are created on Google Drive and usually contain generic functionality that can be used across multiple Google apps. In other words, these scripts are not bound to a specific spreadsheet or document. However, we will talk about this kind of functions and scripts in a different tutorial.</div><div style="text-align: justify;"><br />Our first function will calculate the area of a circle, given its radius. The formula is very simple:<br /><div class="separator" style="clear: both; text-align: center;"><span style="color: red;"><b>Area = π ∙ Radius² </b></span></div><br />Here is the complete code for the custom function:<br /><br /><pre><span style="color: green;">/**</span><br /><span style="color: green;"> * This function returns the area of a circle given its radius. </span><br /><span style="color: green;"> *</span><br /><span style="color: green;"> * @param {A1} radius A cell that contains the circle radius.</span><br /><span style="color: green;"> * @return The area of the circle given its radius.</span><br /><span style="color: green;"> * @customfunction</span><br /><span style="color: green;"> */</span><br /><span style="color: blue;">function</span> circleArea(radius) {<br /> <span style="color: blue;">return</span> Math.PI * Math.pow(radius, 2);<br />} </pre><br />Let’s analyze the code a little bit:<br /><ul><li>The <span style="color: orange;">circleArea </span>is the name of the function that will be used from the spreadsheet. Here we followed the typical camel case notation (e.g. the practice of writing compound words or phrases such that each word or abbreviation in the middle of the phrase begins with a capital letter, with no intervening spaces or punctuation).</li><li>The <span style="color: orange;">radius</span> is our input variable. Note that, unlike VBA, we don’t have to declare the variable’s type (e.g. double) since, in Apps Script, all the variables are of type var.</li><li>The code that starts with the <span style="color: orange;">return</span> word is actually the mathematical formula translated into code. Ιn Apps Script, similar to JavaScript, we have to use an internal library called Math for some common mathematical expressions (as it is in the case of power and the constant pi - π).</li><li>Finally, the <span style="color: orange;">comments</span> that are enclosed between “/*” and “*/” are optional, but very helpful since will be used by the <a href="https://developers.google.com/apps-script/guides/sheets/functions#autocomplete">Autocomplete feature</a>, which we will see below.</li></ul>Your first custom function is almost ready! As you can see in the image below, an asterisk appears next to the Code.gs file. Click the <span style="color: orange;">Save button </span>or use the <span style="color: orange;">CTRL + S</span> shortcut from your keyboard.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Custom Function Before Saving" border="0" data-original-height="254" data-original-width="550" src="https://4.bp.blogspot.com/-tR9YGM5fQLg/W5PHyLDqXQI/AAAAAAAAD3o/_37K9zmYpjgRSoFqHOmpxxgei9K8SIohgCLcBGAs/s1600/Custom%2BFunction%2BBefore%2BSaving.jpg" title="Custom Function Before Saving" /></div><br /><span style="color: red;">Congratulations! You just created your first custom Apps Script function.</span> Here is how the saved function would look like. The asterisk is gone and the function name will appear in the dropdown menu that contains the functions.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Custom Function Saved" border="0" data-original-height="249" data-original-width="550" src="https://4.bp.blogspot.com/-D7fBqmI1lXM/W5PH8kLE_mI/AAAAAAAAD3s/oaC437BIXVUz_2dXh2qhFWRoTIF-BdilgCLcBGAs/s1600/Custom%2BFunction%2BSaved.jpg" title="Custom Function Saved" /></div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Use the custom Apps Script function from the spreadsheet</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br />You can now close the Apps Script editor and go back to the spreadsheet. In a random cell (e.g. B1) start typing the function name (=circleArea). If you just type <span style="color: orange;">=ci</span> notice that the Autocomplete feature appears, showing the function description.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Typing The Function Name" border="0" data-original-height="318" data-original-width="497" src="https://3.bp.blogspot.com/-jMee69BALIo/W5PKtEVKaNI/AAAAAAAAD38/jUBnlz167wcFGhA9qoaIBdFcrsxS3Bw0ACLcBGAs/s1600/Typing%2BThe%2BFunction%2BName.jpg" title="Typing The Function Name" /></div><br />If you click with the mouse upon the Autocomplete window, or simply press the <span style="color: orange;">Tab key</span>, you will see the full function description. <span style="color: red;">This is the reason why it is a good practice to fill the function description in the script editor.</span><br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Full Function Description" border="0" data-original-height="343" data-original-width="491" src="https://3.bp.blogspot.com/-MvIVTY_E6y4/W5PK2TSca0I/AAAAAAAAD4A/e1HRzZNQUmMewn9slw2kHWzyMtogrefCQCLcBGAs/s1600/Full%2BFunction%2BDescription.jpg" title="Full Function Description" /></div><br />If you type <span style="color: orange;">=circleArea(5)</span> and click the Enter, you will see a <span style="color: orange;">Loading message </span>and after a few seconds, <span style="color: orange;">the actual result </span>in the cell (78.53981634).<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Custom Function With Value" border="0" data-original-height="348" data-original-width="550" src="https://2.bp.blogspot.com/-OVT0dWWCHLI/W5PLAi_ThII/AAAAAAAAD4I/y1pNVkHiRsIzi4OgrgYlkKJnIi44P9V_ACLcBGAs/s1600/Custom%2BFunction%2BWith%2BValue.jpg" title="Custom Function With Value" /></div><br />Of course, apart from manual values, we can use the function with cell references. In the example below, we use the function with the cell A4.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Custom Function With Cell Reference" border="0" data-original-height="185" data-original-width="550" src="https://1.bp.blogspot.com/-vFecicKmAuc/W5PLIYtaTDI/AAAAAAAAD4Q/qJvdBv2SmqUzhXa_WX0tow3E_Xd0aK-zgCLcBGAs/s1600/Custom%2BFunction%2BWith%2BCell%2BReference.jpg" title="Custom Function With Cell Reference" /></div><br /><span style="color: orange;">Well done! You have successfully created and used your first custom Apps Script function.</span> More tutorials on Apps Script will <a href="https://www.myengineeringworld.net/2018/08/geocoding-reverse-gas-functions.html">follow soon</a>. Meanwhile, if you need more information about the custom Apps Script functions, you can visit the <a href="https://developers.google.com/apps-script/guides/sheets/functions">official page</a>.<br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Read also</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><a href="https://www.myengineeringworld.net/2018/08/geocoding-reverse-gas-functions.html">Geocoding & Reverse Geocoding Functions In Google Sheets</a></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-83238876039019364542018-07-15T17:54:00.000+03:002018-07-15T17:54:20.936+03:00Get & Set The Default Windows Printer With VBA<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;">Last updated: 15/07/2018, 2 min read (without the code)<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Get & Set The Default Windows Printer With VBA" border="0" data-original-height="256" data-original-width="550" src="https://2.bp.blogspot.com/-qwQKHfYra3I/W0p3IZKdrSI/AAAAAAAADvw/hE1zT9b3v245gPgLJimJPAV42hg0Si2cACLcBGAs/s1600/Get%2B%2526%2BSet%2BThe%2BDefault%2BWindows%2BPrinter%2BWith%2BVBA.jpg" title="Get & Set The Default Windows Printer With VBA" /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div><span style="color: red;">Although VBA is a great language for building Office “applications”, when it comes to handling Windows devices, such as a printer, for example, things start to become difficult.</span> The obvious reason is that the VBA was not designed for this kind of purposes. However, what if your VBA “application” needs to know how many printers are installed and available in the particular computer? What if your “application” must set the default Windows printer to “Adobe PDF”, hence, printing in a PDF file, instead of a paper?<br /><br />To answer the last two questions, somebody might think to search for some old Visual Basic 6.0 code snippets. In other words, he/she might try to find solutions based on some old example. While this totally OK, the VB 6.0 solutions will probably rely on one or more Windows APIs. <span style="color: orange;">So, if you are not very familiar using Windows APIs in your VBA code, you might have troubles adjusting the API calls.</span> The latter is particularly true when the Office version in which your “application” will run, is 64bit. In that case, you need to carefully "alter" the API calls so as to work in 64bit (e.g. data type conversion: the Long should become LongPtr in 64bit).<br /><br />But, are there any simpler solutions? Yes, there are! <span style="color: red;">If we combine the VBA with Windows Management Instrumentation (WMI) and Windows Script Host (WSH) objects we can do wonders quite easily!</span> The VBA module that follows demonstrates several techniques that show: how somebody can get the installed printers from a computer, how to check if a printer is the default one, and, finally, how to set a particular printer to be the default one.<br /><br />Note: the term “application” in the above paragraphs actually implies a solution to a given problem (e.g. a budget spreadsheet), not an application with the strict definition of the term (e.g. an executable). You can develop “real” applications using VB 6.0, as well as with other programming languages, but not with VBA.<br /><div style="text-align: justify;"><br /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>VBA code</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br />The code below is an entire module that contains 3 VBA functions:<br /><ul><li><span style="color: orange;">PrinterExists</span>: A function that checks if there is a printer installed with the given name.</li><li><span style="color: orange;">IsDefaultPrinter</span>: A function that checks if the given printer corresponds to the default Windows printer.</li><li><span style="color: orange;">SetDefaultPrinter</span>: A functions that set the given printer to be the default Windows printer.</li></ul> <br />Next, there are 2 macros that demonstrate how these 3 functions can be used/combined to do something useful:<br /><ul><li><span style="color: red;">GetInstalledPrinters</span>: A macro that loops through all the installed printers of the computer and writes their names in the "Printers" worksheet. Moreover, it checks if each printer is the default one or not.</li><li><span style="color: red;">SetAsTheDefaultPrinter</span>: A macro that sets the selected range, if it corresponds to an installed printer, to be the default Windows printer. The user must select a range within the given range of (valid) printers, and, then, run the macro.</li></ul><br /><pre><span style="color: blue;">Option Explicit</span><br /> <br /> <span style="color: green;">'-----------------------------------------------------------------------------------------------------------------------</span>--<br /> <span style="color: green;">'This module contains 3 functions that can help you whenever you deal with printers from VBA:</span><br /> <span style="color: green;">'- PrinterExists: Checks if there is a printer installed with the given name.</span><br /> <span style="color: green;">'- IsDefaultPrinter: Checks if the given printer corresponds to the default windows printer.</span><br /> <span style="color: green;">'- SetDefaultPrinter: Makes the given printer to be the default one.</span><br /> <span style="color: green;">'</span><br /> <span style="color: green;">'After these functions, there are 2 macros that demonstrate how these functions can be used to do something useful.</span><br /> <span style="color: green;">'Note that the macros were adjusted to work with the specific workbook that contains the worksheet named "Printers".</span><br /> <span style="color: green;">'- GetInstalledPrinters: Loops through all the installed printers and writes their names in the "Printers" worksheet.</span><br /> <span style="color: green;">' Moreover, it checks if each printer is the default one.</span><br /> <span style="color: green;">'</span><br /> <span style="color: green;">'- SetAsTheDefaultPrinter: The user selects a range within the given range of printers and then by running the macro</span><br /> <span style="color: green;">' the selected printer becomes the default one.</span><br /> <span style="color: green;">'</span><br /> <span style="color: green;">'Written By: Christos Samaras</span><br /> <span style="color: green;">'Date: 14/08/2018</span><br /> <span style="color: green;">'E-mail: xristos.samaras@gmail.com</span><br /> <span style="color: green;">'Site: https://www.myengineeringworld.net</span><br /> <span style="color: green;">'-----------------------------------------------------------------------------------------------------------------------</span>--<br /> <br /><span style="color: blue;">Function</span> PrinterExists(printerName <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">Boolean</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> computer <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span><br /> <span style="color: blue;">Dim</span> wmiService <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <span style="color: blue;">Dim</span> installedPrinters <span style="color: blue;">As</span> <span style="color: #2b91af;">Variant</span><br /> <span style="color: blue;">Dim</span> printer <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Check if the printer name is empty.</span><br /> <span style="color: blue;">If</span> printerName = vbNullString <span style="color: blue;">Then</span> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /><br /> <span style="color: green;">'Set the computer. Dot means the computer running the code.</span> <br /> computer = <span style="color: #a31515;">"."</span><br /> <br /> <span style="color: green;">'Get the WMI object</span><br /> <span style="color: blue;">Set</span> wmiService = GetObject(<span style="color: #a31515;">"winmgmts:"</span> & <span style="color: #a31515;">"{impersonationLevel=impersonate}!\\"</span> & computer & <span style="color: #a31515;">"\root\cimv2"</span>)<br /> <br /> <span style="color: green;">'Retrieve information about the installed printers (by running a query).</span><br /> <span style="color: blue;">Set</span> installedPrinters = wmiService.ExecQuery(<span style="color: #a31515;">"Select * from Win32_Printer"</span>)<br /> <br /> <span style="color: green;">'If an error occurs in the previous step, the function should exit and return False.</span><br /> <span style="color: blue;">If</span> Err.Number <> 0 <span style="color: blue;">Then</span> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /><br /> <span style="color: green;">'Loop through all the installed printers. If the given name matches to any of the installed printers, exit the loop and return True.</span> <br /> <span style="color: blue;">For</span> <span style="color: blue;">Each</span> printer <span style="color: blue;">In</span> installedPrinters<br /> <span style="color: blue;">If</span> UCase(printer.Name) = UCase(printerName) <span style="color: blue;">Then</span><br /> PrinterExists = <span style="color: blue;">True</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <span style="color: blue;">Next</span> printer<br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Function</span> IsDefaultPrinter(printerName <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">Boolean</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> computer <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span><br /> <span style="color: blue;">Dim</span> wmiService <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <span style="color: blue;">Dim</span> installedPrinters <span style="color: blue;">As</span> <span style="color: #2b91af;">Variant</span><br /> <span style="color: blue;">Dim</span> printer <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Check if the printer name is empty.</span><br /> <span style="color: blue;">If</span> printerName = vbNullString <span style="color: blue;">Then</span> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> <br /> <span style="color: green;">'Set the computer. Dot means the computer running the code.</span> <br /> computer = <span style="color: #a31515;">"."</span><br /> <br /> <span style="color: green;">'Get the WMI object</span><br /> <span style="color: blue;">Set</span> wmiService = GetObject(<span style="color: #a31515;">"winmgmts:"</span> & <span style="color: #a31515;">"{impersonationLevel=impersonate}!\\"</span> & computer & <span style="color: #a31515;">"\root\cimv2"</span>)<br /> <br /> <span style="color: green;">'Retrieve information about the installed printers (by running a query).</span><br /> <span style="color: blue;">Set</span> installedPrinters = wmiService.ExecQuery(<span style="color: #a31515;">"Select * from Win32_Printer"</span>)<br /> <br /> <span style="color: green;">'If an error occurs in the previous step, the function should exit and return False.</span><br /> <span style="color: blue;">If</span> Err.Number <> 0 <span style="color: blue;">Then</span> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /><br /> <span style="color: green;">'Loop through all the installed printers. If the given name matches to any of the installed printers</span> <br /> <span style="color: green;">'and the Default property is set to True, exit the loop and return True.</span><br /> <span style="color: blue;">For</span> <span style="color: blue;">Each</span> printer <span style="color: blue;">In</span> installedPrinters<br /> <span style="color: blue;">If</span> UCase(printer.Name) = UCase(printerName) <span style="color: blue;">And</span> printer.Default = <span style="color: blue;">True</span> <span style="color: blue;">Then</span><br /> IsDefaultPrinter = <span style="color: blue;">True</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <span style="color: blue;">Next</span> printer<br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Function</span> SetDefaultPrinter(printerName <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">Boolean</span><br /> <br /> <span style="color: green;">'Declaring the necessary variable.</span><br /> <span style="color: blue;">Dim</span> wshNetwork <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Check if the printer name is empty.</span><br /> <span style="color: blue;">If</span> printerName = vbNullString <span style="color: blue;">Then</span> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /><br /> <span style="color: green;">'Test if the printer is already the default one. If yes, return True.</span> <br /> <span style="color: blue;">If</span> IsDefaultPrinter(printerName) = <span style="color: blue;">True</span> <span style="color: blue;">Then</span><br /> SetDefaultPrinter = <span style="color: blue;">True</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'The printer is not the default one. Create the WScript.Network object.</span><br /> <span style="color: blue;">Set</span> wshNetwork = CreateObject(<span style="color: #a31515;">"WScript.Network"</span>)<br /> <br /> <span style="color: green;">'If the WScript.Network object was not created, exit.</span><br /> <span style="color: blue;">If</span> wshNetwork <span style="color: blue;">Is</span> <span style="color: blue;">Nothing</span> <span style="color: blue;">Then</span> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /><br /> <span style="color: green;">'Set the given printer to be the default one.</span> <br /> wshNetwork.SetDefaultPrinter printerName<br /> <br /> <span style="color: green;">'Release the WScript.Network object.</span><br /> <span style="color: blue;">Set</span> wshNetwork = <span style="color: blue;">Nothing</span><br /> <br /> <span style="color: green;">'Check (again) if after the change, the given printer is indeed the default one.</span><br /> SetDefaultPrinter = IsDefaultPrinter(printerName)<br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Sub</span> GetInstalledPrinters()<br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> sht <span style="color: blue;">As</span> Worksheet<br /> <span style="color: blue;">Dim</span> computer <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span><br /> <span style="color: blue;">Dim</span> wmiService <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <span style="color: blue;">Dim</span> installedPrinters <span style="color: blue;">As</span> <span style="color: #2b91af;">Variant</span><br /> <span style="color: blue;">Dim</span> printer <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <span style="color: blue;">Dim</span> i <span style="color: blue;">As</span> <span style="color: #2b91af;">Integer</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Set the worksheet in which the information will be written.</span><br /> <span style="color: blue;">Set</span> sht = ThisWorkbook.Worksheets(<span style="color: #a31515;">"Printers"</span>)<br /> <br /> <span style="color: green;">'Check if the sheet exist (there is no error).</span><br /> <span style="color: blue;">If</span> Err.Number <> 0 <span style="color: blue;">Then</span><br /> MsgBox <span style="color: #a31515;">"The sheet does not exists!"</span>, vbCritical, <span style="color: #a31515;">"Sheet Name Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Clear existing data.</span><br /> <span style="color: blue;">Call</span> ClearAll<br /> <br /> <span style="color: green;">'Set the computer. Dot means the computer running the code.</span> <br /> computer = <span style="color: #a31515;">"."</span><br /> <br /> <span style="color: green;">'Get the WMI object</span><br /> <span style="color: blue;">Set</span> wmiService = GetObject(<span style="color: #a31515;">"winmgmts:"</span> & <span style="color: #a31515;">"{impersonationLevel=impersonate}!\\"</span> & computer & <span style="color: #a31515;">"\root\cimv2"</span>)<br /> <br /> <span style="color: green;">'Retrieve information about the installed printers (by running a query).</span><br /> <span style="color: blue;">Set</span> installedPrinters = wmiService.ExecQuery(<span style="color: #a31515;">"Select * from Win32_Printer"</span>)<br /> <br /> <span style="color: green;">'If an error occurs in the previous step, inform the user.</span><br /> <span style="color: blue;">If</span> Err.Number <> 0 <span style="color: blue;">Then</span><br /> MsgBox <span style="color: #a31515;">"Could not retrieve the printer information from WMI object!"</span>, vbCritical, <span style="color: #a31515;">"WMI Object Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Set the starting row.</span><br /> i = 5<br /> <br /> <span style="color: green;">'Loop through all the installed printers and get their name. Check if one of them is the default one.</span><br /> <span style="color: blue;">For</span> <span style="color: blue;">Each</span> printer <span style="color: blue;">In</span> installedPrinters<br /> <br /> <span style="color: green;">'Write the results to the worksheet.</span><br /> sht.Range(<span style="color: #a31515;">"C"</span> & i).Value = printer.Name<br /> sht.Range(<span style="color: #a31515;">"D"</span> & i).Value = printer.Default<br /> i = i + 1<br /> <br /> <span style="color: blue;">Next</span> printer<br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /><br /><span style="color: blue;">End</span> <span style="color: blue;">Sub</span><br /><br /><span style="color: blue;">Sub</span> SetAsTheDefaultPrinter()<br /> <br /> <span style="color: green;">'Declaring the necessary variable.</span><br /> <span style="color: blue;">Dim</span> sht <span style="color: blue;">As</span> Worksheet<br /> <span style="color: blue;">Dim</span> rng <span style="color: blue;">As</span> Range<br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Set the worksheet in which the information will be written.</span><br /> <span style="color: blue;">Set</span> sht = ThisWorkbook.Worksheets(<span style="color: #a31515;">"Printers"</span>)<br /> <br /> <span style="color: green;">'Check if the sheet exist (there is no error).</span><br /> <span style="color: blue;">If</span> Err.Number <> 0 <span style="color: blue;">Then</span><br /> MsgBox <span style="color: #a31515;">"The sheet does not exists!"</span>, vbCritical, <span style="color: #a31515;">"Sheet Name Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Get the intersected range.</span><br /> <span style="color: blue;">Set</span> rng = Application.Intersect(sht.Range(<span style="color: #a31515;">"C5:C24"</span>), Selection.Range(<span style="color: #a31515;">"A1"</span>))<br /> <br /> <span style="color: green;">'If there is no "common" range, exit.</span><br /> <span style="color: blue;">If</span> rng <span style="color: blue;">Is</span> <span style="color: blue;">Nothing</span> <span style="color: blue;">Then</span><br /> MsgBox <span style="color: #a31515;">"The selected range is outside the 'C5:C24' range!"</span>, vbCritical, <span style="color: #a31515;">"Invalid Common Range Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'If the common range is empty, exit.</span><br /> <span style="color: blue;">If</span> IsEmpty(rng) <span style="color: blue;">Then</span><br /> MsgBox <span style="color: #a31515;">"The range you selected is empty!"</span>, vbCritical, <span style="color: #a31515;">"Empty Range Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Check if the selected printer is already the default printer.</span><br /> <span style="color: blue;">If</span> IsDefaultPrinter(rng.Range(<span style="color: #a31515;">"A1"</span>)) <span style="color: blue;">Then</span><br /> MsgBox <span style="color: #a31515;">"The selected printer '"</span> & rng.Range(<span style="color: #a31515;">"A1"</span>) & <span style="color: #a31515;">"' is already the default printer!"</span>, vbExclamation, <span style="color: #a31515;">"Default Printer Warning"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Finally, set the selected printer as the default one and inform the user.</span><br /> <span style="color: blue;">If</span> SetDefaultPrinter(rng.Range(<span style="color: #a31515;">"A1"</span>)) = <span style="color: blue;">True</span> <span style="color: blue;">Then</span><br /> <br /> <span style="color: green;">'Run the GetInstalledPrinters macro to "prove" the change.</span><br /> <span style="color: blue;">Call</span> GetInstalledPrinters<br /> <br /> <span style="color: green;">'The process succeded.</span><br /> MsgBox <span style="color: #a31515;">"The selected printer '"</span> & rng.Range(<span style="color: #a31515;">"A1"</span>) & <span style="color: #a31515;">"' was set as the default printer!"</span>, vbInformation, <span style="color: #a31515;">"Success"</span><br /> <br /> <span style="color: blue;">Else</span><br /> <br /> <span style="color: green;">'The process failed.</span><br /> MsgBox <span style="color: #a31515;">"It was impossible to set the selected printer '"</span> & rng.Range(<span style="color: #a31515;">"A1"</span>) & <span style="color: #a31515;">"' as the default printer!"</span>, vbCritical, <span style="color: #a31515;">"Failure"</span><br /> <br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /><br /><span style="color: blue;">End</span> <span style="color: blue;">Sub</span><br /><br /><span style="color: blue;">Sub</span> ClearAll()<br /> <br /> <span style="color: green;">'Declaring the necessary variable.</span><br /> <span style="color: blue;">Dim</span> sht <span style="color: blue;">As</span> Worksheet<br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Set the worksheet in which the information will be written.</span><br /> <span style="color: blue;">Set</span> sht = ThisWorkbook.Worksheets(<span style="color: #a31515;">"Printers"</span>)<br /> <br /> <span style="color: green;">'Check if the sheet exist (there is no error).</span><br /> <span style="color: blue;">If</span> Err.Number <> 0 <span style="color: blue;">Then</span><br /> MsgBox <span style="color: #a31515;">"The sheet does not exists!"</span>, vbCritical, <span style="color: #a31515;">"Sheet Name Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Clear the data.</span><br /> sht.Range(<span style="color: #a31515;">"C5:D24"</span>).ClearContents<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Sub</span><br /></pre><span style="color: orange;"><br /></span><span style="color: orange;">Note that the two macros were adjusted to work with the specific workbook that contains a worksheet named "Printers".</span> You can find this workbook in the Downloads section that follows.<br /><br /><br /><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;"><b>Downloads</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://goo.gl/jk5ttG"><img alt="Download" border="0" height="108" src="https://4.bp.blogspot.com/-JhKlqA-EMkM/UoVNR1_v4II/AAAAAAAABtM/Q1GE7I4707g/s1600/Download.jpg" title="Click to download the file" width="320" /></a></div><div style="text-align: justify;"><br /></div>The file can be opened with <span style="color: red;">Excel 2007</span> or newer. Please enable macros before using it.</div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-48622289480684288092018-06-30T23:38:00.000+03:002018-09-07T16:13:26.122+03:00Get Laptop's Battery Information Through VBA<div dir="ltr" style="text-align: left;" trbidi="on">Last updated: 03/07/2018, 1 min read (without the code)<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://4.bp.blogspot.com/--dqsXmeF9Ko/W5J5Tk5SS1I/AAAAAAAAD3U/BoFMUk-TaSsXoL6P_ftGM1-2HqB9hwPaQCLcBGAs/s1600/Get%2BLaptop%2BBattery%2BInformation%2BThrough%2BVBA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="Get Laptop Battery Information Through VBA" border="0" data-original-height="447" data-original-width="462" src="https://4.bp.blogspot.com/--dqsXmeF9Ko/W5J5Tk5SS1I/AAAAAAAAD3U/BoFMUk-TaSsXoL6P_ftGM1-2HqB9hwPaQCLcBGAs/s1600/Get%2BLaptop%2BBattery%2BInformation%2BThrough%2BVBA.jpg" title="Get Laptop Battery Information Through VBA" /></a></div><div style="text-align: justify;"></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div><div style="text-align: justify;"></div><div style="text-align: justify;">As the title indicates, in this post we will learn how to get several properties related to the battery of the laptop that runs our VBA code. Unfortunately, <span style="color: red;">VBA does not have any built-in functionalit</span>y that can be used to retrieve this kind of information. Therefore, with the help of <a href="https://www.myengineeringworld.net/2014/12/get-public-ip-local-ip-mac-address-vba.html">WMI</a> we will get the following battery properties:</div><ul style="text-align: left;"><li>Availability</li><li>Battery status</li><li>Chemistry</li><li>Estimated charge remaining</li><li>Estimated run time</li><li>Time on battery</li><li>Time to full charge</li></ul><div style="text-align: justify;"><br />These are probably the most useful ones. If you need other attributes, you can check this link to find the additional properties that are available in the <a href="https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-battery">Win32_Battery class</a>. The technique to retrieve any of the other property is similar to the one used below.</div><div style="text-align: justify;"><br /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>VBA code</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;">The code was written in a way that all the functions are based on the <span style="color: red;">GetBatteryObject</span> function, which returns an object containing several properties about the laptop's battery. The other functions simply use this object to check if a particular property exists. If yes, then they retrieve the value of that property. If the returned numeric value is an enumeration, as it is in the case of <span style="color: orange;">GetBatteryAvailability, GetBatteryStatus and GetBatteryChemistry</span> functions, the code returns the actual string value. In the other cases, the functions return the numeric value that corresponds either to a charge percentage or to time (minutes/seconds).<br /><br /><pre><span style="color: blue;">Option Explicit</span><br /><br /><span style="color: green;">'-------------------------------------------------------------------------------------------</span><br /><span style="color: green;">'This module contains some functions for retrieving information about the laptop's battery.</span><br /><span style="color: green;">'The main function is the GetBatteryObject, which retrieves an object containing several</span><br /><span style="color: green;">'properties about the laptop's battery. The other functions simply use this object to</span><br /><span style="color: green;">'retrieve the appropriate property. The GetBatteryObject function uses WMI to query</span><br /><span style="color: green;">'the Win32_Battery class of Windows. More information about this class can be found in:</span><br /><span style="color: green;">'https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-battery</span><br /><br /><span style="color: green;">'Written By: Christos Samaras</span><br /><span style="color: green;">'Date: 30/06/2018</span><br /><span style="color: green;">'Last Updated: 03/07/2018</span><br /><span style="color: green;">'E-mail: xristos.samaras@gmail.com</span><br /><span style="color: green;">'Site: https://www.myengineeringworld.net</span><br /><span style="color: green;">'-------------------------------------------------------------------------------------------</span><br /><br /><span style="color: blue;">Private</span> <span style="color: blue;">Function</span> GetBatteryObject() <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <br /> <span style="color: green;">'----------------------------------------------------------------------------</span><br /> <span style="color: green;">'Returns an object containing several properties about the laptop's battery.</span><br /> <span style="color: green;">'The function is private, so as to not be visible from Excel worksheets.</span><br /> <span style="color: green;">'----------------------------------------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> computer <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span><br /> <span style="color: blue;">Dim</span> wmiService <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <span style="color: blue;">Dim</span> colItems <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <span style="color: blue;">Dim</span> item <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Set the computer.</span><br /> computer = <span style="color: #a31515;">"."</span><br /> <br /> <span style="color: green;">'The root\cimv2 namespace is used to access the Win32_Battery class.</span><br /> <span style="color: blue;">Set</span> wmiService = GetObject(<span style="color: #a31515;">"winmgmts:\\"</span> & computer & <span style="color: #a31515;">"\root\cimv2"</span>)<br /> <br /> <span style="color: green;">'A select query is used to get a collection of battery objects.</span><br /> <span style="color: blue;">Set</span> colItems = wmiService.ExecQuery(<span style="color: #a31515;">"SELECT * FROM Win32_Battery"</span>, , 48)<br /> <br /> <span style="color: green;">'Note, an alternative here will be to select only the properties you need and not all (the asterisk means all).</span><br /> <span style="color: green;">'In that case, the query could be written like this:</span><br /> <span style="color: green;">'Set colItems = wmiService.ExecQuery("SELECT Availability, BatteryStatus, Chemistry," + _</span><br /> <span style="color: #a31515;">"EstimatedChargeRemaining, EstimatedRunTime,"</span> + _<br /> <span style="color: #a31515;">"TimeOnBattery, TimeToFullCharge FROM Win32_Battery"</span>, , 48)<br /> <br /> <span style="color: green;">'Get the first object that is not null.</span><br /> <span style="color: blue;">For</span> <span style="color: blue;">Each</span> item <span style="color: blue;">In</span> colItems<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IsNull(item) <span style="color: blue;">Then</span> <span style="color: blue;">Set</span> GetBatteryObject = item<br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> Next<br /> <br /> <span style="color: green;">'If no battery object is found, return nothing.</span><br /> <span style="color: blue;">Set</span> GetBatteryObject = <span style="color: blue;">Nothing</span><br /><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Public</span> <span style="color: blue;">Function</span> GetBatteryAvailability() <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span><br /> <br /> <span style="color: green;">'-----------------------------------------</span><br /> <span style="color: green;">'Returns the availability of the battery.</span><br /> <span style="color: green;">'-----------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> batteryObject <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <span style="color: blue;">Dim</span> i <span style="color: blue;">As</span> <span style="color: #2b91af;">Integer</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Get the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = GetBatteryObject()<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IsObject(batteryObject) <span style="color: blue;">Then</span><br /> GetBatteryAvailability = <span style="color: #a31515;">"Battery Object Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Check if the Availability property can be retrieved.</span><br /> <span style="color: blue;">If</span> IsNull(batteryObject.Availability) <span style="color: blue;">Then</span><br /> GetBatteryAvailability = <span style="color: #a31515;">"Battery Property Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Get the numeric value from the property.</span><br /> i = <span style="color: blue;">CInt</span>(batteryObject.Availability)<br /> <span style="color: blue;">If</span> i < 1 <span style="color: blue;">Or</span> i > 21 <span style="color: blue;">Then</span><br /> GetBatteryAvailability = <span style="color: #a31515;">"Battery Availability Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Use the numeric value to return the actual string value.</span><br /> GetBatteryAvailability = Array(<span style="color: #a31515;">"Other"</span>, <span style="color: #a31515;">"Unknown"</span>, <span style="color: #a31515;">"Running/Full Power"</span>, <span style="color: #a31515;">"Warning"</span>, <span style="color: #a31515;">"In Test"</span>, <span style="color: #a31515;">"Not Applicable"</span>, _<br /> <span style="color: #a31515;">"Power Off"</span>, <span style="color: #a31515;">"Off Line"</span>, <span style="color: #a31515;">"Off Duty"</span>, <span style="color: #a31515;">"Degraded"</span>, <span style="color: #a31515;">"Not Installed"</span>, <span style="color: #a31515;">"Install Error"</span>, _<br /> <span style="color: #a31515;">"Power Save - Unknown"</span>, <span style="color: #a31515;">"Power Save - Low Power Mode"</span>, <span style="color: #a31515;">"Power Save - Standby"</span>, _<br /> <span style="color: #a31515;">"Power Cycle"</span>, <span style="color: #a31515;">"Power Save - Warning"</span>, <span style="color: #a31515;">"Paused"</span>, <span style="color: #a31515;">"Not Ready"</span>, <span style="color: #a31515;">"Not Configured"</span>, _<br /> <span style="color: #a31515;">"Quiesced"</span>)(i - 1)<br /> <br /> <span style="color: green;">'Release the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = <span style="color: blue;">Nothing</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Public</span> <span style="color: blue;">Function</span> GetBatteryStatus() <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span><br /> <br /> <span style="color: green;">'-----------------------------------</span><br /> <span style="color: green;">'Returns the status of the battery.</span><br /> <span style="color: green;">'-----------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> batteryObject <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <span style="color: blue;">Dim</span> i <span style="color: blue;">As</span> <span style="color: #2b91af;">Integer</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Get the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = GetBatteryObject()<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IsObject(batteryObject) <span style="color: blue;">Then</span><br /> GetBatteryStatus = <span style="color: #a31515;">"Battery Object Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Check if the BatteryStatus property can be retrieved.</span><br /> <span style="color: blue;">If</span> IsNull(batteryObject.BatteryStatus) <span style="color: blue;">Then</span><br /> GetBatteryStatus = <span style="color: #a31515;">"Battery Property Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Get the numeric value from the property.</span><br /> i = <span style="color: blue;">CInt</span>(batteryObject.BatteryStatus)<br /> <span style="color: blue;">If</span> i < 1 <span style="color: blue;">Or</span> i > 11 <span style="color: blue;">Then</span><br /> GetBatteryStatus = <span style="color: #a31515;">"Battery Status Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Use the numeric value to return the actual string value.</span><br /> GetBatteryStatus = Array(<span style="color: #a31515;">"Discharging"</span>, <span style="color: #a31515;">"On A/C"</span>, <span style="color: #a31515;">"Fully Charged"</span>, <span style="color: #a31515;">"Low"</span>, <span style="color: #a31515;">"Critical"</span>, <span style="color: #a31515;">"Charging"</span>, <span style="color: #a31515;">"Charging High"</span>, _<br /> <span style="color: #a31515;">"Charging Low"</span>, <span style="color: #a31515;">"Charging Critical"</span>, <span style="color: #a31515;">"Undefined"</span>, <span style="color: #a31515;">"Partially Charged"</span>)(i - 1)<br /> <br /> <span style="color: green;">'Release the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = <span style="color: blue;">Nothing</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /><br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Public</span> <span style="color: blue;">Function</span> GetBatteryChemistry() <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span><br /> <br /> <span style="color: green;">'---------------------------------</span><br /> <span style="color: green;">'Returns the battery's chemistry.</span><br /> <span style="color: green;">'---------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> batteryObject <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <span style="color: blue;">Dim</span> i <span style="color: blue;">As</span> <span style="color: #2b91af;">Integer</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Get the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = GetBatteryObject()<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IsObject(batteryObject) <span style="color: blue;">Then</span><br /> GetBatteryChemistry = <span style="color: #a31515;">"Battery Object Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Check if the Chemistry property can be retrieved.</span><br /> <span style="color: blue;">If</span> IsNull(batteryObject.Chemistry) <span style="color: blue;">Then</span><br /> GetBatteryChemistry = <span style="color: #a31515;">"Battery Property Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Get the numeric value from the property.</span><br /> i = <span style="color: blue;">CInt</span>(batteryObject.Chemistry)<br /> <span style="color: blue;">If</span> i < 1 <span style="color: blue;">Or</span> i > 11 <span style="color: blue;">Then</span><br /> GetBatteryChemistry = <span style="color: #a31515;">"Battery Chemistry Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Use the numeric value to return the actual string value.</span><br /> GetBatteryChemistry = Array(<span style="color: #a31515;">"Other"</span>, <span style="color: #a31515;">"Unknown"</span>, <span style="color: #a31515;">"Lead Acid"</span>, <span style="color: #a31515;">"Nickel Cadmium"</span>, <span style="color: #a31515;">"Nickel Metal Hydride"</span>, _<br /> <span style="color: #a31515;">"Lithium-ion"</span>, <span style="color: #a31515;">"Zinc air"</span>, <span style="color: #a31515;">"Lithium Polymer"</span>)(i - 1)<br /> <br /> <span style="color: green;">'Release the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = <span style="color: blue;">Nothing</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Public</span> <span style="color: blue;">Function</span> GetEstimatedChargeRemaining() <span style="color: blue;">As</span> <span style="color: #2b91af;">Integer</span><br /> <br /> <span style="color: green;">'-------------------------------------------------------------</span><br /> <span style="color: green;">'Returns the remaining charge of the battery as a percentage.</span><br /> <span style="color: green;">'A value of 100 means that the battery is fully charged.</span><br /> <span style="color: green;">'-------------------------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variable.</span><br /> <span style="color: blue;">Dim</span> batteryObject <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Get the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = GetBatteryObject()<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IsObject(batteryObject) <span style="color: blue;">Then</span><br /> GetEstimatedChargeRemaining = <span style="color: #a31515;">"Battery Object Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Check if the EstimatedChargeRemaining property can be retrieved.</span><br /> <span style="color: blue;">If</span> IsNull(batteryObject.EstimatedChargeRemaining) <span style="color: blue;">Then</span><br /> GetEstimatedChargeRemaining = <span style="color: #a31515;">"Battery Property Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Get the numeric value from the property.</span><br /> GetEstimatedChargeRemaining = <span style="color: blue;">CInt</span>(batteryObject.EstimatedChargeRemaining)<br /> <br /> <span style="color: green;">'Release the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = <span style="color: blue;">Nothing</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Public</span> <span style="color: blue;">Function</span> GetEstimatedRunTime() <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span><br /> <br /> <span style="color: green;">'------------------------------------------------------------------------------------------------------------</span><br /> <span style="color: green;">'Returns the time (in minutes) to battery charge depletion under the present load conditions if the utility</span><br /> <span style="color: green;">'power is off, or lost and remains off, or the laptop is disconnected from a power source.</span><br /> <span style="color: green;">'A value of 30 means that the battery can continue providing power to your laptop for about 30 minutes.</span><br /> <span style="color: green;">'------------------------------------------------------------------------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variable.</span><br /> <span style="color: blue;">Dim</span> batteryObject <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Get the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = GetBatteryObject()<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IsObject(batteryObject) <span style="color: blue;">Then</span><br /> GetEstimatedRunTime = <span style="color: #a31515;">"Battery Object Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Check if the EstimatedRunTime property can be retrieved.</span><br /> <span style="color: blue;">If</span> IsNull(batteryObject.EstimatedRunTime) <span style="color: blue;">Then</span><br /> GetEstimatedRunTime = <span style="color: #a31515;">"Battery Property Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Get the numeric value from the property.</span><br /> GetEstimatedRunTime = <span style="color: blue;">CLng</span>(batteryObject.EstimatedRunTime)<br /> <br /> <span style="color: green;">'Release the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = <span style="color: blue;">Nothing</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Public</span> <span style="color: blue;">Function</span> GetTimeOnBattery() <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span><br /> <br /> <span style="color: green;">'------------------------------------------------------------------------------------------------------------------</span><br /> <span style="color: green;">'Returns the elapsed time (in seconds) since the computer system's UPS last switched to battery power or the time</span><br /> <span style="color: green;">'since the system or UPS was last restarted, whichever is less. If the battery is "online", 0 (zero) is returned.</span><br /> <span style="color: green;">'------------------------------------------------------------------------------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variable.</span><br /> <span style="color: blue;">Dim</span> batteryObject <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Get the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = GetBatteryObject()<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IsObject(batteryObject) <span style="color: blue;">Then</span><br /> GetTimeOnBattery = <span style="color: #a31515;">"Battery Object Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Check if the TimeOnBattery property can be retrieved.</span><br /> <span style="color: blue;">If</span> IsNull(batteryObject.TimeOnBattery) <span style="color: blue;">Then</span><br /> GetTimeOnBattery = <span style="color: #a31515;">"Battery Property Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Get the numeric value from the property.</span><br /> GetTimeOnBattery = <span style="color: blue;">CLng</span>(batteryObject.TimeOnBattery)<br /> <br /> <span style="color: green;">'Release the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = <span style="color: blue;">Nothing</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Public</span> <span style="color: blue;">Function</span> GetTimeToFullCharge() <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span><br /> <br /> <span style="color: green;">'-------------------------------------------------------------------------------------------------------------</span><br /> <span style="color: green;">'Returns the remaining time (in minutes) to charge the battery fully at the current charging rate and usage.</span><br /> <span style="color: green;">'A value of 45 means that the battery will be fully charged in about 45 minutes.</span><br /> <span style="color: green;">'-------------------------------------------------------------------------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variable.</span><br /> <span style="color: blue;">Dim</span> batteryObject <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Get the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = GetBatteryObject()<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IsObject(batteryObject) <span style="color: blue;">Then</span><br /> GetTimeToFullCharge = <span style="color: #a31515;">"Battery Object Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Check if the TimeToFullCharge property can be retrieved.</span><br /> <span style="color: blue;">If</span> IsNull(batteryObject.TimeToFullCharge) <span style="color: blue;">Then</span><br /> GetTimeToFullCharge = <span style="color: #a31515;">"Battery Property Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Get the numeric value from the property.</span><br /> GetTimeToFullCharge = <span style="color: blue;">CLng</span>(batteryObject.TimeToFullCharge)<br /> <br /> <span style="color: green;">'Release the battery object.</span><br /> <span style="color: blue;">Set</span> batteryObject = <span style="color: blue;">Nothing</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /></pre><br />The above code is contained in the sample workbook that you will find in the Downloads section that follows. As a bonus tip, you will also find there a "<span style="color: orange;">battery chart</span>" that shows the remaining charge of your laptop's battery.<br /><br /><br /><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;"><b>Downloads</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://goo.gl/h8BtYC"><img alt="Download" border="0" height="108" src="https://4.bp.blogspot.com/-JhKlqA-EMkM/UoVNR1_v4II/AAAAAAAABtM/Q1GE7I4707g/s1600/Download.jpg" title="Click to download the file" width="320" /></a></div><div style="text-align: justify;"><br /></div>The file can be opened with <span style="color: red;">Excel 2007</span> or newer. Please enable macros before using it.</div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-37134730090968074502018-05-20T18:36:00.000+03:002018-07-15T18:10:27.234+03:00Get External Hyperlinks From A Web Page<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;"></div>Last updated: 20/05/2018, 1 min read<br /><br /><div style="text-align: justify;"><div class="separator" style="clear: both; text-align: center;"><img alt="Get External Hyperlinks From A Webpage" border="0" data-original-height="201" data-original-width="550" src="https://4.bp.blogspot.com/-iGarY_kA4R8/WwGLgo2hL4I/AAAAAAAADtw/x-x-D-u3oOsFaoS_ia9Vgoyrnv1wUakGACLcBGAs/s1600/Get%2BExternal%2BHyperlinks%2BFrom%2BA%2BWebpage.jpg" title="Get External Hyperlinks From A Webpage" /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div>Some years ago, I published a VBA code that proved to be very popular in blog users: the code and the accompanying workbook <a href="https://www.myengineeringworld.net/2013/11/excel-vba-download-internet-files.html">could be used for downloading files</a> from the internet. One common request that I get since I published that code, <span style="color: orange;">is how to retrieve the hyperlinks from a web page</span>, so as to download the files afterwards.<br /><br />I decided to help all these blog users by creating a workbook that does exactly what they requested. So, the workbook that you will find in the downloads section can be used for grabbing all the hyperlinks from a given URL. The procedure is quite straightforward:<span style="color: red;"> just enter the URL on a specific cell and hit the Get Links button.</span> After a few seconds (depending on your internet speed) you will have all the external hyperlinks from the particular URL. Moreover, apart from the hyperlinks, <span style="color: orange;">you will also get the displayed text for each hyperlink</span>. In this way, you can match the hyperlink with its position on the web page and filter out those links that are not relevant.<br /><br /><span style="color: red;"><u>NOTE:</u></span> the code <span style="color: orange;">ignores internal hyperlinks and anchor tags</span>. In other words, the hyperlinks that are retrieved should all start with "http".<br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3 style="text-align: left;"><span style="color: #3d85c6;"><b>Demonstration video</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;">The short video below demonstrates the usage of the spreadsheet using as an example the URL of this blog. In the particular case, 43 hyperlinks retrieved.</div><div style="text-align: center;"><br /></div><div style="text-align: center;"><iframe allowfullscreen="" frameborder="0" height="338" src="//www.youtube.com/embed/kj47Y-fMW5E" width="600"></iframe></div><br /><br /><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;"><b>Downloads</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://goo.gl/JCjgj5"><img alt="Download" border="0" height="108" src="https://4.bp.blogspot.com/-JhKlqA-EMkM/UoVNR1_v4II/AAAAAAAABtM/Q1GE7I4707g/s1600/Download.jpg" title="Click to download the file" width="320" /></a></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">The file can be opened with <span style="color: red;">Excel 2007</span> or newer. Please enable macros before using it. The VBA code was protected using the <a href="https://sites.fastspring.com/esotericconsulting/instant/unviewablevbalicenseoptions?source=engineeringworld">Unviewable+</a>.</div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Read also</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><a href="https://www.myengineeringworld.net/2013/11/excel-vba-download-internet-files.html">Excel & VBA: Download Internet Files Automatically</a> </div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-20850810591731482242018-04-30T23:58:00.000+03:002018-05-04T18:00:32.977+03:00Open A Password-Protected PDF File With VBA<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;"></div>Last updated: 03/05/2018, 2 min read (without the code)<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Open A Password-Protected PDF File With VBA" border="0" data-original-height="405" data-original-width="550" src="https://2.bp.blogspot.com/-VXY3_qWSIgQ/WusrLKhDLjI/AAAAAAAADtI/vOX7pm2OiscpkAkjHvu7DqjAsXZKK_LVACLcBGAs/s1600/Open%2BA%2BPassword-Protected%2BPDF%2BFile%2BWith%2BVBA.jpg" title="Open A Password-Protected PDF File With VBA" /></div><div style="text-align: justify;"><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div>In the past, we have seen two ways to open PDF files with VBA: <a href="https://www.myengineeringworld.net/2012/07/vba-macro-to-open-pdf-file.html">the first one</a> involved the usage of the Adobe Object Model and it could be only used with Adobe Professional. The <a href="https://www.myengineeringworld.net/2013/04/open-pdf-file-with-vba.html">second one</a> was more generic; it was taking advantage of the Windows API functions and it could be used by both Adobe Reader and Professional. Both ways worked and continue to work just fine. There is a problem, though: <span style="color: red;">what will happen if the PDF file you want to open is password-protected?</span> Is there a way to fill the password in the open dialog and continue opening the file?<br /><br />The answer is, yes! <span style="color: orange;">The suggested solution relies on several Windows APIs since the Adobe Object Model does not provide an option/method for including the password when opening a PDF file.</span> Note: <span style="color: red;">the VBA code that you will see below is NOT a password cracking piece of code!</span> The code implies that you know the password of the file. It just automates the opening procedure, especially if you have to open multiple PDF files.<br /><br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Spy++ For Opening Locked PDF File" border="0" data-original-height="202" data-original-width="550" src="https://3.bp.blogspot.com/-kWAJ-RPwu7M/WustbbiXPxI/AAAAAAAADtU/KiXE4_T_K0UubpKRpfkjmOFSiJIJJQOSwCLcBGAs/s1600/Spy%252B%252B%2BFor%2BOpening%2BLocked%2BPDF%2BFile.jpg" title="Spy++ For Opening Locked PDF File" /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Main idea</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div>The main idea behind the code below can be divided into four steps:<br /><ol><li>First of all, the code checks if the given path is valid (e.g. the file exists). Then, the <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx">ShellExecute</a> API is used to open the file using the associated default program (either Adobe Reader or Adobe Professional).</li><li>The <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms633499(v=vs.85).aspx">FindWindow</a> API function is used to find the pop-up window that prompts the user to fill the password.</li><li>By using the <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms633500(v=vs.85).aspx">FindWindowEx</a> API function, the code searches the subsequent child windows until the text box is reached (using the class RICHEDIT50W).</li><li>Finally, the <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms644950(v=vs.85).aspx">SendMessage</a> and <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms644944(v=vs.85).aspx">PostMessage</a> API functions are invoked in order to fill the known password and hit the Open button.</li></ol>Similar to many previous posts, the <a href="https://goo.gl/Ld3p8W">Spy++</a> software was used to specify the windows hierarchy when opening the password-protected file. The above image shows the window tree that should be navigated in order to find the text box that will receive the password.<br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>VBA code</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br />Below you will find the <span style="color: red;">OpenLockedPdf</span> macro that does the main work, the <span style="color: orange;">FileExists</span> function that is responsible for testing the file existence, as well as the <span style="color: orange;">SamplePdfTest</span> macro that performs a sample test. Note that:<br /><ul><li><span style="color: red;">The OpenLockedPdf macro can be used to open a PDF file that is NOT password-protected.</span> In that case, the code that is right after the line of ShellExecute is ignored.</li><li><span style="color: orange;">The macro can be used in every Office application, as well as in AutoCAD.</span> It works with both 32 and 64-bit applications.</li></ul><ul></ul></div><br /><pre><span style="color: blue;">Option Explicit</span><br /><br /><span style="color: green;">'Declaring the necessary API functions for both 64 and 32 bit applications.</span><br /><span style="color: blue;">#If VBA7 And Win64 Then</span><br /> <br /> <span style="color: green;">'For 64 bit applications.</span><br /> <span style="color: green;">'Performs an operation on a specified file.</span><br /> <span style="color: blue;">Public</span> <span style="color: blue;">Declare</span> PtrSafe <span style="color: blue;">Function</span> ShellExecute <span style="color: blue;">Lib</span> <span style="color: #a31515;">"shell32.dll"</span> <span style="color: blue;">Alias</span> <span style="color: #a31515;">"ShellExecuteA"</span> _<br /> (<span style="color: blue;">ByVal</span> hwnd <span style="color: blue;">As</span> LongPtr, _<br /> <span style="color: blue;">ByVal</span> lpOperation <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, _<br /> <span style="color: blue;">ByVal</span> lpFile <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, _<br /> <span style="color: blue;">ByVal</span> lpParameters <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, _<br /> <span style="color: blue;">ByVal</span> lpDirectory <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, _<br /> <span style="color: blue;">ByVal</span> nShowCmd <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>) <span style="color: blue;">As</span> LongPtr<br /> <br /> <span style="color: green;">'Retrieves a handle to the top-level window whose class name and window name match the specified strings.</span><br /> <span style="color: green;">'This function does not search child windows. This function does not perform a case-sensitive search.</span><br /> <span style="color: blue;">Public</span> <span style="color: blue;">Declare</span> PtrSafe <span style="color: blue;">Function</span> FindWindow <span style="color: blue;">Lib</span> <span style="color: #a31515;">"user32"</span> <span style="color: blue;">Alias</span> <span style="color: #a31515;">"FindWindowA"</span> _<br /> (<span style="color: blue;">ByVal</span> lpClassName <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, _<br /> <span style="color: blue;">ByVal</span> lpWindowName <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> LongPtr<br /> <br /> <span style="color: green;">'Retrieves a handle to a window whose class name and window name match the specified strings.</span><br /> <span style="color: green;">'The function searches child windows, beginning with the one following the specified child window.</span><br /> <span style="color: green;">'This function does not perform a case-sensitive search.</span><br /> <span style="color: blue;">Public</span> <span style="color: blue;">Declare</span> PtrSafe <span style="color: blue;">Function</span> FindWindowEx <span style="color: blue;">Lib</span> <span style="color: #a31515;">"user32"</span> <span style="color: blue;">Alias</span> <span style="color: #a31515;">"FindWindowExA"</span> _<br /> (<span style="color: blue;">ByVal</span> hWnd1 <span style="color: blue;">As</span> LongPtr, _<br /> <span style="color: blue;">ByVal</span> hWnd2 <span style="color: blue;">As</span> LongPtr, _<br /> <span style="color: blue;">ByVal</span> lpClassName <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, _<br /> <span style="color: blue;">ByVal</span> lpWindowName <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> LongPtr<br /> <br /> <span style="color: green;">'Sends the specified message to a window or windows. The SendMessage function calls the window procedure</span><br /> <span style="color: green;">'for the specified window and does not parentWindowurn until the window procedure has processed the message.</span><br /> <span style="color: blue;">Public</span> <span style="color: blue;">Declare</span> PtrSafe <span style="color: blue;">Function</span> SendMessage <span style="color: blue;">Lib</span> <span style="color: #a31515;">"user32"</span> <span style="color: blue;">Alias</span> <span style="color: #a31515;">"SendMessageA"</span> _<br /> (<span style="color: blue;">ByVal</span> hwnd <span style="color: blue;">As</span> LongPtr, _<br /> <span style="color: blue;">ByVal</span> wMsg <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>, _<br /> <span style="color: blue;">ByVal</span> wParam <span style="color: blue;">As</span> LongPtr, _<br /> lParam <span style="color: blue;">As</span> Any) <span style="color: blue;">As</span> LongPtr<br /> <br /> <span style="color: green;">'Places (posts) a message in the message queue associated with the thread that created the specified</span><br /> <span style="color: green;">'window and parentWindowurns without waiting for the thread to process the message.</span><br /> <span style="color: blue;">Public</span> <span style="color: blue;">Declare</span> PtrSafe <span style="color: blue;">Function</span> PostMessage <span style="color: blue;">Lib</span> <span style="color: #a31515;">"user32"</span> <span style="color: blue;">Alias</span> <span style="color: #a31515;">"PostMessageA"</span> _<br /> (<span style="color: blue;">ByVal</span> hwnd <span style="color: blue;">As</span> LongPtr, _<br /> <span style="color: blue;">ByVal</span> wMsg <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>, _<br /> <span style="color: blue;">ByVal</span> wParam <span style="color: blue;">As</span> LongPtr, _<br /> <span style="color: blue;">ByVal</span> lParam <span style="color: blue;">As</span> LongPtr) <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span><br /> <br /> <br />#<span style="color: blue;">Else</span><br /><br /> <span style="color: green;">'For 32 bit applications.</span><br /> <span style="color: blue;">Public</span> <span style="color: blue;">Declare</span> <span style="color: blue;">Function</span> ShellExecute <span style="color: blue;">Lib</span> <span style="color: #a31515;">"shell32.dll"</span> <span style="color: blue;">Alias</span> <span style="color: #a31515;">"ShellExecuteA"</span> _<br /> (<span style="color: blue;">ByVal</span> hwnd <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>, _<br /> <span style="color: blue;">ByVal</span> lpOperation <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, _<br /> <span style="color: blue;">ByVal</span> lpFile <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, _<br /> <span style="color: blue;">ByVal</span> lpParameters <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, _<br /> <span style="color: blue;">ByVal</span> lpDirectory <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, _<br /> <span style="color: blue;">ByVal</span> nShowCmd <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span><br /> <br /> <span style="color: blue;">Public</span> <span style="color: blue;">Declare</span> <span style="color: blue;">Function</span> FindWindow <span style="color: blue;">Lib</span> <span style="color: #a31515;">"user32"</span> <span style="color: blue;">Alias</span> <span style="color: #a31515;">"FindWindowA"</span> _<br /> (<span style="color: blue;">ByVal</span> lpClassName <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, _<br /> <span style="color: blue;">ByVal</span> lpWindowName <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span><br /><br /> <span style="color: blue;">Public</span> <span style="color: blue;">Declare</span> <span style="color: blue;">Function</span> FindWindowEx <span style="color: blue;">Lib</span> <span style="color: #a31515;">"user32"</span> <span style="color: blue;">Alias</span> <span style="color: #a31515;">"FindWindowExA"</span> _<br /> (<span style="color: blue;">ByVal</span> hWnd1 <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>, _<br /> <span style="color: blue;">ByVal</span> hWnd2 <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>, _<br /> <span style="color: blue;">ByVal</span> lpClassName <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, _<br /> <span style="color: blue;">ByVal</span> lpWindowName <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span><br /><br /> <span style="color: blue;">Public</span> <span style="color: blue;">Declare</span> <span style="color: blue;">Function</span> SendMessage <span style="color: blue;">Lib</span> <span style="color: #a31515;">"user32"</span> <span style="color: blue;">Alias</span> <span style="color: #a31515;">"SendMessageA"</span> _<br /> (<span style="color: blue;">ByVal</span> hwnd <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>, _<br /> <span style="color: blue;">ByVal</span> wMsg <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>, _<br /> <span style="color: blue;">ByVal</span> wParam <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>, _<br /> lParam <span style="color: blue;">As</span> Any) <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span><br /><br /> <span style="color: blue;">Public</span> <span style="color: blue;">Declare</span> <span style="color: blue;">Function</span> PostMessage <span style="color: blue;">Lib</span> <span style="color: #a31515;">"user32.dll"</span> <span style="color: blue;">Alias</span> <span style="color: #a31515;">"PostMessageA"</span> _<br /> (<span style="color: blue;">ByVal</span> hwnd <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>, _<br /> <span style="color: blue;">ByVal</span> wMsg <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>, _<br /> <span style="color: blue;">ByVal</span> wParam <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>, _<br /> <span style="color: blue;">ByVal</span> lParam <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span><br /> <br /><span style="color: blue;">#End If</span><br /><br /><span style="color: green;">'Constants used in API functions.</span><br /><span style="color: blue;">Public</span> <span style="color: blue;">Const</span> SW_HIDE <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span> = 0<br /><span style="color: blue;">Public</span> <span style="color: blue;">Const</span> SW_SHOWNORMAL <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span> = 1<br /><span style="color: blue;">Public</span> <span style="color: blue;">Const</span> SW_SHOWMAXIMIZED <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span> = 3<br /><span style="color: blue;">Public</span> <span style="color: blue;">Const</span> SW_SHOWMINIMIZED <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span> = 2<br /><span style="color: blue;">Public</span> <span style="color: blue;">Const</span> WM_SETTEXT = &HC<br /><span style="color: blue;">Public</span> <span style="color: blue;">Const</span> VK_RETURN = &HD<br /><span style="color: blue;">Public</span> <span style="color: blue;">Const</span> WM_KEYDOWN = &H100<br /><br /><span style="color: blue;">Public</span> <span style="color: blue;">Sub</span> OpenLockedPdf(pdfPath <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>, password <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>)<br /> <br /> <span style="color: green;">'------------------------------------------------------------------------</span><br /> <span style="color: green;">'Opens a password-protected PDF file, given its (known) password.</span><br /> <span style="color: green;">'API functions are used to find the pop-up window and fill the password.</span><br /> <span style="color: green;">'</span><br /> <span style="color: green;">'The subroutine can be used in every Office application, as well as</span><br /> <span style="color: green;">'in AutoCAD. It works for both 32 and 64 bit applications.</span><br /> <br /> <span style="color: green;">'The macro also works with PDF files that are NOT password-protected.</span><br /> <span style="color: green;">'In that case, the code after the line of ShellExecute is ignored.</span><br /> <br /> <span style="color: green;">'Written By: Christos Samaras</span><br /> <span style="color: green;">'Date: 30/04/2018</span><br /> <span style="color: green;">'E-mail: xristos.samaras@gmail.com</span><br /> <span style="color: green;">'Site: http://www.myengineeringworld.net</span><br /> <span style="color: green;">'------------------------------------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables (different for 32 or 64 bit applications).</span><br /> <span style="color: blue;">#If VBA7 And Win64 Then</span><br /> <span style="color: blue;">Dim</span> parentWindow <span style="color: blue;">As</span> LongPtr<br /> <span style="color: blue;">Dim</span> firstChildWindow <span style="color: blue;">As</span> LongPtr<br /> <span style="color: blue;">Dim</span> secondChildFirstWindow <span style="color: blue;">As</span> LongPtr<br /> #<span style="color: blue;">Else</span><br /> <span style="color: blue;">Dim</span> parentWindow <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span><br /> <span style="color: blue;">Dim</span> firstChildWindow <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span><br /> <span style="color: blue;">Dim</span> secondChildFirstWindow <span style="color: blue;">As</span> <span style="color: #2b91af;">Long</span><br /> <span style="color: blue;">#End If</span><br /> <span style="color: blue;">Dim</span> timeCount <span style="color: blue;">As</span> <span style="color: #2b91af;">Date</span><br /><br /> <span style="color: green;">'Check if the PDF file exists.</span><br /> <span style="color: blue;">If</span> FileExists(pdfPath) = <span style="color: blue;">False</span> <span style="color: blue;">Then</span><br /> MsgBox <span style="color: #a31515;">"The PDF file doesn't exist!"</span>, vbCritical, <span style="color: #a31515;">"Error in PDF path"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'The ShellExecute API will try to open the PDF file using the default application that</span><br /> <span style="color: green;">'is associated with PDF files (either Adobe Reader or Professional).</span><br /> ShellExecute Application.hwnd, <span style="color: #a31515;">"Open"</span>, pdfPath, vbNullString, <span style="color: #a31515;">"C:\"</span>, SW_SHOWNORMAL<br /> <br /> <span style="color: green;">'Note: The code below will be ignored if the PDF file has no protection.</span><br /> <br /> <span style="color: green;">'Find the handle of the pop-up window.</span><br /> timeCount = Now()<br /> <span style="color: blue;">Do</span> Until Now() > timeCount + TimeValue(<span style="color: #a31515;">"00:00:05"</span>)<br /> parentWindow = 0<br /> DoEvents<br /> parentWindow = FindWindow(<span style="color: #a31515;">"#32770"</span>, <span style="color: #a31515;">"Password"</span>)<br /> <span style="color: blue;">If</span> parentWindow <> 0 <span style="color: blue;">Then</span> <span style="color: blue;">Exit</span> <span style="color: blue;">Do</span><br /> <span style="color: blue;">Loop</span><br /> <br /> <span style="color: blue;">If</span> parentWindow <> 0 <span style="color: blue;">Then</span><br /> <br /> <span style="color: green;">'Find the handle of the first child window (it is a group box).</span><br /> timeCount = Now()<br /> <span style="color: blue;">Do</span> Until Now() > timeCount + TimeValue(<span style="color: #a31515;">"00:00:05"</span>)<br /> firstChildWindow = 0<br /> DoEvents<br /> firstChildWindow = FindWindowEx(parentWindow, <span style="color: blue;">ByVal</span> 0&, <span style="color: #a31515;">"GroupBox"</span>, vbNullString)<br /> <span style="color: blue;">If</span> firstChildWindow <> 0 <span style="color: blue;">Then</span> <span style="color: blue;">Exit</span> <span style="color: blue;">Do</span><br /> <span style="color: blue;">Loop</span><br /><br /> <span style="color: green;">'Find the handle of the subsequent child window (it is the text box for filling the password).</span><br /> <span style="color: blue;">If</span> firstChildWindow <> 0 <span style="color: blue;">Then</span><br /> timeCount = Now()<br /> <span style="color: blue;">Do</span> Until Now() > timeCount + TimeValue(<span style="color: #a31515;">"00:00:05"</span>)<br /> secondChildFirstWindow = 0<br /> DoEvents<br /> secondChildFirstWindow = FindWindowEx(firstChildWindow, <span style="color: blue;">ByVal</span> 0&, <span style="color: #a31515;">"RICHEDIT50W"</span>, vbNullString)<br /> <span style="color: blue;">If</span> secondChildFirstWindow <> 0 <span style="color: blue;">Then</span> <span style="color: blue;">Exit</span> <span style="color: blue;">Do</span><br /> <span style="color: blue;">Loop</span><br /> <br /> <span style="color: green;">'The handle was found, so...</span><br /> <span style="color: blue;">If</span> secondChildFirstWindow <> 0 <span style="color: blue;">Then</span><br /> <br /> <span style="color: green;">'Fill the password in the text box.</span><br /> SendMessage secondChildFirstWindow, WM_SETTEXT, 0&, <span style="color: blue;">ByVal</span> password<br /> <br /> <span style="color: green;">'Press the OK button (it is the default action, so no need to find the handle of the button).</span><br /> PostMessage secondChildFirstWindow, WM_KEYDOWN, VK_RETURN, 0<br /><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Sub</span><br /><br /><span style="color: blue;">Function</span> FileExists(FilePath <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">Boolean</span><br /> <br /> <span style="color: green;">'--------------------------------------------------</span><br /> <span style="color: green;">'Checks if a file exists (using the Dir function).</span><br /> <span style="color: green;">'--------------------------------------------------</span><br /><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <span style="color: blue;">If</span> Len(FilePath) > 0 <span style="color: blue;">Then</span><br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> Dir(FilePath, vbDirectory) = vbNullString <span style="color: blue;">Then</span> FileExists = <span style="color: blue;">True</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Sub</span> SamplePdfTest()<br /> <br /> <span style="color: green;">'Full path example that can be used in every Office application, as well as with AutoCAD:</span><br /> <span style="color: green;">'OpenLockedPdf "C:\Users\Christos\Desktop\Locked File.pdf", "Newsletter"</span><br /> <br /> <span style="color: green;">'Relative path example (in Excel):</span><br /> <span style="color: green;">'OpenLockedPdf ThisWorkbook.Path & "\" & "Locked File.pdf", "Newsletter"</span><br /> <br /> <span style="color: green;">'For this example only (with the button):</span><br /> OpenLockedPdf ThisWorkbook.Sheets(<span style="color: #a31515;">"Open Locked Sample File"</span>).Range(<span style="color: #a31515;">"C3"</span>), ThisWorkbook.Sheets(<span style="color: #a31515;">"Open Locked Sample File"</span>).Range(<span style="color: #a31515;">"C5"</span>)<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Sub</span><br /></pre><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3 style="text-align: left;"><span style="color: #3d85c6;"><b>Demonstration video</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;">The video that follows demonstrates the result of the "OpenLockedPdf" macro, as well as gives more information on the usage of Spy++.</div><div style="text-align: center;"><br /></div><div style="text-align: center;"><iframe allowfullscreen="" frameborder="0" height="338" src="//www.youtube.com/embed/PUUX6peE11s" width="600"></iframe></div><br /><br /><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;"><b>Downloads</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://goo.gl/CF2exq"><img alt="Download" border="0" height="108" src="https://4.bp.blogspot.com/-JhKlqA-EMkM/UoVNR1_v4II/AAAAAAAABtM/Q1GE7I4707g/s1600/Download.jpg" title="Click to download the file" width="320" /></a></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">The <span style="color: red;">zip file </span>contains a sample PDF file that is locked with a password, as well as a sample workbook containing the code presented above that can be used to open that file (the password is included). The workbook can be opened with Excel 2007 or newer. Please enable macros before using it. Finally, there is also a VBA module with the code to attach it in any Office or AutoCAD application you might have.</div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Read also</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><a href="https://www.myengineeringworld.net/2012/07/vba-macro-to-open-pdf-file.html">VBA Macro To Open A PDF File</a><br /><a href="https://www.myengineeringworld.net/2013/04/open-pdf-file-with-vba.html">Open PDF File With VBA</a> </div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-90244366742292038942018-03-21T00:09:00.000+02:002018-08-09T12:50:07.076+03:00Reverse Geocoding Using VBA & Google API<div dir="ltr" style="text-align: left;" trbidi="on">Last updated: 09/08/2018, 1 min read (without the code)<br /><br /><div style="text-align: justify;"><div class="separator" style="clear: both; text-align: center;"><img alt="Reverse Geocoding Using VBA & Google API" border="0" data-original-height="212" data-original-width="550" src="https://3.bp.blogspot.com/-5zoGIo9WjbY/WrF8lzwpkCI/AAAAAAAADr0/j6UdmRfyLTcnGsCr9bple3j6rYncrfLPgCLcBGAs/s1600/Reverse%2BGeocoding%2BUsing%2BVBA%2B%2526%2BGoogle%2BAPI.jpg" title="Reverse Geocoding Using VBA & Google API" /></div><br /></div><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div><div style="text-align: justify;">After updating the existing VBA functions that dealt with Google APIs, it is time to introduce a new function that can be used for Reverse Geocoding, a short definition of which, based on <a href="https://en.wikipedia.org/wiki/Reverse_geocoding">Wikipedia</a>, is the following: “<i>Reverse geocoding is the process of back (reverse) coding of a point location (latitude, longitude) to a readable address or place name. This permits the identification of nearby street addresses, places, and/or areal subdivisions such as neighbourhoods, county, state, or country</i>”.<br /><br /><span style="color: red;">The developed VBA function can be utilized directly from Excel, as long as the user provides a valid pair of latitude and longitude.</span> The <span style="color: orange;">GetAddress</span> function sends a request to the Google server and, then, uses its XML response to read the appropriate information (the formatted address in particular). If you are curious how the server response looks like, see the picture below, where the main nodes are highlighted in red boxes. This function does the exact opposite of the <a href="https://www.myengineeringworld.net/2014/06/geocoding-using-vba-google-api.html">GetCoordinates function</a> that was written a few years ago.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Reverse Geocoding Response" border="0" data-original-height="386" data-original-width="550" src="https://4.bp.blogspot.com/-jI06zx8p8W4/WrIdhRej6pI/AAAAAAAADsE/S3fSiUXt_EYaCEM3_IU0UtuDKKgKE7J7ACLcBGAs/s1600/Reverse%2BGeocoding%2BResponse.jpg" title="Reverse Geocoding Response" /></div><br />Apart from latitude and longitude, the function incorporates an optional third parameter (ResultTypeFilter). This parameter acts as a filter, by reducing/filtering the results returned from the server. In the code comments, you will find the values that are supported.</div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>VBA code</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;">Below you will find the VBA code for the GetAddress function. <span style="color: red;"><u>Bear in mind that the use of the Google Geocoding API is subject to a limit of 40,000 requests per month, so be careful not to exceed this limit.</u></span> To use this VBA function you will need a valid API key. Check this <a href="https://www.myengineeringworld.net/2018/02/how-to-get-free-google-api-key.html">link</a> that presents a step-by-step guide on how to acquire one for free.<br /><br /><pre><span style="color: blue;">Option Explicit</span><br /><br /><span style="color: blue;">Function</span> GetAddress(Latitude <span style="color: blue;">As</span> <span style="color: #2b91af;">Double</span>, Longitude <span style="color: blue;">As</span> <span style="color: #2b91af;">Double</span>, <span style="color: blue;">Optional</span> ResultTypeFilter <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span><br /> <br /> <span style="color: green;">'-----------------------------------------------------------------------------------------------------------------------</span><br /> <span style="color: green;">'This function returns the address of a given latitude, longitude pair using the Google (Reverse) Geocoding API.</span><br /> <br /> <span style="color: green;">'The optional paramter ResultTypeFilter is a filter of one or more address types, separated by a pipe (|).</span><br /> <span style="color: green;">'If the parameter contains multiple address types, the API returns all addresses that match any of the types.</span><br /> <span style="color: green;">'A note about processing: The result_type parameter does not restrict the search to the specified address type(s).</span><br /> <span style="color: green;">'Rather, the result_type acts as a post-search filter: the API fetches all results for the specified latlng,</span><br /> <span style="color: green;">'then discards those results that do not match the specified address type(s).</span><br /> <br /> <span style="color: green;">'The following values are supported:</span><br /> <span style="color: green;">'street_address: indicates a precise street address.</span><br /> <span style="color: green;">'route: indicates a named route (such as "US 101").</span><br /> <span style="color: green;">'intersection: indicates a major intersection, usually of two major roads.</span><br /> <span style="color: green;">'political: indicates a political entity. Usually, this type indicates a polygon of some civil administration.</span><br /> <span style="color: green;">'country: indicates the national political entity, and is typically the highest order type returned by the Geocoder.</span><br /> <span style="color: green;">'administrative_area_level_1: indicates a first-order civil entity below the country level. Within the United States,</span><br /> <span style="color: green;">'these administrative levels are states. Not all nations exhibit these administrative levels.</span><br /> <span style="color: green;">'In most cases, administrative_area_level_1 short names will closely match ISO 3166-2 subdivisions and other</span><br /> <span style="color: green;">'widely circulated lists; however this is not guaranteed as our geocoding results are based on a variety of</span><br /> <span style="color: green;">'signals and location data.</span><br /> <span style="color: green;">'administrative_area_level_2: indicates a second-order civil entity below the country level. Within the United States,</span><br /> <span style="color: green;">'these administrative levels are counties. Not all nations exhibit these administrative levels.</span><br /> <span style="color: green;">'administrative_area_level_3: indicates a third-order civil entity below the country level.</span><br /> <span style="color: green;">'This type indicates a minor civil division. Not all nations exhibit these administrative levels.</span><br /> <span style="color: green;">'administrative_area_level_4: indicates a fourth-order civil entity below the country level.</span><br /> <span style="color: green;">'This type indicates a minor civil division. Not all nations exhibit these administrative levels.</span><br /> <span style="color: green;">'administrative_area_level_5: indicates a fifth-order civil entity below the country level.</span><br /> <span style="color: green;">'This type indicates a minor civil division. Not all nations exhibit these administrative levels.</span><br /> <span style="color: green;">'colloquial_area" indicates a commonly-used alternative name for the entity.</span><br /> <span style="color: green;">'locality: indicates an incorporated city or town political entity.</span><br /> <span style="color: green;">'ward: indicates a specific type of Japanese locality, to facilitate distinction between multiple</span><br /> <span style="color: green;">'locality components within a Japanese address.</span><br /> <span style="color: green;">'sublocality: indicates a first-order civil entity below a locality. For some locations may receive one of the</span><br /> <span style="color: green;">'additional types: sublocality_level_1 to sublocality_level_5. Each sublocality level is a civil entity.</span><br /> <span style="color: green;">'Larger numbers indicate a smaller geographic area.</span><br /> <span style="color: green;">'neighborhood: indicates a named neighborhood</span><br /> <span style="color: green;">'premise: indicates a named location, usually a building or collection of buildings with a common name</span><br /> <span style="color: green;">'subpremise: indicates a first-order entity below a named location, usually a singular building within</span><br /> <span style="color: green;">'a collection of buildings with a common name</span><br /> <span style="color: green;">'postal_code: indicates a postal code as used to address postal mail within the country.</span><br /> <span style="color: green;">'natural_feature: indicates a prominent natural feature.</span><br /> <span style="color: green;">'airport: indicates an airport.</span><br /> <span style="color: green;">'park: indicates a named park.</span><br /> <span style="color: green;">'point_of_interest: indicates a named point of interest. Typically, these "POI"s are prominent local entities that</span><br /> <span style="color: green;">'don't easily fit in another category, such as "Empire State Building" or "Statue of Liberty."</span><br /> <br /> <span style="color: green;">'The function ignores parameters such as language and location_type and returns always the FIRST RESULT.</span><br /> <br /> <span style="color: green;">'NOTE: As Google points out, the use of the Google Geocoding API is subject to a limit of 40,000</span><br /> <span style="color: green;">'requests per month, so be careful not to exceed this limit. For more info check:</span><br /> <span style="color: green;">'https://cloud.google.com/maps-platform/pricing/sheet</span><br /> <br /> <span style="color: green;">'In order to use this function you must enable the XML, v3.0 library from VBA editor:</span><br /> <span style="color: green;">'Go to Tools -> References -> check the Microsoft XML, v3.0.</span><br /> <br /> <span style="color: green;">'Moreover, to use this function you will also need a valid API key.</span><br /> <span style="color: green;">'Check the next link that guides you on how to acquire a free API key:</span><br /> <span style="color: green;">'https://www.myengineeringworld.net/2018/02/how-to-get-free-google-api-key.html</span><br /> <br /> <span style="color: green;">'Written By: Christos Samaras</span><br /> <span style="color: green;">'Date: 17/03/2018</span><br /> <span style="color: green;">'Last Updated: 09/08/2018</span><br /> <span style="color: green;">'E-mail: xristos.samaras@gmail.com</span><br /> <span style="color: green;">'Site: https://www.myengineeringworld.net</span><br /> <span style="color: green;">'-----------------------------------------------------------------------------------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables. Using 30 at the first two variables because it</span><br /> <span style="color: green;">'corresponds to the "Microsoft XML, v3.0" library in VBA (msxml3.dll).</span><br /> <span style="color: blue;">Dim</span> ApiKey <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span><br /> <span style="color: blue;">Dim</span> Request <span style="color: blue;">As</span> <span style="color: blue;">New</span> XMLHTTP30<br /> <span style="color: blue;">Dim</span> Results <span style="color: blue;">As</span> <span style="color: blue;">New</span> DOMDocument30<br /> <span style="color: blue;">Dim</span> StatusNode <span style="color: blue;">As</span> IXMLDOMNode<br /> <span style="color: blue;">Dim</span> AddressNode <span style="color: blue;">As</span> IXMLDOMNode<br /><br /> <span style="color: green;">'Set your API key in this variable. Check this link for more info:</span><br /> <span style="color: green;">'https://www.myengineeringworld.net/2018/02/how-to-get-free-google-api-key.html</span><br /> ApiKey = <span style="color: #a31515;">"Your API Key goes here!"</span><br /> <br /> <span style="color: green;">'Check that an API key has been provided.</span><br /> <span style="color: blue;">If</span> ApiKey = vbNullString <span style="color: blue;">Or</span> ApiKey = <span style="color: #a31515;">"Your API Key goes here!"</span> <span style="color: blue;">Then</span><br /> GetAddress = <span style="color: #a31515;">"Invalid API Key"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Check the input variables:</span><br /> <span style="color: green;">'The valid range of latitude in degrees is -90 and +90 for the Southern and Northern hemisphere respectively.</span><br /> <span style="color: blue;">If</span> Latitude < -90 <span style="color: blue;">Or</span> Latitude > 90 <span style="color: blue;">Then</span><br /> GetAddress = <span style="color: #a31515;">"Invalid Latitude value"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /><br /> <span style="color: green;">'Longitude is in the range -180 and +180 specifying coordinates West and East of the Prime Meridian, respectively.</span><br /> <span style="color: blue;">If</span> Longitude < -180 <span style="color: blue;">Or</span> Longitude > 180 <span style="color: blue;">Then</span><br /> GetAddress = <span style="color: #a31515;">"Invalid Longitude value"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Generic error handling.</span><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> errorHandler<br /> <br /> <span style="color: green;">'Create the request based on Google's (Reverse) Geocoding API. Parameters:</span><br /> <span style="color: green;">'- latlng: The latitude and longitude values specifying the location for which you wish to obtain the closest, human-readable address.</span><br /> <span style="color: green;">'- key: Your application's API key. This key identifies your application for purposes of quota management.</span><br /> <span style="color: green;">'Differentiate the request if a filter is provided.</span><br /> <span style="color: blue;">If</span> ResultTypeFilter = vbNullString <span style="color: blue;">Then</span><br /> Request.Open <span style="color: #a31515;">"GET"</span>, <span style="color: #a31515;">"https://maps.googleapis.com/maps/api/geocode/xml?"</span> _<br /> & <span style="color: #a31515;">"latlng="</span> & Latitude & <span style="color: #a31515;">","</span> & Longitude & <span style="color: #a31515;">"&key="</span> & ApiKey, <span style="color: blue;">False</span><br /> <span style="color: blue;">Else</span><br /> Request.Open <span style="color: #a31515;">"GET"</span>, <span style="color: #a31515;">"https://maps.googleapis.com/maps/api/geocode/xml?"</span> _<br /> & <span style="color: #a31515;">"latlng="</span> & Latitude & <span style="color: #a31515;">","</span> & Longitude & <span style="color: #a31515;">"&result_type="</span> & ResultTypeFilter & <span style="color: #a31515;">"&key="</span> & ApiKey, <span style="color: blue;">False</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <span style="color: green;">'Send the request to the Google server.</span><br /> Request.send<br /> <br /> <span style="color: green;">'Read the results from the request.</span><br /> Results.LoadXML Request.responseText<br /> <br /> <span style="color: green;">'Get the status node value.</span><br /> <span style="color: blue;">Set</span> StatusNode = Results.SelectSingleNode(<span style="color: #a31515;">"//status"</span>)<br /> <br /> <span style="color: green;">'Based on the status node result, proceed accordingly.</span><br /> <span style="color: blue;">Select</span> <span style="color: blue;">Case</span> UCase(StatusNode.Text)<br /> <br /> <span style="color: blue;">Case</span> <span style="color: #a31515;">"OK"</span> <span style="color: green;">'The API request was successful. At least one result was returned.</span><br /> <br /> <span style="color: green;">'Get the formatted address of the first result.</span><br /> <span style="color: blue;">Set</span> AddressNode = Results.SelectSingleNode(<span style="color: #a31515;">"//result/formatted_address"</span>)<br /> <br /> <span style="color: green;">'Return the address.</span><br /> GetAddress = AddressNode.Text<br /> <br /> <span style="color: blue;">Case</span> <span style="color: #a31515;">"ZERO_RESULTS"</span> <span style="color: green;">'The geocode was successful but returned no results.</span><br /> GetAddress = <span style="color: #a31515;">"The address probably not exists"</span><br /> <br /> <span style="color: blue;">Case</span> <span style="color: #a31515;">"OVER_QUERY_LIMIT"</span> <span style="color: green;">'The requestor has exceeded the limit of 2500 request/day.</span><br /> GetAddress = <span style="color: #a31515;">"Requestor has exceeded the server limit"</span><br /> <br /> <span style="color: blue;">Case</span> <span style="color: #a31515;">"REQUEST_DENIED"</span> <span style="color: green;">'The API did not complete the request.</span><br /> GetAddress = <span style="color: #a31515;">"Server denied the request"</span><br /> <br /> <span style="color: blue;">Case</span> <span style="color: #a31515;">"INVALID_REQUEST"</span> <span style="color: green;">'The API request is empty or is malformed.</span><br /> GetAddress = <span style="color: #a31515;">"Request was empty or malformed"</span><br /> <br /> <span style="color: blue;">Case</span> <span style="color: #a31515;">"UNKNOWN_ERROR"</span> <span style="color: green;">'Indicates that the request could not be processed due to a server error.</span><br /> GetAddress = <span style="color: #a31515;">"Unknown error"</span><br /> <br /> <span style="color: blue;">Case</span> <span style="color: blue;">Else</span> <span style="color: green;">'Just in case...</span><br /> GetAddress = <span style="color: #a31515;">"Error"</span><br /> <br /> <span style="color: blue;">End</span> <span style="color: blue;">Select</span><br /> <br /> <span style="color: green;">'Release the objects.</span><br />errorHandler:<br /> <span style="color: blue;">Set</span> AddressNode = <span style="color: blue;">Nothing</span><br /> <span style="color: blue;">Set</span> StatusNode = <span style="color: blue;">Nothing</span><br /> <span style="color: blue;">Set</span> Results = <span style="color: blue;">Nothing</span><br /> <span style="color: blue;">Set</span> Request = <span style="color: blue;">Nothing</span><br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /></pre><br /><span style="color: red;"><u>NOTE:</u></span> In case of multiple results, the function returns the <span style="color: red;">first result</span>, so be careful with your inputs. Tip: use the ResultTypeFilter parameter to reduce the results that are returned. For anyone who is interested to learn how the Google (Reverse) Geocoding API works, he/she can visit the <a href="https://developers.google.com/maps/documentation/geocoding/intro#ReverseGeocoding">corresponding page</a>.</div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><br /><div style="text-align: justify;"><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Downloads</b></span></h3><hr color="#3d85c6" size="1" width="100%" /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://goo.gl/z37LaC"><img alt="Download" border="0" height="108" src="https://4.bp.blogspot.com/-JhKlqA-EMkM/UoVNR1_v4II/AAAAAAAABtM/Q1GE7I4707g/s1600/Download.jpg" title="Click to download the file" width="320" /></a></div><br />The file can be opened with <span style="color: red;">Excel 2007</span> or newer. Please enable macros before using it.<br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Read also</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><a href="https://www.myengineeringworld.net/2018/02/how-to-get-free-google-api-key.html">How To Get A Free Google API Key</a><br /><a href="https://www.myengineeringworld.net/2014/06/geocoding-using-vba-google-api.html">Geocoding Using VBA & Google API</a> <br /><a href="https://www.myengineeringworld.net/2013/08/custom-vba-trip-distance-function-google-directions-api.html">Custom Trip Distance Function (VBA & Google Directions API)</a><br /><a href="https://www.myengineeringworld.net/2013/08/custom-vba-function-google-elevation-api.html">Custom Elevation Function (VBA & Google API)</a> </div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-15975729215182754812018-02-24T19:45:00.000+02:002018-08-09T17:34:34.243+03:00How To Get A Free Google API Key (August 2018 Update)<div dir="ltr" style="text-align: left;" trbidi="on"><span style="font-size: small;">Last updated: 09/08/2018, 4 min read</span><br /><div style="text-align: justify;"><div><br /><div class="separator" style="clear: both; text-align: center;"><img alt="How To Get A Free Google API Key" border="0" data-original-height="315" data-original-width="550" src="https://2.bp.blogspot.com/-SXVpahAH9tE/WpGZgGctv5I/AAAAAAAADpU/7P-bW9tTKT0wArWfntgRYL2C_ItTiNdcACLcBGAs/s1600/How%2BTo%2BGet%2BA%2BFree%2BGoogle%2BAPI%2BKey.jpg" title="How To Get A Free Google API Key" /></div><br /></div><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><br />During the previous years, I published several VBA functions that used Google web services, such as <a href="https://www.myengineeringworld.net/2014/06/geocoding-using-vba-google-api.html">Geocoding</a>, <a href="https://www.myengineeringworld.net/2013/08/custom-vba-function-google-elevation-api.html">Elevation</a>, and <a href="https://www.myengineeringworld.net/2013/08/custom-vba-trip-distance-function-google-directions-api.html">Directions</a>. These functions worked in the same way: a properly formatted request was sent to the Google server, and, then, a response was received in XML format. If the response was successful, the requested field was retrieved (latitude, longitude, elevation, trip distance etc.) by reading the XML file. <span style="color: red;">These VBA functions working fine for several years, however, Google started to not allow the requests over HTTP protocol anymore, so these functions have become useless.</span> This is the reason why I decided to rewrite all of these VBA functions while guiding you on how to get a free Google API Key that is now required.<br /><br /><span style="color: red;"><u><b>August 2018 Update</b></u></span>: Due to the <a href="https://developers.google.com/maps/billing/important-updates">new Google's pricing</a> in effect for the entire Maps Platform (started on July 16, 2018), I decided to re-write this guide, so as to follow along with their updated policy. <span style="color: orange;">The new pricing policy reduces significantly the free usage, but, still, for small, personal projects can be adequate.</span><br /><br /><div><br /></div><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;">The necessary steps to get a free, valid API key</span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><br />The tutorial can be used for <span style="color: red;">ANY Google API</span>. As an example, I use the <span style="color: red;">Geocoding API</span>. The only prerequisite for this process is to just <span style="color: red;"><u>have a Google account</u></span> and a <span style="color: red;"><u>credit card</u></span>. <span style="color: orange;">Note: if you don't exceed the free limits, your credit card will be charged nothing.</span> So, yes, you can use the API key for free, as long as your API usage is low. More information is given below.<br /><br /><span style="color: red;"><b><u>Common steps</u></b></span><br /><span style="color: red;"><b><u><br /></u></b></span> 1. First of all, log in to your <span style="color: orange;">Google Account</span>.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Logged In With Gmail Account" border="0" data-original-height="276" data-original-width="550" src="https://2.bp.blogspot.com/-c57Kksohyo8/WpGasQ-tzsI/AAAAAAAADpg/H1dqZEAa0vUP2DJ7bTBoGV5eRS0FqmtAwCLcBGAs/s1600/Logged%2BIn%2BWith%2BGmail%2BAccount.JPG" title="Logged In With Gmail Account" /></div><br />2. Search for the appropriate API by using something like “<span style="color: orange;">Google API Name API Key</span>”. The first result will probably be the one you need to click.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Search For API Key" border="0" data-original-height="249" data-original-width="550" src="https://4.bp.blogspot.com/-ZB1974ZawXE/WpGbKN8t1wI/AAAAAAAADpk/gDYM-YabFBw37p60PywypppiP1Np2_YwACLcBGAs/s1600/Search%2BFor%2BAPI%2BKey.JPG" title="Search For API Key" /></div><br />3. On the API page, click on the <span style="color: orange;">Get Started</span> button.<br /><br /><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><img alt="Navigate To Google API Page" border="0" data-original-height="380" data-original-width="550" src="https://4.bp.blogspot.com/-BxU7WjiqLlI/W2r39YeUO1I/AAAAAAAADwI/Wvav4v0J6iAGVBx34bg5YBw8rkftlbTGgCLcBGAs/s1600/Navigate%2BTo%2BGoogle%2BAPI%2BPage.JPG" title="Navigate To Google API Page" /></div><br />4. A new page will load. In that page, you can<span style="color: orange;"> hover over the available products</span>, so as to select the one that contains the appropriate API. In this example, we can see that the Geocoding API is included in the third option (Places).<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Hover Over The Options" border="0" data-original-height="322" data-original-width="550" src="https://3.bp.blogspot.com/-90iWtLw0iVs/W2xQWhMXz6I/AAAAAAAADz8/WVsV1bKTDO0IuYixLf4uD3za4QdyCBmUwCLcBGAs/s1600/Hover%2BOver%2BThe%2BOptions.jpg" title="Hover Over The Options" /></div><br /><div class="separator" style="clear: both; text-align: center;"></div>5. <span style="color: orange;">Check</span> the appropriate product, the third one in this case, and then click on the <span style="color: orange;">Continue</span> button.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Select Product" border="0" data-original-height="346" data-original-width="550" src="https://3.bp.blogspot.com/-VIO9MuMlVrI/W2xQv4decdI/AAAAAAAAD0E/S38o0mHKZ-Q6jSNrkopmeqT9ZC3oAd69gCLcBGAs/s1600/Select%2BProduct.jpg" title="Select Product" /></div><br />6. Select the <span style="color: orange;">Create a new project</span> option and click it.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Create A New Project" border="0" data-original-height="425" data-original-width="550" src="https://2.bp.blogspot.com/-o6UnURaWFaM/W2r7c8FSuCI/AAAAAAAADwo/oXNjzf5B34gi7C3-mZ8awy2t7XsPUpHrgCLcBGAs/s1600/Create%2BA%2BNew%2BProject.JPG" title="Create A New Project" /></div>7. <span style="color: orange;">Rename</span> the project, select the <span style="color: orange;">Yes</span> radio button (agreeing with terms of service) and press the <span style="color: orange;">Next</span> button.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="New Project" border="0" data-original-height="531" data-original-width="550" src="https://1.bp.blogspot.com/-zW48LPjLU18/W2r8K4jKv4I/AAAAAAAADw0/50BAQRu8J-gBrKe0MnVpFOXYET7UweFvACLcBGAs/s1600/New%2BProject.JPG" title="New Project" /></div><br /><hr /><br /><span style="color: red;"><b><u>New billing account</u></b></span> <br /><br />8. If this is the <span style="color: red;"><b><u>first time</u></b></span> that you are using any Google API, you will have to create a new billing account. So, click on <span style="color: orange;">Create Billing Account</span> button to create one. <span style="color: red;"> </span>The next time that you will need to use another Google API, you can simply use the same billing account and follow the <a href="https://www.myengineeringworld.net/2018/02/how-to-get-free-google-api-key.html#existing">steps 14 to 16</a>.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Enable Billing" border="0" data-original-height="190" data-original-width="499" src="https://1.bp.blogspot.com/-b4kYbe9vYSs/W2r9TlglLZI/AAAAAAAADxA/R2IEfPIjoKAhRccRtJJXyYBUI0JPke8QgCLcBGAs/s1600/Enable%2BBilling.JPG" title="Enable Billing" /></div><br />9. <span style="color: orange;">Select your country</span> using the arrow button on the left, click <span style="color: orange;">Yes</span> (or No) on the radio button to receive (or not) updates from Google, click <span style="color: orange;">Yes</span> on the radio button to agree with the terms of service and press the <span style="color: orange;">Agree And Continue</span> button.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Country Selection" border="0" data-original-height="352" data-original-width="550" src="https://3.bp.blogspot.com/-64769z6rkYM/W2r-25w3_FI/AAAAAAAADxM/y188oDcnWuAcFEQFAsVJWT_4Xcm_INrhgCLcBGAs/s1600/Country%2BSelection.JPG" title="Country Selection" /></div><br />10. Fill in the necessary information, such as tax information, address and your credit card. Note that <span style="color: red;">debit cards are NOT allowed</span>. Then, press the <span style="color: orange;">Start My Free Trial</span> button.<br /><br /><div><div class="separator" style="clear: both; text-align: center;"><img alt="Payment Information" border="0" data-original-height="816" data-original-width="550" src="https://3.bp.blogspot.com/-9wfhj_Tb798/W2r_6bKaoOI/AAAAAAAADxU/o6-Eamn-OOgD7vm22ol28K-oDTFuG9AOQCLcBGAs/s1600/Payment%2BInformation.JPG" title="Payment Information" /></div><br />11. Finally, if you filled all the information successfully, you will get a message like the one below. Press the <span style="color: orange;">Got It</span> button.</div><div><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Successful Registration" border="0" data-original-height="268" data-original-width="541" src="https://3.bp.blogspot.com/-bR-HPlFxVzQ/W2sGL7BrM5I/AAAAAAAADxg/p9noMZVLpHU6M-cGlYNe6zV10i6OOzW5gCLcBGAs/s1600/Successful%2BRegistration.JPG" title="Successful Registration" /></div><br />12. The main dashboard of the project appears. Hre you can view some useful information, such as the number of requests, the traffic, as well as the billing. <span style="color: orange;">Click on your API</span> (here is the Geocoding API).</div><div><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Main Dashboard" border="0" data-original-height="184" data-original-width="550" src="https://3.bp.blogspot.com/-hN25uEFvKmE/W2sH6SDquUI/AAAAAAAADxs/2ge2Rr8HSmYFImXYGc5DvFtmQ-9X4mTLQCLcBGAs/s1600/Main%2BDashboard.JPG" title="Main Dashboard" /></div><br />13. In the API page, click on the <span style="color: orange;">Credentials</span> tab. There, you will find the API key, which you can <span style="color: orange;">copy using the button</span> shown below. If you click on the <span style="color: orange;">API Key hyperlink</span>, a new page opens. See more on the <a href="https://www.myengineeringworld.net/2018/02/how-to-get-free-google-api-key.html#restrict">Restring the API key usage</a> section of the guide.</div><div></div><br /><div><div class="separator" style="clear: both; text-align: center;"><img alt="Get The API Key" border="0" data-original-height="127" data-original-width="550" src="https://1.bp.blogspot.com/-sfN9RlBzwaU/W2tnBrfS7PI/AAAAAAAADzk/-EGxe0vgMIcYNitXzHWcSjhbALhoYHbWACLcBGAs/s1600/Get%2BThe%2BAPI%2BKey.JPG" title="Get The API Key" /></div><br /><hr /><br id="existing" /><span style="color: red;"><b><u>Existing billing account</u></b></span><span style="color: red;"><b><u><span style="color: red;"><b><u><br /></u></b></span> </u></b></span><br /><br />14. If you <span style="color: red;"><b><u>already have a billing account</u></b></span>, then you have to click on the <span style="color: orange;">Set Account</span> button on the form that will pop up.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Set Existing Billing Account" border="0" class="gr-progress" data-original-height="195" data-original-width="505" src="https://2.bp.blogspot.com/-c-szbs6RGOc/W2td_TApRqI/AAAAAAAADzE/46yK3pZMrAMugdQzrBJHQ1HnTQ7VNXYNwCLcBGAs/s1600/Set%2BExisting%2BBilling%2BAccount.JPG" title="Set Existing Billing Account" /></div><br />15. On the next form, click on the <span style="color: orange;">Next</span> button.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Enable APIs" border="0" data-original-height="258" data-original-width="550" src="https://4.bp.blogspot.com/-unWYLrtyHjc/W2tfM-kJkeI/AAAAAAAADzQ/lDFn5taP7MkzLHiJA98sy9obz8bJyqIXACLcBGAs/s1600/Enable%2BAPIs.JPG" title="Enable APIs" /></div><br />16. In the API form that pop-ups, you will find the API key, which you can <span style="color: orange;">copy using the button</span> shown below. Finally, click on the <span style="color: orange;">Done</span> button. If you click on the <span style="color: orange;">API Console hyperlink</span>, a new page opens. See more on the <a href="https://www.myengineeringworld.net/2018/02/how-to-get-free-google-api-key.html#restrict">Restring the API key usage</a> section that follows.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Get The New API Key" border="0" data-original-height="366" data-original-width="550" src="https://3.bp.blogspot.com/-KaPVMKON_hQ/W2toLuROGQI/AAAAAAAADzw/G98l5w1dOVchKZoFGNYvrDs6MZ2mSAdQgCLcBGAs/s1600/Get%2BThe%2BNew%2BAPI%2BKey.JPG" title="Get The New API Key" /></div><br /><br /><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;">Aditional information</span></h3><hr color="#3d85c6" id="restrict" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><br /><span style="color: red;"><b><u>Restring the API key usage</u></b></span> <br /><br />If you clicked either the API key hyperlink (step 13) or the API Console hyperlink (step 16), you will end up in a new page where you can <span style="color: orange;">rename your API key</span>, but more importantly, you can <span style="color: red;">restrict its access</span> (if you need to do that). Furthermore, you can<span style="color: orange;"> regenerate</span> or even <span style="color: orange;">delete</span> the key if you no longer need it. Don't forget to click the <span style="color: orange;">Save</span> button after any change you make.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Console Options" border="0" data-original-height="640" data-original-width="550" src="https://1.bp.blogspot.com/-VK36QGFYpFM/W2sKQoSWK9I/AAAAAAAADyA/GWZwSV1JzU03tzUULpt96oIQTMwUzqNywCLcBGAs/s1600/Console%2BOptions.JPG" title="Console Options" /></div><br /><hr /><br /><span style="color: red;"><b><u>Using API key in VBA functions</u></b></span> <br /><br />If you want to use your API key in one of the VBA functions that I have uploaded, for example, the <a href="https://www.myengineeringworld.net/2014/06/geocoding-using-vba-google-api.html">GetCoordinates function</a>, then <span style="color: orange;">you have to paste the key in the line that is shown below</span> (e.g. assign it to the ApiKey string variable). Note: use quotation marks before and after the key!<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Using The API Key In VBA" border="0" data-original-height="257" data-original-width="550" src="https://4.bp.blogspot.com/-pQE_8bou3Mg/W2sNaJuu2KI/AAAAAAAADyU/2_kxuGb-IvkyUErpfh7_Pdj18-TYVZwoACLcBGAs/s1600/Using%2BThe%2BAPI%2BKey%2BIn%2BVBA.JPG" title="Using The API Key In VBA" /></div><br /><hr /><br /><span style="color: red;"><b><u>Free API key limitations</u></b></span> <br /><br />Until July 16, the pricing policy of Google allowed <a href="https://developers.google.com/maps/previous-pricing">2,500 free requests per day</a> and per API. However, the free requests have been dramatically reduced. Hence, according to the <a href="https://cloud.google.com/maps-platform/pricing/sheet/?__utma=102347093.1640834864.1519418257.1533724450.1533724450.1&__utmb=102347093.0.10.1533724450&__utmc=102347093&__utmx=-&__utmz=102347093.1533724450.1.1.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided)&__utmv=-&__utmk=129641740&_ga=2.152920880.826510022.1533721749-1640834864.1519418257">new pricing</a>, for the case of Geocoding API, as the image below shows, the free requests per month are only 40,000 (i.e. 1,333 requests per day on average). In other words, <span style="color: red;">the free requests were dropped in the half</span> (46% reduction). So, when you need to use an API, take a closer look at the allowable free requests on <a href="https://cloud.google.com/maps-platform/pricing/sheet">this table</a> and try not to exceed them. More information about the billing can be found <a href="https://developers.google.com/maps/billing/understanding-cost-of-use">here</a>.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Geocoding Free Limits" border="0" data-original-height="167" data-original-width="550" src="https://1.bp.blogspot.com/-X1OvFn-G3rA/W2sUET1NcJI/AAAAAAAADy4/_xhyTkazFtUue2Erf37X6L3Ad-1QpzksACLcBGAs/s1600/Geocoding%2BFree%2BLimits.JPG" title="Geocoding Free Limits" /></div><br /><hr /><br /><span style="color: red;"><b><u>Monitoring your API requests</u></b></span> <br /><br />As we already mentioned, <span style="color: red;">the free usage is limited to a specific number of requests per month</span>. In order to have an overview of how many requests you performed, you can go to your project's page in the <a href="https://cloud.google.com/cloud-console/">Google Cloud Console</a>. There, on the <span style="color: orange;">Overview</span> tab, you can see the number of requests you have already done. In the example below, 22 requests were sent to Google's server. If you don't want to pay any money, be careful with your free limits.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Requests Counter" border="0" data-original-height="294" data-original-width="550" src="https://1.bp.blogspot.com/-OaEOykKe84M/W2sR2EaD3SI/AAAAAAAADys/bmFD0EOjzE0vEj_lVjyKuaZvRqUID2xJwCLcBGAs/s1600/Requests%2BCounter.JPG" title="Requests Counter" /></div><br /><br /></div><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;">Epilogue</span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><br />If you followed these steps, you will get a valid API key that you can use on your projects. To save you some time, here are the links to the API key pages that I have used in the VBA functions published in this blog:<br /><ul><li><a href="https://developers.google.com/maps/documentation/geocoding/get-api-key">Geocoding API Key</a> – <a href="https://www.myengineeringworld.net/2014/06/geocoding-using-vba-google-api.html">Geocoding Using VBA & Google API</a></li><li><a href="https://developers.google.com/maps/documentation/geocoding/get-api-key">Geocoding API Key</a> – <a href="https://www.myengineeringworld.net/2018/03/reverse-geocoding-vba-google-api.html">Reverse Geocoding Using VBA & Google API</a></li><li><a href="https://developers.google.com/maps/documentation/elevation/get-api-key">Elevation API Key</a> – <a href="https://www.myengineeringworld.net/2013/08/custom-vba-function-google-elevation-api.html">Custom Elevation Function (VBA & Google API)</a></li><li><a href="https://developers.google.com/maps/documentation/directions/get-api-key">Directions API Key</a> – <a href="https://www.myengineeringworld.net/2013/08/custom-vba-trip-distance-function-google-directions-api.html">Custom Trip Distance Function (VBA & Google Directions API)</a></li></ul></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-74275676513966313462018-02-18T23:30:00.000+02:002018-02-19T23:17:29.799+02:00Get Image Size In Pixels With VBA<div dir="ltr" style="text-align: left;" trbidi="on"><div dir="ltr" style="text-align: justify;" trbidi="on"><span style="font-size: small;">Last updated: 19/02/2018, 2 min read (without the code)</span></div><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Get Image Size In Pixels With VBA" border="0" data-original-height="325" data-original-width="550" src="https://2.bp.blogspot.com/-9c1kObNp_IQ/WosoWWsCmjI/AAAAAAAADoE/rxvSqNbZ_9c-UecQ6Z0fdQo2r_NSa19wwCLcBGAs/s1600/Get%2BImage%2BSize%2BIn%2BPixels%2BWith%2BVBA.jpg" title="Get Image Size In Pixels With VBA" /></div><br /><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><div style="text-align: justify;"><div style="text-align: justify;"><br />People who are interested in digital photography taking seriously the size of the images that they take. The dimensions of an image – width and height – can be easily spotted in the Windows Explorer. If you <span style="color: orange;">hover your mouse</span> over an image file you will see a pop window showing its dimensions.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Get Image Size By Hovering" border="0" data-original-height="175" data-original-width="213" src="https://1.bp.blogspot.com/-hMciq-EiVnw/Wospqhwq1AI/AAAAAAAADoQ/leajtjV4NvUcsQzRTZfMYZA_zPi-bBqagCLcBGAs/s1600/Get%2BImage%2BSize%2BBy%2BHovering.JPG" title="Get Image Size By Hovering" /></div><br /></div><div style="text-align: justify;">Alternatively, you can right-click on an image file, select properties on the menu that pop-ups and then, in the Properties form, go to the <span style="color: orange;">Details </span>tab. Close to the end, there is the image section where the dimensions, the width and height of the image is shown (in pixels).<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Get Image Size Using File Properties" border="0" data-original-height="509" data-original-width="370" src="https://1.bp.blogspot.com/--WrL_BM1PUg/Wosp1r0YSTI/AAAAAAAADoU/8D73YFf7_eExnm1BHaBEVA0nli8u5La8wCLcBGAs/s1600/Get%2BImage%2BSize%2BUsing%2BFile%2BProperties.jpg" title="Get Image Size Using File Properties" /></div></div><br />But, is there a way to get programmatically the size of an image with VBA? The answer is YES, and, unlike a <a href="http://www.myengineeringworld.net/2017/11/playing-with-photoshop-vba.html">previous post</a>, does <span style="color: red;">NOT involve Photoshop</span>!</div><br /><br /><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3 style="text-align: justify;"><span style="color: #3d85c6;"><b>VBA code</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><br /><div style="text-align: justify;">The <span style="color: red;">GetImageSize</span> function uses the <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms630368(v=vs.85).aspx">Microsoft Windows Image Acquisition Library v2.0</a> in order to retrieve the necessary information from an image file. The function returns an array of integers that hold the image width and height in pixels. The <span style="color: orange;">FileExists</span> and the <span style="color: orange;">IsValidImageFormat</span> functions check if a file exists and if a given path corresponds to a valid image file format respectively.<br /><br /><pre style="line-height: 125%; margin: 0;"><span style="color: blue;">Option Explicit</span><br /><br /><span style="color: blue;">Function</span> GetImageSize(ImagePath <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">Variant</span><br /> <br /> <span style="color: green;">'--------------------------------------------------------------------------------------</span><br /> <span style="color: green;">'Returns an array of integers that hold the image width and height in pixels.</span><br /> <span style="color: green;">'The first element of the array corresponds to the width and the second to the height.</span><br /> <br /> <span style="color: green;">'The function uses the Microsoft Windows Image Acquisition Library v2.0, which can be</span><br /> <span style="color: green;">'found in the path: C:\Windows\System32\wiaaut.dll</span><br /> <span style="color: green;">'However, the code is written in late binding, so no reference is required.</span><br /> <br /> <span style="color: green;">'Written By: Christos Samaras</span><br /> <span style="color: green;">'Date: 18/02/2018</span><br /> <span style="color: green;">'E-mail: xristos.samaras@gmail.com</span><br /> <span style="color: green;">'Site: http://www.myengineeringworld.net</span><br /> <span style="color: green;">'--------------------------------------------------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> imgSize(1) <span style="color: blue;">As</span> <span style="color: #2b91af;">Integer</span><br /> <span style="color: blue;">Dim</span> wia <span style="color: blue;">As</span> <span style="color: #2b91af;">Object</span><br /> <br /> <span style="color: green;">'Check that the image file exists.</span><br /> <span style="color: blue;">If</span> FileExists(ImagePath) = <span style="color: blue;">False</span> <span style="color: blue;">Then</span> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> <br /> <span style="color: green;">'Check that the image file corresponds to an image format.</span><br /> <span style="color: blue;">If</span> IsValidImageFormat(ImagePath) = <span style="color: blue;">False</span> <span style="color: blue;">Then</span> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> <br /> <span style="color: green;">'Create the ImageFile object and check if it exists.</span><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <span style="color: blue;">Set</span> wia = CreateObject(<span style="color: #a31515;">"WIA.ImageFile"</span>)<br /> <span style="color: blue;">If</span> wia <span style="color: blue;">Is</span> <span style="color: blue;">Nothing</span> <span style="color: blue;">Then</span> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> On <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /> <span style="color: green;">'Load the ImageFile object with the specified File.</span><br /> wia.LoadFile ImagePath<br /> <br /> <span style="color: green;">'Get the necessary properties.</span><br /> imgSize(0) = wia.Width<br /> imgSize(1) = wia.Height<br /> <br /> <span style="color: green;">'Release the ImageFile object.</span><br /> <span style="color: blue;">Set</span> wia = <span style="color: blue;">Nothing</span><br /> <br /> <span style="color: green;">'Return the array.</span><br /> GetImageSize = imgSize<br /><br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Function</span> FileExists(FilePath <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">Boolean</span><br /> <br /> <span style="color: green;">'--------------------------------------------------</span><br /> <span style="color: green;">'Checks if a file exists (using the Dir function).</span><br /> <span style="color: green;">'--------------------------------------------------</span><br /><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <span style="color: blue;">If</span> Len(FilePath) > 0 <span style="color: blue;">Then</span><br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> Dir(FilePath, vbDirectory) = vbNullString <span style="color: blue;">Then</span> FileExists = <span style="color: blue;">True</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Function</span> IsValidImageFormat(FilePath <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">Boolean</span><br /> <br /> <span style="color: green;">'----------------------------------------------</span><br /> <span style="color: green;">'Checks if a given path is a valid image file.</span><br /> <span style="color: green;">'----------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> imageFormats <span style="color: blue;">As</span> <span style="color: #2b91af;">Variant</span><br /> <span style="color: blue;">Dim</span> i <span style="color: blue;">As</span> <span style="color: #2b91af;">Integer</span><br /> <br /> <span style="color: green;">'Some common image extentions.</span><br /> imageFormats = Array(<span style="color: #a31515;">".bmp"</span>, <span style="color: #a31515;">".jpg"</span>, <span style="color: #a31515;">".gif"</span>, <span style="color: #a31515;">".tif"</span>, <span style="color: #a31515;">".png"</span>)<br /> <br /> <span style="color: green;">'Loop through all the extentions and check if the path contains one of them.</span><br /> <span style="color: blue;">For</span> i = LBound(imageFormats) <span style="color: blue;">To</span> UBound(imageFormats)<br /> <span style="color: green;">'If the file path contains the extension return true.</span><br /> <span style="color: blue;">If</span> InStr(1, UCase(FilePath), UCase(imageFormats(i)), vbTextCompare) > 0 <span style="color: blue;">Then</span><br /> IsValidImageFormat = <span style="color: blue;">True</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> End <span style="color: blue;">If</span><br /> <span style="color: blue;">Next</span> i<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /></pre><br /><br /><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3 style="text-align: justify;"><span style="color: #3d85c6;"><b>Sample workbook</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><br />The sample workbook contains two sheets: the first one (Function Examples) presents several ways of using the GetImageSize function <span style="color: red;">directly from the worksheet</span>. The examples involve the CELL, TRANSPOSE and the INDEX function.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Get Image Size Function Examples" border="0" data-original-height="389" data-original-width="550" src="https://1.bp.blogspot.com/-6r3eSxN9Xpk/WoswDd1BJJI/AAAAAAAADoo/WQuF218LLwQCpAwURLeKAE0PsNLXUaHrgCLcBGAs/s1600/Function%2BExamples.jpg" title="Get Image Size Function Examples" /></div><br />The second sheet (Batch Mode) can be used to <span style="color: red;">get the size information of up to 100 different images</span>. By inserting the image paths in column C, the width and height are automatically shown in columns D and E. If you need an easy way to retrieve the file paths form a folder, use this <a href="http://www.myengineeringworld.net/2012/07/folders-subfolders-files.html">free tool</a>.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Get Image Size In Batch Mode" border="0" data-original-height="131" data-original-width="550" src="https://4.bp.blogspot.com/-u4fZi55xqyc/WoswpVvW02I/AAAAAAAADow/F2nCSFTR_OgzBSuXgVpGtEdqJK-nMDuOQCLcBGAs/s1600/Batch%2BMode.jpg" title="Get Image Size In Batch Mode" /></div><br /><br /><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;"><b>Downloads</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://goo.gl/5bq3FX"><img alt="Download" border="0" height="108" src="https://4.bp.blogspot.com/-JhKlqA-EMkM/UoVNR1_v4II/AAAAAAAABtM/Q1GE7I4707g/s1600/Download.jpg" title="Click to download the file" width="320" /></a></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">The <span style="color: red;">zip file</span> contains a workbook with the two worksheets that presented above, the VBA code and a sample image to play with it. The workbook can be opened with <span style="color: red;">Excel 2007</span> or newer. Please enable macros before using it.</div></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-89735866055558229622018-01-15T01:48:00.000+02:002018-05-27T01:04:02.686+03:00Pipe Friction Factor - Online Calculator<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;"><span style="font-size: x-small;">Last updated: 15/01/2018, 1 min read (without the tables)</span></div><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Pipe Friction Factor - Online Calculator" border="0" data-original-height="363" data-original-width="594" src="https://1.bp.blogspot.com/-LxqAUuvGhPY/Wly94GjfA9I/AAAAAAAADns/FAf9UR2-39kE8S5P8eRKfw4YvGfsrO2WACLcBGAs/s1600/Pipe%2BFriction%2BFactor%2B-%2BOnline%2BCalculator.JPG" title="Pipe Friction Factor - Online Calculator" /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;">After a long time, I am returning to a subject that has bothered me many times in the past: friction factor. This time, however, I tried to switch my typical tools, so instead of an Excel/VBA solution, you will get a free online tool. The tool calculates the pipe friction factor based on Churchill's equation. If you have read any of my previous <a href="http://www.myengineeringworld.net/2011/06/10-1-ways-to-find-friction-factor-in.html">posts</a>, you will probably remember that Churchill's equation is valid for the entire Reynolds range (e.g. any flow type). The equation involves the calculation of two intermediate values that are used in the final equation.</div><div style="text-align: justify;"><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Churchill Equations" border="0" src="https://3.bp.blogspot.com/-wM_3d_hrzw8/Ujty57TJubI/AAAAAAAABdc/o3u3i4WKW-A/s1600/Churchill+Equations.jpg" title="Churchill Equations" /></div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: orange;"><u><b>Inputs</b></u></span></h3><a href="https://www.myengineeringworld.net/2018/01/pipe-friction-factor-online-calculator.html#roughnesstable">Roughness</a> of the pipe material [μm]<br /><input id="roughness" type="text" /><br /><br />Internal Diameter of the (circular) pipe [mm]<br /><input id="diameter" type="text" /> <br /><br />Discharge [m³/h]<br /><input id="discharge" type="text" /><br /><br /><a href="https://www.myengineeringworld.net/2018/01/pipe-friction-factor-online-calculator.html#viscositytable">Kinematic Viscosity</a> of the fluid flowing through the pipe [cSt = 10−6 m²/s]<br /><input id="viscosity" type="text" /> <br /><br /><button id="calculate" style="padding: 5px 42px;">Calculate</button><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: orange;"><u><b>Results</b></u></span></h3>Fluid Velocity [m/s]<br /><input id="velocity" type="text" /> <br /><br />Reynolds [-]<br /><input id="reynolds" type="text" /> <br /><br />Type of flow [-]<br /><input id="flowtype" type="text" /> <br /><br /><span style="color: blue;">Friction Factor [-]</span><br /><input id="friction" type="text" /><br /><br /><button id="reset" style="background-color: #f44336; padding: 5px 53px;">Reset</button> <br /><br /><hr color="#3d85c6" size="1" width="100%" /><script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script> <script src="https://goo.gl/kNbzSa"></script> <script type="text/javascript">$(document).ready(ResetInput); $("#calculate").click(CalculateAndDisplayResults); $("#reset").click(ResetInput); </script><br /><br /><div id="roughnesstable" style="text-align: center;"><span style="color: orange;">Table 1:</span> Suggested roughness values for several pipe materials.</div><br /><table><thead><tr><th style="text-align: left;"><span style="font-size: large;">Pipe Material</span></th><th style="text-align: left;"><span style="font-size: large;">Roughness (μm)</span></th></tr></thead><tbody><tr><td>Aluminum</td><td>1.5</td></tr><tr><td>Asbestos cement</td><td>25</td></tr><tr><td>Cast iron (asphalted)</td><td>122</td></tr><tr><td>Cast iron (uncoated)</td><td>254</td></tr><tr><td>Concrete (steel forms, with smooth joints)</td><td>180</td></tr><tr><td>Drawn brass</td><td>1.5</td></tr><tr><td>Drawn copper</td><td>1.5</td></tr><tr><td>Drawn tubing - glass, brass, plastic</td><td>1.5</td></tr><tr><td>Fiberglass</td><td>5</td></tr><tr><td>Fiberglass with epoxy</td><td>3</td></tr><tr><td>Galvanized iron</td><td>152</td></tr><tr><td>Polyethylene</td><td>3</td></tr><tr><td>PVC</td><td>1.5</td></tr><tr><td>Seamless commercial steel (galvanised)</td><td>150</td></tr><tr><td>Seamless commercial steel (light rust)</td><td>250</td></tr><tr><td>Seamless commercial steel (new)</td><td>25</td></tr><tr><td>Stainless steel</td><td>45</td></tr><tr><td>Welded steel</td><td>600</td></tr><tr><td>Wood stave</td><td>600</td></tr><tr><td>Wrought iron</td><td>45</td></tr></tbody></table><br /><br /><div id="viscositytable" style="text-align: center;"><span style="color: orange;">Table 2:</span> Suggested kinematic viscosity values for various liquids.</div><br /><table><thead><tr><th style="text-align: left;"><span style="font-size: large;">Liquid</span></th><th style="text-align: left;"><span style="font-size: large;">Temperature (C)</span></th><th style="text-align: left;"><span style="font-size: large;">Kinematic Viscosity (cSt)</span></th></tr></thead><tbody><tr><td>Acetaldehyde CH3CHO</td><td>16.1</td><td>0.305</td></tr><tr><td></td><td>20</td><td>0.295</td></tr><tr><td>Acetic acid - vinegar - 10% CH3COOH</td><td>15</td><td>1.35</td></tr><tr><td>Acetic acid - 50%</td><td>15</td><td>2.27</td></tr><tr><td>Acetic acid - 80%</td><td>15</td><td>2.85</td></tr><tr><td>Acetic acid - concentrated glacial</td><td>15</td><td>1.34</td></tr><tr><td>Acetic acid anhydride (CH3COO)2O</td><td>15</td><td>0.88</td></tr><tr><td>Acetone CH3COCH3</td><td>20</td><td>0.41</td></tr><tr><td>Alcohol - allyl</td><td>20</td><td>1.6</td></tr><tr><td>Alcohol - butyl-n</td><td>20</td><td>3.64</td></tr><tr><td>Alcohol - ethyl (grain) C2H5OH</td><td>20</td><td>1.52</td></tr><tr><td></td><td>37.8</td><td>1.2</td></tr><tr><td>Alcohol - methyl (wood) CH3OH</td><td>15</td><td>0.74</td></tr><tr><td></td><td>0</td><td>1.04</td></tr><tr><td>Alcohol - propyl</td><td>20</td><td>2.8</td></tr><tr><td></td><td>50</td><td>1.4</td></tr><tr><td>Aluminum sulfate - 36% solution</td><td>20</td><td>1.41</td></tr><tr><td>Ammonia</td><td>-17.8</td><td>0.3</td></tr><tr><td>Aniline</td><td>20</td><td>4.37</td></tr><tr><td></td><td>10</td><td>6.4</td></tr><tr><td>Asphalt RC-0, MC-0, SC-0</td><td>25</td><td>159-324</td></tr><tr><td></td><td>37.8</td><td>60-108</td></tr><tr><td>Automatic crankcase oil SAE 10W</td><td>-17.8</td><td>1295-max</td></tr><tr><td>Automatic crankcase oil SAE 10W</td><td>-17.8</td><td>1295-2590</td></tr><tr><td>Automatic crankcase oil SAE 20W</td><td>-17.8</td><td>2590-10350</td></tr><tr><td>Automatic crankcase oil SAE 20</td><td>98.9</td><td>5.7-9.6</td></tr><tr><td>Automatic crankcase oil SAE 30</td><td>98.9</td><td>9.6-12.9</td></tr><tr><td>Automatic crankcase oil SAE 40</td><td>98.9</td><td>12.9-16.8</td></tr><tr><td>Automatic crankcase oil SAE 50</td><td>98.9</td><td>16.8-22.7</td></tr><tr><td>Automotive gear oil SAE 75W</td><td>98.9</td><td>4.2 min</td></tr><tr><td>Automotive gear oil SAE 80W</td><td>98.9</td><td>7.0 min</td></tr><tr><td>Automotive gear oil SAE 85W</td><td>98.9</td><td>11.0 min</td></tr><tr><td>Automotive gear oil SAE 90W</td><td>98.9</td><td>14-25</td></tr><tr><td>Automotive gear oil SAE 140</td><td>98.9</td><td>25-43</td></tr><tr><td>Automotive gear oil SAE150</td><td>98.9</td><td>43 - min</td></tr><tr><td>Beer</td><td>20</td><td>1.8</td></tr><tr><td>Benzene (Benzol) C6H6</td><td>0</td><td>1</td></tr><tr><td></td><td>20</td><td>0.74</td></tr><tr><td>Bone oil</td><td>54.4</td><td>47.5</td></tr><tr><td></td><td>100</td><td>11.6</td></tr><tr><td>Bromine</td><td>20</td><td>0.34</td></tr><tr><td>Butane-n</td><td>-1.1</td><td>0.52</td></tr><tr><td></td><td></td><td>0.35</td></tr><tr><td>Butyric acid n</td><td>20</td><td>1.61</td></tr><tr><td>Calcium chloride 5%</td><td>18.3</td><td>1.156</td></tr><tr><td>Calcium chloride 25%</td><td>15.6</td><td>4</td></tr><tr><td>Carbolic acid (phenol)</td><td>18.3</td><td>11.83</td></tr><tr><td>Carbon tetrachloride CCl4</td><td>20</td><td>0.612</td></tr><tr><td></td><td>37.8</td><td>0.53</td></tr><tr><td>Carbon disulfide CS2</td><td>0</td><td>0.33</td></tr><tr><td></td><td>20</td><td>0.298</td></tr><tr><td>Castor oil</td><td>37.8</td><td>259-325</td></tr><tr><td></td><td>54.4</td><td>98-130</td></tr><tr><td>China wood oil</td><td>20.6</td><td>308.5</td></tr><tr><td></td><td>37.8</td><td>125.5</td></tr><tr><td>Chloroform</td><td>20</td><td>0.38</td></tr><tr><td></td><td>60</td><td>0.35</td></tr><tr><td>Coconut oil</td><td>37.8</td><td>29.8-31.6</td></tr><tr><td></td><td>54.4</td><td>14.7-15.7</td></tr><tr><td>Cod oil (fish oil)</td><td>37.8</td><td>32.1</td></tr><tr><td></td><td>54.4</td><td>19.4</td></tr><tr><td>Corn oil</td><td>54.4</td><td>28.7</td></tr><tr><td></td><td>100</td><td>8.6</td></tr><tr><td>Corn starch solution, 22 Baumé</td><td>21.1</td><td>32.1</td></tr><tr><td></td><td>37.8</td><td>27.5</td></tr><tr><td>Corn starch solution, 24 Baumé</td><td>21.1</td><td>129.8</td></tr><tr><td></td><td>37.8</td><td>95.2</td></tr><tr><td>Corn starch solution, 25 Baumé</td><td>21.1</td><td>303</td></tr><tr><td></td><td>37.8</td><td>173.2</td></tr><tr><td>Cotton seed oil</td><td>37.8</td><td>37.9</td></tr><tr><td></td><td>54.4</td><td>20.6</td></tr><tr><td>Crude oil 48o API</td><td>15.6</td><td>3.8</td></tr><tr><td></td><td>54.4</td><td>1.6</td></tr><tr><td>Crude oil 40o API</td><td>15.6</td><td>9.7</td></tr><tr><td></td><td>54.4</td><td>3.5</td></tr><tr><td>Crude oil 35.6o API</td><td>15.6</td><td>17.8</td></tr><tr><td></td><td>54.4</td><td>4.9</td></tr><tr><td>Crude oil 32.6o API</td><td>15.6</td><td>23.2</td></tr><tr><td></td><td>54.4</td><td>7.1</td></tr><tr><td>Decane-n</td><td>17.8</td><td>2.36</td></tr><tr><td></td><td>37.8</td><td>1.001</td></tr><tr><td>Diethyl glycol</td><td>21.1</td><td>32</td></tr><tr><td>Diethyl ether</td><td>20</td><td>0.32</td></tr><tr><td>Diesel fuel 2D</td><td>37.8</td><td>2-Ιουν</td></tr><tr><td></td><td>54.4</td><td>1.-3.97</td></tr><tr><td>Diesel fuel 3D</td><td>37.8</td><td>6-11.75</td></tr><tr><td></td><td>54.4</td><td>3.97-6.78</td></tr><tr><td>Diesel fuel 4D</td><td>37.8</td><td>29.8 max</td></tr><tr><td></td><td>54.4</td><td>13.1 max</td></tr><tr><td>Diesel fuel 5D</td><td>50</td><td>86.6 max</td></tr><tr><td></td><td>71.1</td><td>35.2 max</td></tr><tr><td>Ethyl acetate CH3COOC2H3</td><td>15</td><td>0.4</td></tr><tr><td></td><td>20</td><td>0.49</td></tr><tr><td>Ethyl bromide C2H5Br</td><td>20</td><td>0.27</td></tr><tr><td>Ethylene bromide</td><td>20</td><td>0.787</td></tr><tr><td>Ethylene chloride</td><td>20</td><td>0.668</td></tr><tr><td>Ethylene glycol</td><td>21.1</td><td>17.8</td></tr><tr><td>Formic acid 10%</td><td>20</td><td>1.04</td></tr><tr><td>Formic acid 50%</td><td>20</td><td>1.2</td></tr><tr><td>Formic acid 80%</td><td>20</td><td>1.4</td></tr><tr><td>Formic acid concentrated</td><td>20</td><td>1.48</td></tr><tr><td>Trichlorofluoromethane, R-11</td><td>21.1</td><td>0.21</td></tr><tr><td>Dichlorodifluoromethane, R-12</td><td>21.1</td><td>0.27</td></tr><tr><td>FDichloro-fluoromethane, R-21</td><td>21.1</td><td>1.45</td></tr><tr><td>Furfurol</td><td>20</td><td>1.45</td></tr><tr><td>Fuel oil 1</td><td>21.1</td><td>2.39-4.28</td></tr><tr><td></td><td>37.8</td><td>-2.69</td></tr><tr><td>Fuel oil 2</td><td>21.1</td><td>3.0-7.4</td></tr><tr><td></td><td>37.8</td><td>2.11-4.28</td></tr><tr><td>Fuel oil 3</td><td>21.1</td><td>2.69-5.84</td></tr><tr><td></td><td>37.8</td><td>2.06-3.97</td></tr><tr><td>Fuel oil 5A</td><td>21.1</td><td>7.4-26.4</td></tr><tr><td></td><td>37.8</td><td>4.91-13.7</td></tr><tr><td>Fuel oil 5B</td><td>21.1</td><td>26.4-</td></tr><tr><td></td><td>37.8</td><td>13.6-67.1</td></tr><tr><td>Fuel oil 6</td><td>50</td><td>97.4-660</td></tr><tr><td></td><td>71.1</td><td>37.5-172</td></tr><tr><td>Gas oils</td><td>21.1</td><td>13.9</td></tr><tr><td></td><td>37.8</td><td>7.4</td></tr><tr><td>Gasoline a</td><td>15.6</td><td>0.88</td></tr><tr><td></td><td>37.8</td><td>0.71</td></tr><tr><td>Gasoline b</td><td>15.6</td><td>0.64</td></tr><tr><td></td><td>37.8</td><td></td></tr><tr><td>Gasoline c</td><td>15.6</td><td>0.46</td></tr><tr><td></td><td>37.8</td><td>0.4</td></tr><tr><td>Glycerine 100%</td><td>20.3</td><td>648</td></tr><tr><td></td><td>37.8</td><td>176</td></tr><tr><td>Glycerine 50% water</td><td>20</td><td>5.29</td></tr><tr><td>Glycol</td><td></td><td>52</td></tr><tr><td>Glucose</td><td>37.8</td><td>7.7M-22M</td></tr><tr><td></td><td>65.6</td><td>880-2420</td></tr><tr><td>Heptanes-n</td><td>-17.8</td><td>0.928</td></tr><tr><td></td><td>37.8</td><td>0.511</td></tr><tr><td>Hexane-n</td><td>-17.8</td><td>0.683</td></tr><tr><td></td><td>37.8</td><td>0.401</td></tr><tr><td>Honey</td><td>37.8</td><td>73.6</td></tr><tr><td>Hydrochloric acid</td><td></td><td>1.9</td></tr><tr><td>Ink, printers</td><td>37.8</td><td>550-2200</td></tr><tr><td></td><td>54.4</td><td>238-660</td></tr><tr><td>Insulating oil</td><td>21.1</td><td>24.1 max</td></tr><tr><td></td><td>37.8</td><td>11.75 max</td></tr><tr><td>Kerosene</td><td>20</td><td>2.71</td></tr><tr><td>Jet Fuel</td><td>-34.4</td><td>7.9</td></tr><tr><td>Lard</td><td>37.8</td><td>62.1</td></tr><tr><td></td><td>54.4</td><td>34.3</td></tr><tr><td>Lard oil</td><td>37.8</td><td>41-47.5</td></tr><tr><td></td><td>54.4</td><td>23.4-27.1</td></tr><tr><td>Linseed oil</td><td>37.8</td><td>30.5</td></tr><tr><td></td><td>54.4</td><td>18.94</td></tr><tr><td>Mercury</td><td>21.1</td><td>0.118</td></tr><tr><td></td><td>37.8</td><td>0.11</td></tr><tr><td>Methyl acetate</td><td>20</td><td>0.44</td></tr><tr><td>Methyl iodide</td><td>20</td><td>0.213</td></tr><tr><td>Menhaden oil</td><td>37.8</td><td>29.8</td></tr><tr><td></td><td>54.4</td><td>18.2</td></tr><tr><td>Milk</td><td>20</td><td>1.13</td></tr><tr><td>Molasses A, first</td><td>37.8</td><td>281-5070</td></tr><tr><td></td><td>54.4</td><td>151-1760</td></tr><tr><td>Molasses B, second</td><td>37.8</td><td>1410-13200</td></tr><tr><td></td><td>54.4</td><td>660-3300</td></tr><tr><td>Molasses C, blackstrap</td><td>37.8</td><td>2630-5500</td></tr><tr><td></td><td>54.4</td><td>1320-16500</td></tr><tr><td>Naphthalene</td><td>80</td><td>0.9</td></tr><tr><td>Neatstool oil</td><td>37.8</td><td>49.7</td></tr><tr><td></td><td>54.4</td><td>27.5</td></tr><tr><td>Nitrobenzene</td><td>20</td><td>1.67</td></tr><tr><td>Nonane-n</td><td>-17.8</td><td>1.728</td></tr><tr><td></td><td>37.8</td><td>0.807</td></tr><tr><td>Octane-n</td><td>-17.8</td><td>1.266</td></tr><tr><td></td><td>37.8</td><td>0.645</td></tr><tr><td>Olive oil</td><td>37.8</td><td>43.2</td></tr><tr><td></td><td>54.4</td><td>24.1</td></tr><tr><td>Palms oil</td><td>37.8</td><td>47.8</td></tr><tr><td></td><td>54.4</td><td>26.4</td></tr><tr><td>Peanut oil</td><td>37.8</td><td>42</td></tr><tr><td></td><td>54.4</td><td>23.4</td></tr><tr><td>Pentane-n</td><td>17.8</td><td>0.508</td></tr><tr><td></td><td>26.7</td><td>0.342</td></tr><tr><td>Petrolatum</td><td>54.4</td><td>20.5</td></tr><tr><td></td><td>71.1</td><td>15</td></tr><tr><td>Petroleum ether</td><td>15.6</td><td>31(est)</td></tr><tr><td>Phenol, carbolic acid</td><td></td><td>11.7</td></tr><tr><td></td><td>20</td><td>1.13</td></tr><tr><td>Propylene glycol</td><td>21.1</td><td>52</td></tr><tr><td>Quenching oil</td><td></td><td>100-120</td></tr><tr><td>(typical)</td><td></td><td></td></tr><tr><td>Rapeseed oil</td><td>37.8</td><td>54.1</td></tr><tr><td></td><td>54.4</td><td>31</td></tr><tr><td>Rosin oil</td><td>37.8</td><td>324.7</td></tr><tr><td></td><td>54.4</td><td>129.9</td></tr><tr><td>Rosin (wood)</td><td>37.8</td><td>216-11M</td></tr><tr><td></td><td>93.3</td><td>108-4400</td></tr><tr><td>Sesame seed oil</td><td>37.8</td><td>39.6</td></tr><tr><td></td><td>54.4</td><td>23</td></tr><tr><td>Silicate of soda</td><td></td><td>79</td></tr><tr><td>Sodium chloride 5%</td><td>20</td><td>1.097</td></tr><tr><td>Sodium chloride 25%</td><td>15.6</td><td>2.4</td></tr><tr><td>Sodium hydroxide (caustic soda) 20%</td><td>18.3</td><td>4</td></tr><tr><td>Sodium hydroxide (caustic soda) 30%</td><td>18.3</td><td>10</td></tr><tr><td>Sodium hydroxide (caustic soda) 40%</td><td>18.3</td><td></td></tr><tr><td>Soya bean oil</td><td>37.8</td><td>35.4</td></tr><tr><td></td><td>54.4</td><td>19.64</td></tr><tr><td>Sperm oil</td><td>37.5</td><td>21-23</td></tr><tr><td></td><td>54.4</td><td>15.2</td></tr><tr><td>Sulphuric acid 100%</td><td>20</td><td>14.56</td></tr><tr><td>Sulphuric acid 95%</td><td>20</td><td>14.5</td></tr><tr><td>Sulphuric acid 60%</td><td>20</td><td>4.4</td></tr><tr><td>Sulphuric acid 20%</td><td></td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td>Tar, coke oven</td><td>21.1</td><td>600-1760</td></tr><tr><td></td><td>37.8</td><td>141-308</td></tr><tr><td>Tar, gas house</td><td>21.1</td><td>3300-66M</td></tr><tr><td></td><td>37.8</td><td>440-4400</td></tr><tr><td>Tar, pine</td><td>37.8</td><td>559</td></tr><tr><td></td><td>55.6</td><td>108.2</td></tr><tr><td>Toluene</td><td>20</td><td>0.68</td></tr><tr><td>Triethylene glycol</td><td>21.1</td><td>40</td></tr><tr><td></td><td></td><td></td></tr><tr><td>Turpentine</td><td>37.8</td><td>86.5-95.2</td></tr><tr><td></td><td>54.4</td><td>39.9-44.3</td></tr><tr><td>Varnish, spar</td><td>20</td><td>313</td></tr><tr><td></td><td>37.8</td><td>143</td></tr><tr><td>Water, distilled</td><td>20</td><td>1.0038</td></tr><tr><td>Water, fresh</td><td>15.6</td><td>1.13</td></tr><tr><td></td><td>54.4</td><td>0.55</td></tr><tr><td>Water, sea</td><td></td><td>1.15</td></tr><tr><td>Whale oil</td><td>37.8</td><td>35-39.6</td></tr><tr><td></td><td>54.4</td><td>19.9-23.4</td></tr><tr><td>Xylene-o</td><td>20</td><td>0.93</td></tr></tbody></table><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Read also</b></span></h3><hr color="#3d85c6" size="1" width="100%" /></div><div style="text-align: justify;"></div><div style="text-align: justify;"><br /><a href="http://www.myengineeringworld.net/2011/05/solutions-of-colebrook-white-equation.html">Solutions Of Colebrook & White Equation</a><br /><a href="http://www.myengineeringworld.net/2011/06/10-1-ways-to-find-friction-factor-in.html">10 + 1 Ways To Find The Friction Factor In Pipes</a><br /><a href="http://www.myengineeringworld.net/2011/08/friction-factor-function.html">Friction Factor Function</a><br /><a href="http://www.myengineeringworld.net/2011/06/pressure-losses-in-piping-systems.html">Pressure Losses In Piping Systems</a><br /><a href="http://www.myengineeringworld.net/2012/07/head-and-pressure-losses-in-pipes.html">Head And Pressure Losses In Pipes</a> </div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-36735304651982607212017-11-05T01:09:00.002+02:002018-05-12T17:15:22.015+03:00Playing With Photoshop From Excel VBA<div dir="ltr" style="text-align: left;" trbidi="on"><div><span style="font-size: x-small;">Last updated: 06/11/2017, 1 min read (without the code)</span></div><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Photoshop VBA" border="0" data-original-height="338" data-original-width="550" src="https://3.bp.blogspot.com/-P5oIJ0uFOyQ/WgCIRBJq-mI/AAAAAAAADnI/BezK-D1D-OM1k1T69hGi4fFUt_CivhD_ACLcBGAs/s1600/Photoshop%2BVBA.jpg" title="Photoshop VBA" /></div><br /><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><br /><div style="text-align: justify;">Adobe Photoshop is probably the most famous raster graphics editor that is available on the market for almost three decades. You can do <a href="https://www.amazon.com/gp/product/0321827333/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321827333&linkCode=as2&tag=myengiworl-20&linkId=f1074abc73a4550f0e77064b417999f2">amazing things</a> with this software. I have only played with it for a few hours, and I was quite surprised with the results. One of the things that intrigued me the most was the ability to program it. So, in this post I will take the opportunity to show you how to:<br /><br /><ul><li>Resize an image.</li><li>Modify an image (apply filters).</li><li>Convert an image.</li></ul><br /><span style="color: red;">Here I will only scratch the surface on the topic of Photoshop VBA programming.</span> However, I hope that it can give you some insights on what can be achieved.<br /><br /><div style="text-align: justify;"><br /></div><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3 style="text-align: justify;"><span style="color: #3d85c6;"><b>VBA code</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /></div><br /><div style="text-align: justify;">The code was written in early binding, just to allow the user to investigate the Photoshop's object model and the available options.</div><br /><pre style="line-height: 125%; margin: 0;"><span style="color: blue;">Option Explicit</span><br /><br /><span style="color: blue;">Sub</span> PlayingWithPhotoshop()<br /><br /> <span style="color: green;">'-----------------------------------------------------------------------------------------------------------------</span><br /> <span style="color: green;">'The macro demonstrates how to resize, modify (apply filters) and convert an image in Adobe Photoshop using VBA.</span><br /> <br /> <span style="color: green;">'The macro requires the Adobe Photoshop library in order to work.</span><br /> <span style="color: green;">'Go to Tools -> References -> Adobe Photoshop CSx Object Library, where x depends on your</span><br /> <span style="color: green;">'Adobe Photoshop version (i.e. 6 or 5) you have installed on your PC.</span><br /> <br /> <span style="color: green;">'Written By: Christos Samaras</span><br /> <span style="color: green;">'Date: 04/11/2017</span><br /> <span style="color: green;">'E-mail: xristos.samaras@gmail.com</span><br /> <span style="color: green;">'Site: http://www.myengineeringworld.net</span><br /> <span style="color: green;">'-----------------------------------------------------------------------------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> InputImagePath <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span><br /> <span style="color: blue;">Dim</span> OutputImagePath <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span><br /> <span style="color: blue;">Dim</span> OutputWidthInPixels <span style="color: blue;">As</span> <span style="color: #2b91af;">Integer</span><br /> <span style="color: blue;">Dim</span> PsApp <span style="color: blue;">As</span> Photoshop.Application<br /> <span style="color: blue;">Dim</span> PsDoc <span style="color: blue;">As</span> Photoshop.Document<br /> <span style="color: blue;">Dim</span> PsSaveOptions <span style="color: blue;">As</span> Photoshop.PNGSaveOptions<br /> <br /> <span style="color: green;">'Set the necessary input variables.</span><br /> <span style="color: green;">'The input image that will be modified. You can give the full path, e.g.:</span><br /> <span style="color: green;">'InputImagePath = "C:\Users\Christos\Desktop\Input.jpg"</span><br /> InputImagePath = ThisWorkbook.Path & <span style="color: #a31515;">"\"</span> & <span style="color: #a31515;">"Input.jpg"</span><br /> <br /> <span style="color: green;">'The output image that will be created. Here it will be a png image. You can give the full path as well, e.g.:</span><br /> <span style="color: green;">'OutputImagePath = "C:\Users\Christos\Desktop\Output.png"</span><br /> OutputImagePath = ThisWorkbook.Path & <span style="color: #a31515;">"\"</span> & <span style="color: #a31515;">"Output.png"</span><br /> <br /> <span style="color: green;">'The required width of the output image.</span><br /> OutputWidthInPixels = 750<br /> <br /> <span style="color: green;">'Check if the input image path is valid.</span><br /> <span style="color: blue;">If</span> FileExists(InputImagePath) = <span style="color: blue;">False</span> <span style="color: blue;">Then</span><br /> MsgBox <span style="color: #a31515;">"The path of the input image is invalid!"</span>, vbCritical, <span style="color: #a31515;">"Input Image Path Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> End <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Create a new instance of Photoshop application and make it visible.</span><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <span style="color: blue;">Set</span> PsApp = <span style="color: blue;">New</span> Photoshop.Application<br /> <span style="color: blue;">If</span> PsApp <span style="color: blue;">Is</span> <span style="color: blue;">Nothing</span> <span style="color: blue;">Then</span><br /> MsgBox <span style="color: #a31515;">"Sorry, it was impossible to start Photoshop!"</span>, vbCritical, <span style="color: #a31515;">"Photoshop Application Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> End <span style="color: blue;">If</span><br /> PsApp.Visible = <span style="color: blue;">True</span><br /> <br /> <span style="color: green;">'Try to open the input image.</span><br /> <span style="color: blue;">Set</span> PsDoc = PsApp.Open(InputImagePath)<br /> <span style="color: blue;">If</span> PsDoc <span style="color: blue;">Is</span> <span style="color: blue;">Nothing</span> <span style="color: blue;">Then</span><br /> MsgBox <span style="color: #a31515;">"Sorry, it was impossible to open the input image!"</span>, vbCritical, <span style="color: #a31515;">"Image Opening Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> End <span style="color: blue;">If</span><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /> <span style="color: green;">'Print the image dimensions before resizing (in the Immediate window of the VBA editor).</span><br /> Debug.Print <span style="color: #a31515;">"Before: Width (px): "</span> & PsDoc.Width * PsDoc.Resolution / 2.54 & <span style="color: #a31515;">" Height (px): "</span> & PsDoc.Height * PsDoc.Resolution / 2.54<br /> <br /> <span style="color: green;">'Resize the image.</span><br /> PsDoc.ResizeImage 2.54 * OutputWidthInPixels / PsDoc.Resolution<br /> <br /> <span style="color: green;">'Print the image dimensions after resizing.</span><br /> Debug.Print <span style="color: #a31515;">"After: Width (px): "</span> & PsDoc.Width * PsDoc.Resolution / 2.54 & <span style="color: #a31515;">" Height (px): "</span> & PsDoc.Height * PsDoc.Resolution / 2.54<br /> <br /> <span style="color: green;">'Apply the Sharpen filter.</span><br /> PsDoc.ArtLayers(1).ApplySharpen<br /> <br /> <span style="color: green;">'Apply the Gaussian Blur filter within the specified radius (in pixels). Valid range: 0.1 - 250.0.</span><br /> PsDoc.ArtLayers(1).ApplyGaussianBlur 5<br /> <br /> <span style="color: green;">'Create a new PNGSaveOptions object that will store the necessary saving parameters.</span><br /> <span style="color: blue;">Set</span> PsSaveOptions = <span style="color: blue;">New</span> Photoshop.PNGSaveOptions<br /> <br /> <span style="color: green;">'Set the compression of the image. Valid range: 0 - 9, default: 0.</span><br /> PsSaveOptions.Compression = 5<br /> <br /> <span style="color: green;">'Set the Interlaced option, which indicates whether the rows should interlace. Default value: false.</span><br /> PsSaveOptions.Interlaced = <span style="color: blue;">True</span><br /> <br /> <span style="color: green;">'Save the modified image using the defined saving options.</span><br /> PsDoc.SaveAs OutputImagePath, PsSaveOptions, <span style="color: blue;">True</span>, PsExtensionType.psLowercase <span style="color: green;">'PsExtensionType.psLowercase = 2 in late binding</span><br /> <br /> <span style="color: green;">'Close the image.</span><br /> PsDoc.Close psDoNotSaveChanges <span style="color: green;">'psDoNotSaveChanges = 2 in late binding</span><br /> <br /> <span style="color: green;">'Quit Photoshop.</span><br /> PsApp.Quit<br /> <br /> <span style="color: green;">'Release the objects.</span><br /> <span style="color: blue;">Set</span> PsSaveOptions = <span style="color: blue;">Nothing</span><br /> <span style="color: blue;">Set</span> PsDoc = <span style="color: blue;">Nothing</span><br /> <span style="color: blue;">Set</span> PsApp = <span style="color: blue;">Nothing</span><br /> <br /> <span style="color: green;">'Inform the user about the process.</span><br /> <span style="color: blue;">If</span> FileExists(OutputImagePath) = <span style="color: blue;">True</span> <span style="color: blue;">Then</span><br /> MsgBox <span style="color: #a31515;">"The output image was successfully created!"</span>, vbInformation, <span style="color: #a31515;">"Finished"</span><br /> <span style="color: blue;">Else</span><br /> MsgBox <span style="color: #a31515;">"The output image was not created!"</span>, vbCritical, <span style="color: #a31515;">"Output Image Error"</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /><br /><span style="color: blue;">End</span> <span style="color: blue;">Sub</span><br /><br /><span style="color: blue;">Function</span> FileExists(FilePath <span style="color: blue;">As</span> <span style="color: #2b91af;">String</span>) <span style="color: blue;">As</span> <span style="color: #2b91af;">Boolean</span><br /> <br /> <span style="color: green;">'--------------------------------------------------</span><br /> <span style="color: green;">'Checks if a file exists (using the Dir function).</span><br /> <span style="color: green;">'--------------------------------------------------</span><br /><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <span style="color: blue;">If</span> Len(FilePath) > 0 <span style="color: blue;">Then</span><br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> Dir(FilePath, vbDirectory) = vbNullString <span style="color: blue;">Then</span> FileExists = <span style="color: blue;">True</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> 0<br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /></pre><br /><br /><div id="results" style="text-align: justify;"><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;"><b>Example</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><div style="text-align: justify;"><br /></div><div style="text-align: justify;">The picture below is the input image in this example:<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-rIzM2F_8Rfc/Wf5AaH7AqgI/AAAAAAAADm0/_LQuVfAwUoY-MrUu89AMBHeaneuMB5OhgCLcBGAs/s1600/Input.jpg" style="margin-left: 1em; margin-right: 1em;"><img alt="Input Image - Before Processing" border="0" data-original-height="1200" data-original-width="1600" height="480" src="https://1.bp.blogspot.com/-rIzM2F_8Rfc/Wf5AaH7AqgI/AAAAAAAADm0/_LQuVfAwUoY-MrUu89AMBHeaneuMB5OhgCLcBGAs/s640/Input.jpg" title="Input Image - Before Processing" width="640" /></a></div><br /><div style="text-align: center;"><span style="color: orange;">Input image</span>: 4760 x 3120 pixels, jpg format. Click the image to view it in real size.</div><br />And here is the output image, after running the code:<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://4.bp.blogspot.com/-dS-I7PkUQLM/Wf5AwSjvkmI/AAAAAAAADm4/Nehyh7FZfFkDZDsb-Hu5aFPb1ic2u6YQwCLcBGAs/s1600/Output.png" style="margin-left: 1em; margin-right: 1em;"><img alt="Output Image - After Processing" border="0" data-original-height="563" data-original-width="750" height="480" src="https://4.bp.blogspot.com/-dS-I7PkUQLM/Wf5AwSjvkmI/AAAAAAAADm4/Nehyh7FZfFkDZDsb-Hu5aFPb1ic2u6YQwCLcBGAs/s640/Output.png" title="Output Image - After Processing" width="640" /></a></div><br /><div style="text-align: center;"><span style="color: orange;">Output image:</span> 750 x 563 pixels, png format, with Sharpen and Gaussian Blur filter.</div><br />The result is neither fancy nor impressive (this wasn't my intent). However, it just shows some possible applications of VBA programming in respect to Photoshop.</div><div style="text-align: justify;"><br /></div><br /><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;"><b>Downloads</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://goo.gl/7FMQrC"><img alt="Download" border="0" height="108" src="https://4.bp.blogspot.com/-JhKlqA-EMkM/UoVNR1_v4II/AAAAAAAABtM/Q1GE7I4707g/s1600/Download.jpg" title="Click to download the file" width="320" /></a></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">The <span style="color: red;">zip file</span> contains a workbook with the VBA code and a sample image to play with it (the one that used in this example). The workbook can be opened with <span style="color: red;">Excel 2007</span> or newer. Please enable macros before using it.</div><div style="text-align: justify;"></div></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-47256611632361433552015-09-20T01:49:00.000+03:002018-05-27T20:58:16.787+03:00Road Transport Emissions Evolution In Urban Areas; The Case Of Thessaloniki, Greece<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;"><div class="separator" style="clear: both; text-align: center;"><img alt="Road Transport Emissions Evolution In Urban Areas; The Case Of Thessaloniki, Greece" border="0" src="https://4.bp.blogspot.com/-RIMUR8PClVk/Vf6ZLPi8_2I/AAAAAAAADbY/fmQhI5xQnj4/s1600/Road%2BTransport%2BEmissions%2BEvolution%2BIn%2BUrban%2BAreas%253B%2BThe%2BCase%2BOf%2BThessaloniki%252C%2BGreece.jpg" title="Road Transport Emissions Evolution In Urban Areas; The Case Of Thessaloniki, Greece" /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">About</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div><div style="text-align: justify;">First of all, it seems that in previous months <a href="http://www.myengineeringworld.net/2015/03/email-and-comments-policy-blog-related.html">I couldn’t fulfill my own promises</a> regarding this blog. I have an extremely heavy workload the last few months and it seems that until I present my thesis, I will not be able to update this blog regularly. <span style="color: red;">So, dear blog readers please be patient!</span><br /><br />Anyway, in the paragraphs that follow you will find a text version of my latest poster, which was presented at the <a href="http://cest2015.gnest.org/">14th International Conference on Environmental Science and Technology</a>. The conference took place in Rhodes (Greece) during the first week of September. The poster is actually a summary of the corresponding paper. If you are interested in road transport emissions you might find it interesting! </div><div style="text-align: justify;"><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Overview</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><span style="color: red;"><span style="font-size: small;"><u><b>Abstract</b></u></span></span><br /><br /><ul><li><span style="color: orange;">The current study examines the evolution of road transport emissions during the years 2012 – 2014 in Thessaloniki, the second largest city of Greece. The study focuses on the calculation at micro/link level of CO, NOx and VOC emissions.</span></li><li>The developed methodology was based on the combination of PTV VISUM and <a href="http://www.myengineeringworld.net/2013/02/copert-micro-tool-to-calculate-vehicle.html">COPERT Micro</a> models, along with measured traffic data from inductive loop detectors, cameras and radars installed at 37 locations across the city.</li><li>Results indicated the local pollution hot-spots, as well as the local and seasonal variation of pollutants. In general, there is a substantial drop in average annual emissions between 2012 and 2013, whereas a constant trend is observed between 2013 and 2014.</li></ul><br /><span style="color: red;"><u><b>Introduction</b></u></span></div><br /><ul><li>According to the latest census (2011), Thessaloniki is the second largest city in Greece, both in population and area.</li><li>Based on the latest transportation study conducted between 2010 and 2013, the average daily private vehicle traffic on the main roads of the city reaches 1.300.000 vehicle-trips, while the morning peak corresponds to 14% of the daily total. </li><li>The main streets in the central area of the city serve daily volumes of through traffic that reach 45% of traffic volumes recorded at peak periods (morning travel to work and afternoon travel back from work to home).</li></ul><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Materials & Methods</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Flow Chart Of The Methodology" border="0" src="https://2.bp.blogspot.com/-VAzR-GHP6Lw/Vf6Zq9vr71I/AAAAAAAADbg/NwggB2Qb50c/s1600/Flow%2BChart%2BOf%2BThe%2BMethodology.jpg" title="Flow Chart Of The Methodology" /></div><br /><div style="text-align: center;">Flowchart of the methodology.</div><br /><span style="color: red;"><span style="font-size: small;"><u><b>Traffic Modelling</b></u></span></span><br /><br /><ul><li>The transportation model for Thessaloniki has been developed with the <span style="color: orange;">PTV VISUM</span> software. The network consists of 137.938 directed links, 47.838 nodes and 339 traffic analysis zones connected to physical nodes of the road network via 3.508 connectors. </li><li>The demand side is comprised by 24 hourly Origin-Destination (OD) matrices and the travel demand for a typical weekday is within the range of 1.298.745 vehicle trips. </li><li>The obtained OD matrices are corrected using the hourly volume data measured by inductive loop detectors, cameras and radars installed at 37 locations across the city. The OD matrix correction is performed with a fuzzy-set based matrix correction procedure.</li></ul><br /><span style="color: red;"><u><b>Emissions Modelling</b></u></span><br /><br /><ul><li>The emissions calculations were performed with <span style="color: orange;">COPERT Micro</span>, a specially developed version of COPERT 4 for urban areas. It is a bottom-up model, thus, it can calculate the emissions from a single traffic link up to an entire city, focusing primarily on hot exhaust emissions. </li><li>The equations below summarize the main methodology that is applied by COPERT Micro: <br /> - <span style="color: orange;">EF<span style="font-size: xx-small;">i</span>(V)= f<span style="font-size: xx-small;">EF</span> (V)</span> : emission factor as a function of average vehicle speed<br /> - <span style="color: orange;">Emissions<span style="font-size: xx-small;">i, j</span> = L<span style="font-size: xx-small;">j</span> × N<span style="font-size: xx-small;">j</span> × P<span style="font-size: xx-small;">vehcile category, j</span> × EF<span style="font-size: xx-small;">i</span>(V)</span> : emissions due to a single traffic link<br /> - <span style="color: orange;">Emissions<span style="font-size: xx-small;">i, area</span> = ∑Emissions<span style="font-size: xx-small;">i, j</span></span> : emissions from the entire area</li></ul><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Results & Conclusions</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><span style="color: red;"><span style="font-size: small;"><u><b>Results</b></u></span></span><br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Daily CO Hot Emissions In Thessaloniki For Years 2012 & 2014" border="0" src="https://4.bp.blogspot.com/-_QssoZYkZog/Vf6aWmYSNfI/AAAAAAAADbs/e72WypY-VA4/s1600/Daily%2BCO%2BHot%2BEmissions%2BIn%2BThessaloniki%2BFor%2BYears%2B2012%2B%2526%2B2014.jpg" title="Daily CO Hot Emissions In Thessaloniki For Years 2012 & 2014" /></div><br />Daily CO hot emissions (kg) on a 100 x 100 (500 x 500 m2) grid for a typical weekday of summer 2012 (left) and 2014 (right) in the metropolitan area of Thessaloniki.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Seasonal Hourly NOx Hot Emissions In Thessaloniki For 2012" border="0" src="https://1.bp.blogspot.com/-kfvzD_H89Lg/Vf6f8qKBZmI/AAAAAAAADb8/I5mxHEuxVBg/s1600/Seasonal%2BHourly%2BNOx%2BHot%2BEmissions%2BIn%2BThessaloniki%2BFor%2B2012.jpg" title="Seasonal Hourly NOx Hot Emissions In Thessaloniki For 2012" /></div><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Seasonal Hourly NOx Hot Emissions In Thessaloniki For 2014" border="0" src="https://4.bp.blogspot.com/-LH5Vg8qS2o4/Vf6gFhIxECI/AAAAAAAADcE/HTS0f6oKGjg/s1600/Seasonal%2BHourly%2BNOx%2BHot%2BEmissions%2BIn%2BThessaloniki%2BFor%2B2014.jpg" title="Seasonal Hourly NOx Hot Emissions In Thessaloniki For 2014" /></div><br />Hourly NOx hot emissions (kg) for a typical weekday of each season of 2012 (up) and 2014 (down) in the metropolitan area of Thessaloniki.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Seasonal Daily VOC Hot Emissions In Thessaloniki For Years 2012 To 2014" border="0" src="https://3.bp.blogspot.com/-dS87P_EyHLM/Vf6g2bZgrLI/AAAAAAAADcM/jUg8b8qbq0I/s1600/Seasonal%2BDaily%2BVOC%2BHot%2BEmissions%2BIn%2BThessaloniki%2BFor%2BYears%2B2012%2BTo%2B2014.jpg" title="Seasonal Daily VOC Hot Emissions In Thessaloniki For Years 2012 To 2014" /></div><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Average Daily VOC Hot Emissions In Thessaloniki Per Year (2012 - 2014)" border="0" src="https://3.bp.blogspot.com/-5SCZUs0frSo/Vf6g92WNDzI/AAAAAAAADcU/S2fJmVVw53Y/s1600/Average%2BDaily%2BVOC%2BHot%2BEmissions%2BIn%2BThessaloniki%2BPer%2BYear%2B%25282012%2B-%2B2014%2529.jpg" title="Average Daily VOC Hot Emissions In Thessaloniki Per Year (2012 - 2014)" /></div><br />Daily VOC hot emissions (t) for a typical weekday of each season (up) and each year (down) of years 2012 – 2014 in the metropolitan area of Thessaloniki.<br /><br /><span style="color: red;"><span style="font-size: small;"><u><b>Conclusions</b></u></span></span><br /><br /><ul><li><span style="color: orange;">The results at link level revealed the local pollution hot-spots and the high-emission links, which usually lie along the main urban highways of the city. </span></li><span style="color: orange;"> </span><li><span style="color: orange;">The seasonal variation of pollutants is mainly caused by the different transport activity pattern in each season. </span></li><span style="color: orange;"> </span><li><span style="color: orange;">There is a significant drop in average annual emissions between 2012 and 2013, while a constant trend is observed between 2013 and 2014. This could be allocated to the reduced transport activity throughout the city, mainly affected by the economic crisis.</span></li></ul></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-74943069103994265122015-04-30T23:58:00.000+03:002017-09-27T02:54:36.456+03:00CodeEval 3 – Reverse And Add<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;"></div><div style="text-align: justify;"><div class="separator" style="clear: both; text-align: center;"><img alt="CodeEval 3 – Reverse And Add" border="0" src="https://4.bp.blogspot.com/-egYWI3DhWTk/VUOjzh0A4RI/AAAAAAAADXY/iuhdu2rfAG8/s1600/CodeEval%2B3%2B%E2%80%93%2BReverse%2BAnd%2BAdd.jpg" title="CodeEval 3 – Reverse And Add" /></div><br /></div><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>About CodeEval post series</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;">CodeEval is a series of posts which are different than the typical engineering/Excel/VBA posts that are being published in this blog. <span style="color: red;">The purpose of this series is to demonstrate possible solutions to various <a href="https://www.codeeval.com/">CodeEval</a> programming challenges.</span> Each solution has already been submitted and accepted as valid on CodeEval platform, so if you try to submit the presented solution as it is, you will probably get a <i>“not unique solution”</i> result. <span style="color: orange;">The solutions will be presented in C# language, but the logic/algorithm behind them is similar despite the language you might use.</span></div><div style="text-align: justify;"><br /><br /></div><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Reverse And Add – challenge description</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><span style="color: orange;">Choose a number, reverse its digits and add it to the original. If the sum is not a palindrome (which means, it is not the same number from left to right and right to left), repeat this procedure.</span></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">Example:</div><div style="text-align: justify;"><br /></div><div style="text-align: center;"><span style="color: orange;">195 (initial number) + 591 (reverse of initial number) = 786</span></div><div style="text-align: center;"><span style="color: orange;"><br /></span></div><div style="text-align: center;"><span style="color: orange;">786 + 687 = 1473</span></div><div style="text-align: center;"><span style="color: orange;"><br /></span></div><div style="text-align: center;"><span style="color: orange;">1473 + 3741 = 5214</span></div><div style="text-align: center;"><span style="color: orange;"><br /></span></div><div style="text-align: center;"><span style="color: orange;">5214 + 4125 = 9339 (palindrome)</span></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">In this particular case, the palindrome 9339 appeared after the 4th addition. This method leads to palindromes in a few step for almost all of the integers. But there are interesting exceptions. 196 is the first number for which no palindrome has been found. It is not proven though, that there is no such a palindrome.</div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><span style="color: red;"><u>Input sample</u></span></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">Your program should accept as its first argument a path to a file name. Each line in this file is one test case. Each test case will contain an integer n < 10,000. Assume each test case will always have an answer and that it is computable with less than 100 iterations (additions).</div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><span style="color: red;"><u>Output sample</u></span></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"></div><div style="text-align: justify;">For each line of input, generate a line of output which is the number of iterations (additions) to compute the palindrome and the resulting palindrome. (they should be on one line and separated by a single space character). Example:</div><div style="text-align: justify;"><br /></div><div style="text-align: center;"><span style="color: orange;">4 9339 </span></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">The particular challenge has a relatively low success rate (66.6% - 30/04/2015), and its level of difficulty is medium. More info you can find <a href="https://www.codeeval.com/open_challenges/45/">here</a>.</div><div style="text-align: justify;"><br /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Solution</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;">Apart from the main code, the solution incorporates two helping methods for reversing the input number ("ReverseNumber") and for checking if a number is a palindrome ("IsPalindrome").<br /><br /><pre><span style="color: blue;">using</span> System;<br /><span style="color: blue;">using</span> System.Collections.Generic;<br /><span style="color: blue;">using</span> System.IO;<br /><span style="color: blue;">using</span> System.Linq;<br /><span style="color: blue;">using</span> System.Text;<br /><br /><span style="color: green;">/*<br />------------------------------------------------------------------<br />The code below solves the CodeEval challenge - Reverse And Add.<br /><br />Written by: Christos Samaras<br />Date: 02/07/2014<br />e-mail: xristos.samaras@gmail.com<br />site: http://www.myengineeringworld.net<br />------------------------------------------------------------------<br />*/</span><br /><br /><span style="color: blue;">namespace</span> ReverseAndAdd<br />{<br /> <span style="color: blue;">class</span> Program<br /> {<br /> <span style="color: blue;">static</span> <span style="color: blue;">void</span> Main(<span style="color: blue;">string</span>[] args)<br /> {<br /> {<br /> <span style="color: blue;">using</span> (StreamReader reader = File.OpenText(args[<span style="color: maroon;">0</span>])) <br /> <span style="color: blue;">while</span> (!reader.EndOfStream)<br /> {<br /> <span style="color: blue;">string</span> line = reader.ReadLine();<br /> <span style="color: blue;">if</span> (line != <span style="color: blue;">null</span>)<br /> {<br /> <span style="color: blue;">int</span> cnt = <span style="color: maroon;">0</span>;<br /><br /> <span style="color: blue;">while</span> (IsPalindrome(line) == <span style="color: maroon;">false</span>)<br /> {<br /> <span style="color: blue;">int</span> reversedNumber = <span style="color: blue;">int</span>.Parse(ReverseNumber(line));<br /> line = (<span style="color: blue;">int</span>.Parse(line) + reversedNumber).ToString();<br /> cnt++;<br /> }<br /> Console.WriteLine(cnt + <span style="color: maroon;">" "</span> + line);<br /> } <br /> }<br /> Console.ReadLine();<br /> } <br /> }<br /> <br /> <span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">string</span> ReverseNumber(<span style="color: blue;">string</span> number)<br /> {<br /> <span style="color: blue;">char</span>[] charArray = number.ToCharArray();<br /> Array.Reverse(charArray);<br /> <span style="color: blue;">return</span> <span style="color: blue;">new</span> <span style="color: blue;">string</span>(charArray);<br /> }<br /><br /> <span style="color: blue;">public</span> <span style="color: blue;">static</span> <span style="color: blue;">bool</span> IsPalindrome(<span style="color: blue;">string</span> num)<br /> {<br /> <span style="color: blue;">if</span> (num == ReverseNumber(num))<br /> <span style="color: blue;">return</span> <span style="color: maroon;">true</span>;<br /> <span style="color: blue;">else</span><br /> <span style="color: blue;">return</span> <span style="color: maroon;">false</span>;<br /> }<br /> }<br />}</pre><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Points</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br />Here is the proof that the <span style="color: red;">solution works</span> and the points given by the CodeEval platform.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="CodeEval 3 – Reverse And Add - Score" border="0" height="178" src="https://1.bp.blogspot.com/-ugGdQI1IhP0/VUOsmRehI1I/AAAAAAAADXo/hlETQjxmWt0/s1600/CodeEval%2B3%2B%E2%80%93%2BReverse%2BAnd%2BAdd%2B-%2BScore.jpg" title="CodeEval 3 – Reverse And Add - Score" width="640" /></div></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-15426822472794782422015-04-19T21:53:00.000+03:002017-03-16T19:14:54.470+02:00Automatically Adjust Secondary Y Axis Scale Through VBA<div dir="ltr" style="text-align: left;" trbidi="on"><div class="separator" style="clear: both; text-align: center;"><img alt="Automatically Adjust Secondary Y Axis Scale Through VBA" border="0" src="https://4.bp.blogspot.com/-dZaCUzrNR-0/VTPxXqZVEbI/AAAAAAAADVo/n8Yc6Q6dCyI/s1600/Automatically%2BAdjust%2BSecondary%2BY%2BAxis%2BScale%2BThrough%2BVBA.jpg" title="Automatically Adjust Secondary Y Axis Scale Through VBA" /></div><div><br /></div><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><br /><div style="text-align: justify;">Some days ago, while I was checking the discussions on an Excel-related group on LinkedIn, I bumped into an interesting question about charts. The question was <span style="color: red;">“<i>how to automatically set the secondary Y axis scale via code, so that both primary and secondary axes share the same number of gridlines</i>”. </span><br /><br />I thought this question was interesting for two reasons: first, it’s quite common to include a secondary Y axis on a chart, just to display another series that have different scale than the first one. In <a href="http://www.myengineeringworld.net/2013/02/copert-micro-tool-to-calculate-vehicle.html">COPERT Micro tool</a> for example, I have many charts that show emissions (primary Y) and number of vehicles (secondary Y) as a function of hour of the day (X axis). Second, the question reminded me a similar need that I had some years ago. So, I found the code that I had written then (2012) and I made some minor changes in order to make it more generic, and, here it is!<br /><br /><div style="text-align: justify;"><br /></div><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3 style="text-align: justify;"><span style="color: #3d85c6;"><b>The manual way</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><br />By the way, it’s not hard to adjust the scale of secondary Y axis manually. Just follow the instructions that follow (for Excel 2013).<br /><br /><span style="color: red;"><b><u>Step 1:</u></b></span> Set a data series to be plotted on secondary axis: <br /><ol><li>Select a series on the chart by right clicking on it.</li><li>On the pop up menu select <span style="color: orange;">Format Data Series</span>. </li><li>On the Format Data Series menu that will appear select the <span style="color: orange;">Series Options</span> tab. </li><li>Select the plot on <span style="color: orange;">secondary axis</span> radio button.</li></ol><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Format Data Series Menu" border="0" src="https://2.bp.blogspot.com/-zJ1G157oBmw/VTPyXG9WN4I/AAAAAAAADVw/IGJesWT9LC0/s1600/Format%2BData%2BSeries%2BMenu.jpg" title="Format Data Series Menu" /><img alt="Plot Series On Secondary Axis" border="0" src="https://4.bp.blogspot.com/-twoAuiisRho/VTPyfYto59I/AAAAAAAADV4/yyr-sghbQRs/s1600/Plot%2BSeries%2BOn%2BSecondary%2BAxis.jpg" title="Plot Series On Secondary Axis" /></div><br /><span style="color: red;"><b><u>Step 2:</u></b></span> Adjust the scale of the secondary Y axis:<br /><ol><li>Select the secondary Y axis by right clicking on it.</li><li>On the pop up menu select <span style="color: orange;">Format Axis</span>. </li><li>On the Format Axis menu that will appear select the <span style="color: orange;">Axis Options</span> tab. </li><li>Finally, set the desired values on <span style="color: orange;">Minimum/Maximum Bounds and on Major Unit</span> text boxes.</li></ol><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Format Secondary Y Axis" border="0" src="https://4.bp.blogspot.com/-eGeTo_Txm7I/VTPzV8D8McI/AAAAAAAADWA/Vg-eG16ZDPI/s1600/Format%2BSecondary%2BY%2BAxis.jpg" title="Format Secondary Y Axis" /><img alt="Set Secondary Y Axis Scale" border="0" src="https://3.bp.blogspot.com/-1Cd8AfZ3Peg/VTPzb7xf6vI/AAAAAAAADWI/q2CnKNKUM3k/s1600/Set%2BSecondary%2BY%2BAxis%2BScale.jpg" title="Set Secondary Y Axis Scale" /></div><br /><span style="color: red;">In the last step you might need to make some (repeating) tests </span>on the values that you will enter in the 3 text boxes (especially in the Major Unit text box) in order to achieve that both primary and secondary axes will share the same number of gridlines.<br /><br /><div style="text-align: justify;"><br /></div><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3 style="text-align: justify;"><span style="color: #3d85c6;"><b>VBA code</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><br /><span style="color: red;">And here is the “easy way”; no need to test the values on the text boxes, no worries about how the axes will look uniformly.</span> It’s a short macro that can be customized according to your needs.</div><br /><pre><span style="color: blue;">Option Explicit</span><br /><br /><span style="color: blue;">Sub</span> AdjustSecondaryYAxisScale()<br /> <br /> <span style="color: green;">'--------------------------------------------------------------------------</span><br /> <span style="color: green;">'Automatically adjusts the scale of the secondary Y axis, so that both</span><br /> <span style="color: green;">'primary and secondary Y axes share the same number of (major) gridlines.</span><br /> <br /> <span style="color: green;">'Written By: Christos Samaras</span><br /> <span style="color: green;">'Date: 14/09/2012</span><br /> <span style="color: green;">'Last Update: 18/04/2015</span><br /> <span style="color: green;">'E-mail: xristos.samaras@gmail.com</span><br /> <span style="color: green;">'Site: http://www.myengineeringworld.net</span><br /> <span style="color: green;">'--------------------------------------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> ch <span style="color: blue;">As</span> Chart<br /> <span style="color: blue;">Dim</span> Ymin <span style="color: blue;">As</span> <span style="color: blue;">Double</span><br /> <span style="color: blue;">Dim</span> Ymax <span style="color: blue;">As</span> <span style="color: blue;">Double</span><br /> <span style="color: blue;">Dim</span> Yscale <span style="color: blue;">As</span> <span style="color: blue;">Double</span><br /> <span style="color: blue;">Dim</span> Ylines <span style="color: blue;">As</span> <span style="color: blue;">Integer</span><br /> <span style="color: blue;">Dim</span> sYmin <span style="color: blue;">As</span> <span style="color: blue;">Double</span><br /> <span style="color: blue;">Dim</span> sYmax <span style="color: blue;">As</span> <span style="color: blue;">Double</span><br /> <span style="color: blue;">Dim</span> sYscale <span style="color: blue;">As</span> <span style="color: blue;">Double</span><br /> <br /> <span style="color: green;">'Set the ch variable to a specific chart on sheet 1 (in this example).</span><br /> <span style="color: green;">'If you need to set the ch variable to active chart you can use the next line:</span><br /> <span style="color: green;">'Set ch = ActiveChart</span><br /> <span style="color: blue;">Set</span> ch = Sheet1.ChartObjects(<span style="color: maroon;">1</span>).Chart<br /> <br /> <span style="color: green;">'A quick test if the ch variable is not empty.</span><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <span style="color: blue;">If</span> ch <span style="color: blue;">Is</span> <span style="color: blue;">Nothing</span> <span style="color: blue;">Then</span><br /> MsgBox <span style="color: maroon;">"The chart wasn't set!"</span>, vbCritical, <span style="color: maroon;">"Empty Chart"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> <span style="color: maroon;">0</span><br /> <br /> <span style="color: green;">'Set the minimum/maximum bound and the major unit to Auto for both primary and secondary axes.</span><br /> <span style="color: green;">'For the primary Y axis this is NOT always necessary, so the corresponding 3 lines can be deleted/commented.</span><br /> <span style="color: blue;">With</span> ch<br /> .Axes(xlValue).MinimumScaleIsAuto = <span style="color: maroon;">True</span><br /> .Axes(xlValue).MaximumScaleIsAuto = <span style="color: maroon;">True</span><br /> .Axes(xlValue).MajorUnitIsAuto = <span style="color: maroon;">True</span><br /> .Axes(xlValue, xlSecondary).MinimumScaleIsAuto = <span style="color: maroon;">True</span><br /> .Axes(xlValue, xlSecondary).MaximumScaleIsAuto = <span style="color: maroon;">True</span><br /> .Axes(xlValue, xlSecondary).MajorUnitIsAuto = <span style="color: maroon;">True</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">With</span><br /> <br /> <span style="color: green;">'Get the mininmum bound of the primary Y axis.</span><br /> Ymin = ch.Axes(xlValue).MinimumScale<br /> <br /> <span style="color: green;">'Get the maximum bound of the primary Y axis.</span><br /> Ymax = ch.Axes(xlValue).MaximumScale<br /> <br /> <span style="color: green;">'Get the major unit of the primary Y axis.</span><br /> Yscale = ch.Axes(xlValue).MajorUnit<br /> <br /> <span style="color: green;">'Calculate the number of major gridlines.</span><br /> Ylines = Round((Ymax - Ymin) / Yscale)<br /> <br /> <span style="color: green;">'Get the mininmum bound of the secondary Y axis.</span><br /> sYmin = ch.Axes(xlValue, xlSecondary).MinimumScale<br /> <br /> <span style="color: green;">'Get the maximum bound of the secondary Y axis.</span><br /> sYmax = ch.Axes(xlValue, xlSecondary).MaximumScale<br /> <br /> <span style="color: green;">'Note that you can easily set the minimum/maximum bound of the secondary Y axis to any value you like.</span><br /> <span style="color: green;">'In other words, you can make your secondary Y axis look exactly as you want.</span><br /> <span style="color: green;">'For example if you uncomment the next 2 lines, the first gridline of the secondary Y axis will start at 0 and</span><br /> <span style="color: green;">'the last one will end at the value of 30. The number of gridlines will be automatically calculated/adjusted.</span><br /> <span style="color: green;">'sYmin = 0</span><br /> <span style="color: green;">'sYmax = 30</span><br /> <br /> <span style="color: green;">'Calculate the new major unit of the secondary Y axis.</span><br /> sYscale = Round((sYmax - sYmin) / Ylines)<br /> <br /> <span style="color: green;">'Calculate the new maximum bound of the secondary Y axis.</span><br /> sYmax = sYmin + Ylines * sYscale<br /> <br /> <span style="color: green;">'Set the minimum/maximum bound and the major unit of the secondary Y axis to their new values.</span><br /> <span style="color: blue;">With</span> ch.Axes(xlValue, xlSecondary)<br /> .MinimumScale = sYmin<br /> .MaximumScale = sYmax<br /> .MajorUnit = sYscale<br /> <span style="color: blue;">End</span> <span style="color: blue;">With</span><br /> <br /> <span style="color: green;">'Release the chart object.</span><br /> <span style="color: blue;">Set</span> ch = <span style="color: blue;">Nothing</span><br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Sub</span></pre><br /><div style="text-align: justify;">In the workbook that you will find in the downloads section below you can try the above code by experimenting on a chart that I created using random numbers. <span style="color: red;">Just see how nice the scale of the secondary Y axis "follows" the scale of the primary Y axis. </span></div><br /><div style="text-align: justify;"><br /></div><div id="downloads" style="text-align: justify;"><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;"><b>Downloads</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /></div><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://goo.gl/V3WEXW"><img alt="Download" border="0" height="108" src="https://4.bp.blogspot.com/-JhKlqA-EMkM/UoVNR1_v4II/AAAAAAAABtM/Q1GE7I4707g/s1600/Download.jpg" title="Click to download the file" width="320" /></a></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">The file can be opened with <span style="color: red;">Excel 2007</span> or newer. Please enable macros before using it. </div><div style="text-align: justify;"></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-34174097726386319232015-03-23T00:26:00.001+02:002018-12-08T03:15:32.049+02:00Email Engineering, Comments Policy, Blog-Related Stuff & My Free Time<div dir="ltr" style="text-align: left;" trbidi="on"><div class="separator" style="clear: both; text-align: center;"><img alt="Email Engineering, Comments Policy, Blog Related Stuff & My Free Time" border="0" src="https://4.bp.blogspot.com/-LlDdjsAyBn0/VQ80Z9uko7I/AAAAAAAADS8/lrXmXgbOiqc/s1600/Email%2BEngineering%2C%2BComments%2BPolicy%2C%2BBlog%2BRelated%2BStuff%2B%26%2BMy%2BFree%2BTime.jpg" title="Email Engineering, Comments Policy, Blog Related Stuff & My Free Time" /></div><div style="text-align: justify;"></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction </span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br />I know that the title might look strange to most of you, nevertheless, in this post I will try to write about a few things that were spinning around my head for a long time, but, until now, I couldn’t find the time to write about them. The topics that are analyzed below are:</div><br /><ol style="text-align: left;"><li><a href="http://www.myengineeringworld.net/2015/03/email-and-comments-policy-blog-related.html#email-engineering">“Email engineering” and new email policy</a></li><li><a href="http://www.myengineeringworld.net/2015/03/email-and-comments-policy-blog-related.html#comments-policy">New comments policy</a></li><li><a href="http://www.myengineeringworld.net/2015/03/email-and-comments-policy-blog-related.html#blog-related">Blog-related stuff</a></li><li><a href="http://www.myengineeringworld.net/2015/03/email-and-comments-policy-blog-related.html#free-time">My free time</a></li></ol><div style="text-align: justify;"><br />So, as you might have already guessed, this post differs from the typical engineering/programming subjects that are being published in this blog. <span style="color: red;">It’s a long and a little bit personal post, so, if you don’t want to read it all, you can only read the key points at each section.</span> However, if you like this blog, I think that you should read the entire post; you might find it interesting.<br /> </div><br /><hr color="#3d85c6" id="email-engineering" size="1" width="100%" /><h3><span style="color: #3d85c6;">1. “Email engineering” and new email policy</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Email Engineering" border="0" src="https://3.bp.blogspot.com/-cTm3or25jiQ/VQ80wEEo_II/AAAAAAAADTE/Du7ym5_jLhI/s1600/Email%2BEngineering.jpg" title="Email Engineering" /></div><div style="text-align: justify;"><br />The term “email engineering” is an inside joke that my colleagues and I use to express the time that someone spends with a view to send – usually quite long – emails. A typical dialog at my work would be like this:<br /><br />- Me: Hey, what’s going on? You look a little bit upset.<br />- Colleague: Well, I spent all morning with “email engineering, ” and now I have to speed up to finish the presentation on time.<br /><br /><span style="color: orange;">In other words, the term “email engineering” expresses the “unproductive” or “counter-intuitive” time that is wasted for sending (long) emails.</span> Unfortunately, during the last summer, I realized that I do a lot of “email engineering” due to this blog. On January, I counted the number of emails that I received the last three years, and then I estimated the time that I spent to respond to these emails. The picture below shows the results.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Email Requests Per Year" border="0" src="https://4.bp.blogspot.com/-7nK11Yz7ud0/VQ83TavFh9I/AAAAAAAADTo/eo0yWSvXl18/s1600/Email%2BRequests%2BPer%2BYear.jpg" title="Email Requests Per Year" /></div><br />So, only last year I received 229 email requests! That means that I wrote at least 300 emails since in many cases one email was not enough to solve the problem. By making the quite conservative assumption of 1 email request = 10 minutes,<span style="color: red;"> I ended up with a total time of about 38 hours (minimum).</span> Wow! I was shocked when I saw this number! I spent an entire working week by just writing emails! That can’t be true!!!<br /><br />The above helped me to realize that I have to change my email policy. Until now, I respond to every email that I receive, although my answer sometimes might take a few days/weeks to be sent. <span style="color: orange;">This will be tough to write, but, from now on I will no longer reply to all the email requests that I receive.</span> I encourage you to visit the new <a href="http://www.myengineeringworld.net/p/frequently-asked-questions-faq.html#email-requirements">FAQ page</a> to read the requirements for getting a response from me. I like to receive feedback from all of you. However, from now on, if your emails don’t follow the rules, they will just be ignored.<br /><br /><span style="color: red;">Key points on “email engineering” and new email policy:</span><br />Fact 1: I enjoy to communicate with all of you.<br />Fact 2: Honestly, I wish I could solve all the engineering/programming problems that you send me.<br />Fact 3: 38+ hours for answering emails is way too much for me!<br />Result: New stricter email policy that will reduce the “email engineering” time.</div><br /><br /><hr color="#3d85c6" id="comments-policy" size="1" width="100%" /><h3><span style="color: #3d85c6;">2. New comments policy</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Comments Policy" border="0" src="https://4.bp.blogspot.com/-tfgX5GqRkik/VQ81B75ObrI/AAAAAAAADTM/AYRPCjG8Mfs/s1600/Comments%2BPolicy.jpg" title="Comments Policy" /></div><br /><div style="text-align: justify;">About two years ago, on the <a href="http://www.myengineeringworld.net/2013/05/2-years-birthday-blog-renewal.html">last major design update of this blog</a>, I switched the commenting system from built-in Blogger comments to Disqus platform. With Disqus I didn’t follow any particular policy for comments; I used to approve every comment, except for spam ones. However, last summer, after having some indications of increasing “email engineering” time, I silently changed the comments policy. <span style="color: orange;">So, the comments for every new post will stay open for only 60 days AFTER the post-publication.</span><br /><br />Why I did that? Well, I adopted this policy because I was receiving similar comments again and again. Some people don’t even read the entire post, and they just ask questions on the comments; issues that have been already answered in the post. Moreover, in the most popular posts, a lot of people keep asking questions that were answered a few comments below. I decided that this should stop!<br /><br /><span style="color: red;">Key points on new comments policy:</span><br />Fact 1: People don’t even read the posts and just asking questions.<br />Fact 2: Repeated comments especially in popular and old posts.<br />Result: Comments will be open for only 60 days after post-publication.</div><br /><br /><hr color="#3d85c6" id="blog-related" size="1" width="100%" /><h3><span style="color: #3d85c6;">3. Blog-related stuff </span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Blog Related Stuff" border="0" src="https://3.bp.blogspot.com/-dEMQZeruIqs/VQ81OZRq1TI/AAAAAAAADTU/V_kJZGgjJx0/s1600/Blog%2BRelated%2BStuff.jpg" title="Blog Related Stuff" /></div><div style="text-align: justify;"><br /><span style="color: red;"><u><b>a. Post frequency</b></u></span><br />It’s not a secret that from August 2014 and on I don’t write as frequently as I used to. The previous years (until August 2014) I usually published 3 – 4 posts per month. The last few months, however, the frequency has been dropped to 1 – 2 posts per month; September (2014) was the only month in the last – almost –4 years that I am running this blog, in which I didn’t write anything! Terrible!<br /><br /><span style="color: orange;">In the upcoming months, I intend to restore the post frequency to 3 – 4 posts per month.</span> Moreover, I will probably try to write longer posts, which will include more details about the subject; the <a href="http://www.myengineeringworld.net/2015/03/5-common-inefficiencies-of-a-hvac-system.html">previous post</a> that I published is a good example of what I mean by the term “long post.”<br /><br /><span style="color: red;"><u><b>b. Post subjects</b></u></span><br />In general, I always try to keep a balance between engineering and programming on this blog. <span style="color: orange;">I will continue this “subject splitting” approach,</span> but, on “engineering side”, I will try to write more frequently (I slightly neglected to write about engineering issues at 2014), and, on “programming side”, I will probably try to move from VB 6.0 / VBA to other languages (C# is a good example).<br /><br /><span style="color: red;"><u><b>c. Updating old subjects/codes</b></u></span><br />This is a real pain in the neck for me! <span style="color: orange;">The blog is constantly on a never-ending updating process! </span>This means that I continue to “renovate” older posts quite frequently. This is the reason why I would strongly recommend you to follow this blog on social media; I share there all the critical updates on older posts.<br /><br /><span style="color: red;"><b><u>d. Blog interface</u></b></span><br />During the last three months, I spent some time in order to enhance my HTML, CSS and JavaScript skills.<span style="color: orange;"> I tried to implement some of the new skills that I learned to the blog interface</span> (see for example how the navigation bar stays at the top of the page when you scroll towards the bottom). In the upcoming months, it’s quite possible that I will try to apply other interface enhancements too, to make the blog design more user-friendly.<br /><br /><span style="color: red;"><u><b>e. Contests</b></u></span><br /><span style="color: orange;">I am thinking to organize some contests in the next months,</span> but, I am not going to reveal more at the moment. Just stay tuned and be prepared for some surprises!<br /><br /><span style="color: red;">Key points on blog-related stuff:</span><br />Promise 1: Post frequency will be increased.<br />Promise 2: Distinction between engineering and programming subjects will remain the same; improvements on both will be implemented.<br />Promise 3: Continue updating older posts.<br />Promise 4: More user-friendly and less boring blog interface.<br />Promise 5: Contests will be organized.</div><br /><br /><hr color="#3d85c6" id="free-time" size="1" width="100%" /><h3><span style="color: #3d85c6;">4. My free time </span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Free Time" border="0" src="https://4.bp.blogspot.com/-bPsXtqn-yLI/VQ81bmvV3tI/AAAAAAAADTc/_j5YPkmd1n4/s1600/Free%2BTime.jpg" title="Free Time" /></div><div style="text-align: justify;"><br />All the above issues share a common factor: my free time. <span style="color: red;">Unfortunately, as the years are passing by, my obligations increase, and, consequently, I have less spare time available.</span> On weekdays, I spend half of my day at the university (10 hours work + 1 hour going back and forth to university). But, it’s not only the everyday work: the last 1.5 years I have undertaken several programming and engineering projects, on which I spend a significant amount of time (especially during the weekends). Moreover, by the end of this year, or at least by the first months of 2016, I plan to present my PhD thesis, so this means that I will spend even more time at the university!<br /><br /><span style="color: orange;">Having the above time constraints in mind, as well as other personal obligations in top priority, I have no other choice but to move the blog related stuff down to the list of my priorities.</span> Sorry, but 38+ hours (= time spent on “email engineering” during 2014) is enough time for me to write about 5 – 10 new posts that will probably help much more people compared to the people in which I send the emails.<br /><br /><span style="color: orange;">Another thing that I noticed is that “email engineering” doesn’t improve my programming skills</span>. A lot of people keep asking for variations on existing code snippets. Well, I always try to publish detailed, well-commented code snippets, so that anyone who has some fundamental programming background can adapt to his/her own needs. However, I see that this is not always the case and I keep getting similar questions again and again. Sorry, but answering to similar issues is quite counter-intuitive for me! Last but not least, this blog is not a Q & A forum! <span style="color: orange;">I wish I had the time to answer all the questions that I receive, but, unfortunately, I don’t</span><span style="color: orange;">.</span><br /><br /><span style="color: red;">Key points in my free time:</span><br />Fact 1: My free time shrinks due to the increasing obligations that I have (university, other projects, PhD thesis, etc.).<br />Fact 2: Increasing amount of emails, keep asking similar questions.<br />Result 1: Blog-related stuff went down on the list of my priorities.<br />Result 2: Stricter email and comments policy in order to keep this blog running regularly (publishing at least three posts per month).</div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Epilogue </span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br />In this long text, I tried to explain the reasons why I decided to apply stricter policies regarding email requests and comments. I wish I could help all of you, but, unfortunately, I can’t. <span style="color: orange;">The only thing that I can promise to all of you is that I will do my best in order to keep this blog running.</span> Unfortunately, time flies and we can do nothing about it.</div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-87752486386147920092015-03-10T02:33:00.000+02:002015-03-10T21:47:24.898+02:005 Common Inefficiencies That Affect HVAC System’s Efficiency<div dir="ltr" style="text-align: left;" trbidi="on"><div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;"><div class="separator" style="clear: both; text-align: center;"><img alt="5 Common Inefficiencies That Affect HVAC System’s Efficiency" border="0" src="http://3.bp.blogspot.com/-iel_CTUIojg/VP8NhfUwxTI/AAAAAAAADQw/MOaJWGgPX6c/s1600/5%2BCommon%2BInefficiencies%2BThat%2BAffect%2BHVAC%2BSystem%E2%80%99s%2BEfficiency.jpg" title="5 Common Inefficiencies That Affect HVAC System’s Efficiency" /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction </span></h3><hr color="#3d85c6" size="1" width="100%" /><br />This (long) post initiated by the necessity to answer the following question: <span style="color: red;">How much the HVAC system can affect the building’s overall efficiency? Well, the short answer is that for office buildings, the HVAC system contributes about 25 – 30% on the total building energy use!</span> It is more for laboratories, since much more energy is demanded for fume hoods and lab equipment. Moreover, about 25% of an office building’s energy is used for lighting and the balance for plug loads (office equipment, desk comfort heaters, task lighting, etc.). <span style="color: red;">However, the long answer is that some inefficiencies in the HVAC system can increase its contribution to the total building energy use to even higher values.</span> Common – and relatively easy to correct – causes of HVAC inefficiencies include:<br /><br /><ol><li><a href="http://www.myengineeringworld.net//2015/03/5-common-inefficiencies-of-a-hvac-system.html#building-pressure">Lack of adequate building pressure during air conditioning periods.</a></li><li><a href="http://www.myengineeringworld.net//2015/03/5-common-inefficiencies-of-a-hvac-system.html#equipment-schedule">Heating and/or air conditioning equipment running at the wrong times.</a></li><li><a href="http://www.myengineeringworld.net//2015/03/5-common-inefficiencies-of-a-hvac-system.html#hvac-documentation">Poor documentation of how to properly operate the building.</a></li><li><a href="http://www.myengineeringworld.net//2015/03/5-common-inefficiencies-of-a-hvac-system.html#lighting">Inefficient lighting demand management.</a></li><li><a href="http://www.myengineeringworld.net//2015/03/5-common-inefficiencies-of-a-hvac-system.html#plug-load">Inefficient plug load demand management.</a></li></ol><br />These 5 common inefficiencies are analyzed below. Emphasis is given – apart from the causes – on the possible workarounds. Most of the analysis focuses on office buildings, although the suggested solutions could be possibly applied to any type of building. <br /><br /><br /><hr color="#3d85c6" id="building-pressure" size="1" width="100%" /><h3><span style="color: #3d85c6;">1. Building pressure </span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Building Pressure" border="0" src="http://3.bp.blogspot.com/-pCSysrk17bg/VP8OAXIuKaI/AAAAAAAADQ4/xYoz8yfDF8U/s1600/Building%2BPressure.jpg" title="Building Pressure" /></div><br /><span style="color: orange;">A negative building pressure can pull excessive amounts of unconditioned air into the building, whereas an excessive positive pressure can push conditioned air out of the facility.</span> Many designers are designing buildings that have slightly (about 25 Pa) positive pressure during the air conditioning season and about zero pressure during the heating season. Poor building pressure management can affect HVAC energy efficiency by even 40%. Typical causes of facility pressure problems include:<br /><br /><ul>✗ Excessive building exhaust from HVAC system, uncontrolled bathroom exhausts, high ceiling atriums etc.<br />✗ Exhaust fans running when the HVAC system and associated fresh air makeup is operating in unoccupied mode. This also can cause excessive indoor humidity in humid climates.<br />✗ Clogged fresh air makeup filters.<br />✗ Fresh air makeup fans not working, dampers inoperable or on manual override.<br />✗ Door weather stripping in other than excellent operating condition.<br /></ul><br /><span style="color: orange;">Regarding humidity, it is suggested that for summer months – especially in quite humid climates – to slightly pressurize the facility in order to avoid humidity infiltration.</span> In the cooler winter months, humidity is less likely to be a problem.<br /><br /><br /><hr color="#3d85c6" id="equipment-schedule" size="1" width="100%" /><h3><span style="color: #3d85c6;">2. Equipment running at the wrong times</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Equipment Running At The Wrong Times" border="0" src="http://3.bp.blogspot.com/-bonA87qlueE/VP8QG5WMhbI/AAAAAAAADRE/zo6fAXH-XmY/s1600/Equipment%2BRunning%2BAt%2BThe%2BWrong%2BTimes.jpg" title="Equipment Running At The Wrong Times" /></div><br /><span style="color: orange;">Some common problems involving poor equipment time management </span>include the following:<br /><br /><ul><span style="color: red;">✗ The facility time clock or building automation system that sets the schedules for building equipment to accommodate occupied and unoccupied modes may be inappropriately set, or equipment may be on manual override or not connected to building controls.</span> What should be checked:<br />✔ Chillers coming on too early in the morning or staying on too late in the evening.<br />✔ Building being air conditioned at night and on weekends and holidays.<br />✔ Fire system smoke control ventilation running.<br />✔ Lighting coming on too early in the morning and/or too late in the evening.<br />✔ Fresh air makeup on override.<br /></ul><br /><ul><span style="color: orange;">✗ Variable speed fans set at manual override and at full or zero speed.</span> In those cases fans operate either at full speed, or not at all. Action required:<br />✔ Returning the fans back to automatic enables the building automation system to control zone heating and cooling more effectively. </ul><br /><ul>✗ Terminal air box fans set on manual. Similar to the above problem, many times, terminal air box fans may have been set on manual override for maintenance in the past and never returned back to automatic operation. Action required:<br />✔ Returning the fans back to “Auto”, so that the building automation system can better control the entire building. </ul><br /><ul><span style="color: red;">✗ Building automation system’s original set points changed and being undocumented, causing perimeter terminal fan boxes and other equipment to run constantly.</span> What should be checked:<br />✔ Tour the building to assess what is running during the unoccupied mode. Excessive lighting, ventilation fans, bathroom exhaust fans and desk task lights are all symptoms of building automation system scheduling problems.<br />✔ The facility should have all fans off in unoccupied mode, except maybe the fresh air make-up fan that is maintaining building pressure according to the building automation system set point. Fresh air make-up fans operating to maintain building pressure should not have top-run in excess of 40 percent speed. If a high operating speed is observed, either exhaust fans are running, or excessive leakage is occurring.<br /></ul><br /><ul>✗ Equipment (lighting included) may be connected to security light circuits and not being under control by the facility time clock or the building automation system. If major sections of the facility are founded lighted when the building automation system is calling for night lighting:<br />✔ Either a faulty switch, or lighting is not connected to a controlled circuit and could be the problem.<br /></ul><br /><ul>✗ Equipment (i.e. fresh air make-up heaters) running constantly. Many facilities use heaters in fresh air make-up ducting to warm incoming outside air above freezing in the winter. What should be checked:<br />✔ Look for faulty controls that may have these heaters running continuously.<br /></ul><br /><ul><span style="color: red;">✗ Cooling tower pan heater is “on” for the entire year.</span> If the facility operates a chilled water air conditioning system, it will likely operate a cooling tower to discharge building heat. Cooling towers utilize an open tank or drip pan from which to circulate water. The drip pan almost always incorporates a high energy heater to prevent the water from freezing in winter. Action required:<br />✔ It is important to assure these heaters are not operating, except in freezing weather.<br /></ul><br /><ul>✗ Economizer systems are being bypassed. Buildings frequently operate either air economizers, or water side economizers (water chiller systems) to enable the air conditioning system to operate without the high horsepower compressor when outside conditions are cool. Since economizers only operate in early spring and late fall, these systems are at idle most of the year. Action required:<br />✔ Careful maintenance must be employed to assure economizers are in proper order and available to operate when the weather is appropriate for their operation. Approximately 5 to 20% of cooling costs can be saved through the effective use of economizers.<br /></ul><br /><ul><span style="color: orange;">✗ Chilled water reset may be inoperative.</span> Large buildings employing chilled water air conditioning systems frequently have a chilled water reset. This operation allows the building automation system to change the chilled water set point either up, or down from its normal temperature, and to optimize energy efficiency based on outside temperature, building humidity, outside humidity, and a host of other variables.<br />✔ If the building is equipped with the necessary systems, you certainly want these systems to be operational.<br /></ul><br /><ul>✗ Cooling tower operation may not be optimized. An array of operating variables may be controlled by your building automation system. For example, perhaps, the cooling tower fans operate in tandem, in sequence, or with variable speed drives. What should be checked:<br />✔ On very mild days, the cooling tower(s) may not need the fans operating at all to provide condenser cooling water at the proper temperature. If these options are not functional or are set to manual override, fans may be running needlessly and you may be wasting energy.<br /></ul><br /><ul><span style="color: red;">✗ Building sensors may not be calibrated or connected to controls.</span> A large building may operate hundreds of sensors from which the building automation system makes decisions on how to efficiently meet operating set points. If the sensors are inoperable or disconnected, the building automation system cannot optimize operations.<br />✔ An easy solution is to simply review the building systems operating manual and audit the building automation system to see what data are being collected from sensors. Determine if the data makes sense.<br /></ul><br /><hr color="#3d85c6" id="hvac-documentation" size="1" width="100%" /><h3><span style="color: #3d85c6;">3. HVAC documentation</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="HVAC Documentation" border="0" src="http://3.bp.blogspot.com/-pC5BamneItE/VP8alcF1q6I/AAAAAAAADRU/_eUGgmcv9JQ/s1600/HVAC%2BDocumentation.jpg" title="HVAC Documentation" /></div><br /><span style="color: orange;">Ensure that you have the documentation of the system.</span> Documentation is necessary to provide a permanent resource for operations and maintenance (O & M) personnel, for training, and to provide continuity in operations due to O & M staff changes. The facility needs two documents:<br /><br /><ol><li><span style="color: orange;">Facility O & M manual:</span> This manual covers various pieces of equipment and components of the facility, maintenance obligations and requirements, and some detail on the “system” common in this document. Ideally, the facilities manager should maintain a master of this document and then maintain an everyday copy with maintenance staff for daily use and for training.</li><li><span style="color: orange;">HVAC system’s manual:</span> This document details the designers’ intent on the system and how to operate the building. It also details how the various pieces of equipment and systems interact with one another to function in the building.</li></ol><br /><span style="color: red;">If the facility lacks either of these documents, it may be prudent to obtain or establish them first.</span> It will be impossible to operate the building effectively if documents are not on hand to clearly detail how the facility is to be operated, and how to make adjustments for the seasons and other transient conditions in the facility. Sometimes these documents are combined into one volume. In any case, some of the components of a good O & M manual include the following:<br /><br /><ul>✔ Design intent and system limitations.<br />✔ Startup and shutdown procedures.<br />✔ Modes of control, set points and sequence of operations.<br />✔ Detail on building equipment and equipment maintenance, spares, replacement parts etc.<br />✔ Listing of contractors and manufacturer’s contact info.<br />✔ Technical details and instructions on the control system and interlock sequences.<br />✔ Equipment manual override and bypass strategies.<br />✔ Procedures to monitor, trend, troubleshoot and diagnose.<br />✔ Routine and preventative maintenance.<br />✔ Provisions and techniques for emergency shutdowns, interlocks, life safety info.<br />✔ Provisions for ongoing commissioning, functional testing, calibration and fault diagnosis.<br /></ul><br /><hr color="#3d85c6" id="lighting" size="1" width="100%" /><h3><span style="color: #3d85c6;">4. Lighting demand</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Lighting Level Measurement" border="0" src="http://2.bp.blogspot.com/-RCMU8SRh2yc/VP8beb8wmiI/AAAAAAAADRc/8O-U5xQIxDs/s1600/Lighting%2BLevel%2BMeasurement.JPG" title="Lighting Level Measurement" /></div><br /><span style="color: orange;">Lighting adds a significant amount of heat into the building and must be removed in the summer by air conditioning. Thus, optimizing building lighting efficiency directly affects HVAC operation.</span> Lighting energy management is designed to provide the appropriate amount of lighting at the time it is needed. The first step in lighting management is to acquire a light meter. An inexpensive light meter can be purchased at your local electronics store or and is quite cheap (about 15 €). Even experienced lighting engineers are reluctant to rely on their eyes to judge appropriate lighting levels.<br /><br />Office areas in buildings are generally lit to about 500 – 550 lux from the ceiling with task lighting; this increases the light at the work surface to about 750 lux. Cafeterias are also lit to 450 – 550 lux, while hallways and loading docks are lit to about 250 – 350 lux. When auditing the facility with a light meter, use an average of several readings (both under ceiling lamps and in darker areas) to obtain an estimate. Note that windows on bright cloudy days render the highest light readings for windowed rooms. If the facility is illuminated significantly above these levels, it deserves further investigation to see if de-lamping is an option. You can easily remove lamps temporarily to see if this option is viable. In areas where you find de-lamping a permanent opportunity, remember to disconnect the lamp ballasts as these units draw some power too. Usually de-lamping opportunities manifest well in cafeterias, loading docks, and hallways. Verify that lighting schedules coincide with building occupancy to improve efficiency even more.<br /><br /><span style="color: red;">A night survey of lighting is invaluable. Sometimes construction problems have never been corrected. </span>For example you may find large areas where lighting is not under the lighting control system. You may find excessive lighting wired to the night lighting circuit. Evening and night ornamental lighting should be eliminated. Consult with building security and only illuminate outdoors as necessary for security.<br /><br />Lighting technology is changing constantly. If you are interested in purchasing more efficient lighting, opportunities abound. If your facility still operates with T12 fluorescent lamps, there is about a 1 – 3 year payback (at office usage levels) to recoup your investment by retrofitting the system with T8 lamps.<br /><br />If you operate 32 watt T8 fluorescent lamps there are lower wattage versions available down to 25 watts per lamp with little loss in illumination. However, lower wattage T8 lamps may not work with rapid start ballasts or dimming devices. You must check compatibility with your existing fixtures before switching to these. Consult your lamp manufacturer or lamp distributor to assure your ballasts are compatible with the lamps you intend to purchase. If your facility is equipped with incandescent lamps (restrooms, exit signs, loading docks, etc.) that operate for the full day, re-lamping these compact fluorescents has a simple payback 2 – 6 months and significantly reduce the load on your air conditioning.<br /><br /><br /><hr color="#3d85c6" id="plug-load" size="1" width="100%" /><h3><span style="color: #3d85c6;">5. Plug load management</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Plug Load Management" border="0" src="http://3.bp.blogspot.com/-yDdqlD9Aypc/VP8btdckbEI/AAAAAAAADRk/4mupLF0ncnI/s1600/Plug%2BLoad%2BManagement.jpg" title="Plug Load Management" /></div><br /><span style="color: orange;">Similar to lighting, facility plug-in equipment also affects HVAC operation, as all the energy consumed eventually ends up as heat in the building that must be either managed or removed by the HVAC system.</span> Controlling plug loads can be challenging, but two opportunities are easy to achieve:<br /><br /><ol><li><span style="color: red;">Eliminate computers operating at night.</span> At a minimum, have these machines set to “hibernate” if unattended for extensive periods. Similarly, copy machines and desk printers should be set to “sleep” at night. Network servers can be programmed to reset computers even when the computer operator bypasses sleep modes. Contact your information technology person to see how to easily achieve these goals.</li><li>Eliminating desk comfort heaters is challenging as some people really need the extra warmth, however, excessive use should be controlled. Desk heaters should only be authorized on a case by case basis, and the specific brand of the unit should be mandated by building management in order to eliminate the potential for a fire hazard. <span style="color: red;">Desk heaters should be assessed to assure they do not bias building thermostats. </span>A thermostat that is biased by localized heating (desk heaters, copiers, etc.) will destabilize office temperatures. </li></ol><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">References</span></h3><hr color="#3d85c6" size="1" width="100%" /><br />The post was based on material found on the following ASHRAE handbooks:<br /><a href="http://www.amazon.com/gp/product/0123739993/ref=as_li_ss_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=0123739993&linkCode=as2&tag=myengiworl-20">2008 - Fundamentals of HVAC Systems </a><img alt="" border="0" src="http://ir-na.amazon-adsystem.com/e/ir?t=myengiworl-20&l=as2&o=1&a=0123739993" height="1" style="border: none !important; margin: 0px !important;" width="1" /><br /><a href="http://www.amazon.com/gp/product/B0055L5H7K/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=B0055L5H7K&linkCode=as2&tag=myengiworl-20">2011 - HVAC Applications</a><img alt="" border="0" src="http://ir-na.amazon-adsystem.com/e/ir?t=myengiworl-20&l=as2&o=1&a=B0055L5H7K" height="1" style="border: none !important; margin: 0px !important;" width="1" /><br /><a href="http://www.amazon.com/gp/product/1936504251/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1936504251&linkCode=as2&tag=myengiworl-20">2012 - HVAC Systems And Equipment</a><img alt="" border="0" src="http://ir-na.amazon-adsystem.com/e/ir?t=myengiworl-20&l=as2&o=1&a=1936504251" height="1" style="border: none !important; margin: 0px !important;" width="1" /><br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Read also</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><a href="http://www.myengineeringworld.net/2015/02/human-comfort-hvac-system-operation.html">Human Comfort & HVAC System Operation </a></div><a href="http://www.myengineeringworld.net/2013/11/8-factors-affecting-hvac-system-selection.html">8 Key Factors That Affect The Selection Of A HVAC System </a><br /><a href="http://www.myengineeringworld.net/2014/01/control-loops-used-in-hvac-applications.html">Control Loops Used In HVAC Applications</a></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-33378860395522474452015-02-23T19:09:00.000+02:002017-09-27T02:52:15.176+03:00Human Comfort & HVAC System Operation<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;"><div class="separator" style="clear: both; text-align: center;"><img alt="Human Comfort & HVAC System Operation" border="0" src="https://3.bp.blogspot.com/-DKSmfDX5mjk/VOtMqzHRWxI/AAAAAAAADM4/F1jCkVhyDCM/s1600/Human%2BComfort%2B%26%2BHVAC%2BSystem%2BOperation.jpg" title="Human Comfort & HVAC System Operation" /></div><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction </span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /><span style="color: red;">The goal of the heating, ventilating, and air conditioning (HVAC) system is to create and maintain a comfortable environment within a building.</span> Depending on geographic location and building construction, various types of interior climate control systems help ensure that interior spaces are maintained at comfortable levels year-round. With today’s energy conservation concerns, buildings are constructed to be much tighter, reducing the level of natural exchange between indoor and outdoor air. As a result, more and more buildings rely on mechanical conditioning and distribution systems for managing air.<br /><br /><span style="color: orange;">A properly operated HVAC system finds the often delicate balance between optimizing occupant comfort while controlling operating costs. </span>Comfort is an important issue for occupant satisfaction, which can directly affect concentration and productivity. At the same time, controlling these comfort and health parameters directly affects HVAC system operating costs in terms of energy, maintenance and equipment life. Below will be analyzed six of these parameters:</div><div style="text-align: justify;"><br /></div><ol style="text-align: left;"><li><a href="http://www.myengineeringworld.net/2015/02/human-comfort-hvac-system-operation.html#first">Temperature and humidity management</a></li><li><a href="http://www.myengineeringworld.net/2015/02/human-comfort-hvac-system-operation.html#second">Air movement (drafts)</a></li><li><a href="http://www.myengineeringworld.net/2015/02/human-comfort-hvac-system-operation.html#third">Radiant heating/cooling effects</a></li><li><a href="http://www.myengineeringworld.net/2015/02/human-comfort-hvac-system-operation.html#fourth">Airborne chemicals</a></li><li><a href="http://www.myengineeringworld.net/2015/02/human-comfort-hvac-system-operation.html#fifth">Different indoor building conditions</a></li><li><a href="http://www.myengineeringworld.net/2015/02/human-comfort-hvac-system-operation.html#sixth">Building occupant personal preferences</a></li></ol><br /><hr color="#3d85c6" id="first" size="1" width="100%" /><h3><span style="color: #3d85c6;">1. Temperature and humidity management</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Temperature And Humidity Management" border="0" height="287" src="https://4.bp.blogspot.com/-u7-ENwqWDHc/VOtN_i3EEVI/AAAAAAAADNA/VkNT6pEQeis/s1600/Temperature%2BAnd%2BHumidity%2BManagement.jpg" title="Temperature And Humidity Management" width="400" /></div><span style="color: red;"><br /></span><br /><div style="text-align: justify;"><span style="color: red;">The sensation of being hot or cold depends on both temperature and humidity. Temperature and humidity must be controlled and monitored together.</span> For example, on a cold, dry day, the facility may need to operate warmer than a cold, damp day. If the humidity in the building is low in the summer, a warmer set point will achieve adequate comfort. Conversely, if your facility is operating at the maximum humidity of 60%, a cooler temperature setpoint should be set to achieve adequate comfort. In many cases, the control of both temperature and humidity is called “enthalpy control.”<br /><br />Taking into account the above the obvious question is: what exactly are the ideal temperature set points? Well, while there is no definite answer to this question, for temperature and humidity management, <a href="https://www.ashrae.org/resources--publications/bookstore/standard-55">ASHRAE’s standards 55</a> and <a href="https://www.ashrae.org/resources--publications/bookstore/standards-62-1--62-2">62</a> indicate that controlling to 25 – 50% relative humidity is ideal, with 60% as the upper limit to stay within the human comfort zone. <span style="color: orange;">Temperature set points are established based on the amount of humidity present.</span> For office buildings, if building pressure is maintained and fresh air make-up is limited during unoccupied periods, the humidity will not exceed 55%. However, sometimes reheat is necessary for controlling humidity. In those cases, occupant comfort should be enhanced by using innovative means. For example, direct cool air can be used near a source of heat such as a television, copier, or other appliances. Few buildings actively humidify the air to prevent low humidity excursions because of equipment vulnerability to mineral and mold accumulation; equipment would require dedicated and routine maintenance.</div><br /><br /><hr color="#3d85c6" id="second" size="1" width="100%" /><h3><span style="color: #3d85c6;">2. Air movement and drafts</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Air Movement And Drafts" border="0" height="348" src="https://4.bp.blogspot.com/--SxKlgQ2_Xk/VOtP5981phI/AAAAAAAADNM/eh8Ypo41vD4/s1600/Air%2BMovement%2BAnd%2BDrafts.jpg" title="Air Movement And Drafts" width="400" /></div><br /><span style="color: red;">Excessive air flow in buildings is probably rare, but it can be a source of discomfort and complaints.</span> Also, noisy air handlers and supply ducts may give the sense of excessive air movement that can lead to complaints of drafts and uncomfortable temperature. Excessive air movement is generally not desirable in the winter or summer months.<br /><br /><span style="color: orange;">Air movement and radiant energy losses require detailed work on specific areas where problems are being experienced.</span> For air movement issues, facility management may retrofit higher efficiency discharge diffusers with designs better suited for the location and that direct supply air away from occupants. Lower duct velocities may be achieved by providing lower temperature conditioned air in the cooling season and warmer air in winter; thus requiring lower air volumes to be supplied. Sometimes air duct supply volume is simply a tuning problem, where variable air volume terminal air boxes’ minimum airflow setting is not enough to prevent excessive supply air volumes during low demand periods for heating/cooling. Radiant heat transfer discomfort can be addressed by adjustable window shades and curtains. If these are impractical, thin films are available to apply to the window to reduce infrared energy transmission.</div><br /><br /><hr color="#3d85c6" id="third" size="1" width="100%" /><h3><span style="color: #3d85c6;">3. Radiant heating and cooling</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Radiant Heating And Cooling" border="0" src="https://2.bp.blogspot.com/-8lGgVqKksOM/VOtUOhgeU-I/AAAAAAAADN0/BU274swOKP0/s1600/Radiant%2BHeating%2BAnd%2BCooling.JPG" title="Radiant Heating And Cooling" /></div><br /><div style="text-align: justify;"><span style="color: red;">Radiant heating and cooling is a sensation that is common with building occupants who work near windows and where there are high ceilings.</span> Radiation is a form of heat transfer that occurs due to infrared radiation from warmer bodies to cooler ones. Unlike air flow (convective heating), radiation can penetrate windows easily. When applying this form of heat transfer in a building, heat can be either gained or lost through windows. Thus, occupants near windows can feel a sensation of warmth if it is hot outside or a sensation of being chilly on very cold days. The orientation of windows also plays a role. Heating may be lost or gained based on if the window faces a sunny, cloudy or a deep blue clear sky (cooling). Typically, occupants on the north side of a building feel a cooling effect while residents on the south and west side feel a heating effect. Discomfort will be greatest near ‘single pane’ windows, where radiant heating or cooling has the most significant effect.<br /><br /><span style="color: orange;">As a short-term solution, the radiant cooling or heating effects can be addressed by adjusting building temperature set points to compensate. </span>Of course, the long-term and appropriate permanent solution is to address radiant cooling and heating losses with high-efficiency windows. Double pane windows with “low-e” coatings – a microscopically thin, virtually invisible, metal or metallic oxide layer on a window or skylight glazing surface to reduce radiative heat flow – significantly reduce radiant heat transfer, as do drapes and blinds.<br /><br /><span style="color: orange;">Radiant heating and cooling also relate to passive solar design, which uses windows, walls, and floors to absorb and distribute the sun's heat in the winter and reject solar heat in the summer without the use of mechanical systems.</span> It can also maximize the use of sunlight for interior illumination. Buildings designed for passive solar incorporate large south-facing windows, long walls running east to west, and thermal mass to absorb and slowly release the sun's heat. Passive solar designs also incorporate natural ventilation and roof overhangs to block the sun's strongest rays during the summer but allow heat to penetrate in the winter.<br /><br /><span style="color: orange;">Passive solar design techniques are applied most easily in new construction because they involve integral design elements of the building. </span>However, existing buildings can be adapted or "retrofitted" such as installing double-pane windows, thermal floors, and a new HVAC system to passively store solar heat and make the facility more energy efficient.<br /><br /><br /><hr color="#3d85c6" id="fourth" size="1" width="100%" /><h3><span style="color: #3d85c6;">4. Airborne chemicals</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Airborne Chemicals" border="0" src="https://4.bp.blogspot.com/-cYP8ZSZIDGE/VOtSUtml7SI/AAAAAAAADNg/Wl0Q7K5rpEQ/s1600/Airborne%2BChemicals.jpg" title="Airborne Chemicals" /></div><br /><span style="color: red;">Airborne contaminants have become a major issue in the last 20 years. </span>This is a consequence of sealing buildings tightly for energy efficiency while having older HVAC systems with improper fresh air make-up settings, as well as poor building operation due to inadequate or missing system operations manuals. The following are contributing factors to poor indoor air quality and suggestions on how airborne contaminants can be significantly reduced.<br /><br /><span style="color: orange;"><span style="color: red;"><u><b>Biological toxins</b></u> </span>can be produced from live colonies of mold thriving due to excessive humidity in the occupied space and ductwork.</span> Mold can also flourish in condensate drip pans that drain poorly. These conditions cannot be tolerated due to the potentially severe health impact. To combat these conditions, regular humidity monitoring and drip pan inspections are needed. Automation systems will monitor humidity minute by minute, and drip pan inspections by operations staff should be performed quarterly – when filters are changed.<br /><br /><span style="color: orange;"><span style="color: red;"><u><b>Volatile Organic Compounds</b></u> </span>(VOC’s) are airborne chemicals emitted from many building products used in new construction, renovation, and restoration projects.</span> These chemicals can cause respiratory irritation and distress in vulnerable people such as children, elderly, and people with allergies or immunodeficient conditions. The sources of these chemicals can be furniture, high-VOC paints, coatings, particle board, caulking, adhesives, fillers, janitorial cleaners, and waxes. Even natural gas or propane fired heating and cooking systems can be a source when poorly vented. It is important to manage VOC levels and keep them as low as possible. Facility managers should maintain adequate ventilation, with fresh air make-up to keep VOC’s below 1 ppm (parts per million) and with no odors.<br /><br /><span style="color: orange;"><span style="color: red;"><u><b>Inadequate ventilation </b></u></span>can be a significant problem when a building lacks sufficient fresh air make-up, particularly in areas of high occupant density such as theaters, meeting rooms, and classrooms, or areas where the concentration of airborne contaminants may be higher due to the nature of the workspace environment.</span> The ASHRAE standard for adequate ventilation is a minimum of 15 cfm of outdoor air per occupant (20 cfm/person in office spaces). Excessive carbon dioxide (CO2) is a good indicator of inadequate fresh air make-up and a useful tool for monitoring indoor air quality. Areas of high density should be equipped with CO2 sensors that control outside air intake to enable effective control. Most operators maintain set fresh air make-up systems to open at 900 ppm. Humidity is also a substantial problem in high-density populated areas, thus using humidity controls for congregating areas is also a good strategy for increasing ventilation efficiency.<br /><br /><br /><hr color="#3d85c6" id="fifth" size="1" width="100%" /><h3><span style="color: #3d85c6;">5. Difference of indoor conditions in the building</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Difference Of Indoor Conditions In The Building" border="0" src="https://3.bp.blogspot.com/-nRYXRZguY_Y/VOtX8KoN76I/AAAAAAAADOA/ynOFjkO6DMg/s1600/Difference%2BOf%2BIndoor%2BConditions%2BIn%2BThe%2BBuilding.jpg" title="Difference Of Indoor Conditions In The Building" /></div><br /><span style="color: red;">Comfort complaints may result from rapid changes in indoor environmental conditions and/or large variations from one area of the building to the next.</span> These swings in indoor environments may be due to thermostats, humidity sensors, and CO2 sensors not calibrated or operating properly, as well as poor design and/or missing sensors. Sometimes necessary equipment is value-engineered (applying various techniques to provide the necessary function at the lowest overall cost) out of new construction and renovations. Improper placement of sensors and not installing enough zones to accurately control a space are other causes of variable conditions within an area. Even when these issues are adequately covered, unevenness in building conditions may still occur from occupant activity such as office partitions (cubicles) and operate desk comfort heaters, which can bias building sensors.<br /><br /><br /><hr color="#3d85c6" id="sixth" size="1" width="100%" /><h3><span style="color: #3d85c6;">6. Building occupants</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="Building Occupants" border="0" src="https://3.bp.blogspot.com/-qTL0_RndKSQ/VOtS49CVxbI/AAAAAAAADNo/bsZxzahBpVs/s1600/Building%2BOccupants.jpg" title="Building Occupants" /></div><br /><span style="color: red;">Building managers need to be sensitive to the types of people occupying their buildings and their level of activity.</span> An older workforce, people with disabilities and individuals working in sedimentary jobs generally require a warmer building environment to be comfortable. Younger workers, people who are a bit overweight and those in active jobs usually are more comfortable in cooler conditions.<br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">References</span></h3><hr color="#3d85c6" size="1" width="100%" /><br />The post was based on material found in the following ASHRAE handbooks and code standards:<br /><br /><div style="text-align: justify;"><a href="http://www.amazon.com/gp/product/1933742550/ref=as_li_ss_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=1933742550&linkCode=as2&tag=myengiworl-20">ASHRAE - Fundamentals</a> </div><a href="http://www.amazon.com/gp/product/0123739993/ref=as_li_ss_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=0123739993&linkCode=as2&tag=myengiworl-20">ASHRAE - Fundamentals of HVAC Systems </a><img alt="" border="0" src="http://ir-na.amazon-adsystem.com/e/ir?t=myengiworl-20&l=as2&o=1&a=0123739993" height="1" style="border: none !important; margin: 0px !important;" width="1" /><br /><a href="http://www.amazon.com/gp/product/1936504251/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1936504251&linkCode=as2&tag=myengiworl-20">ASHRAE - HVAC Systems And Equipment</a><br /><a href="https://www.ashrae.org/resources--publications/bookstore/standard-55">ASHRAE 55 - Temperature Standards</a><br /><a href="https://www.ashrae.org/resources--publications/bookstore/standards-62-1--62-2">ASHRAE 62 - Ventilation</a><br /><a href="https://www.ashrae.org/resources--publications/bookstore/standard-90-1">ASHRAE 90.1 – Energy Efficiency in Commercial Buildings</a></div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Read also</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><a href="http://www.myengineeringworld.net/2015/03/5-common-inefficiencies-of-a-hvac-system.html">5 Common Inefficiencies That Affect HVAC System’s Efficiency</a> <br /><a href="http://www.myengineeringworld.net/2013/11/8-factors-affecting-hvac-system-selection.html">8 Key Factors That Affect The Selection Of A HVAC System </a><br /><a href="http://www.myengineeringworld.net/2014/01/control-loops-used-in-hvac-applications.html">Control Loops Used In HVAC Applications</a></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-29660644105244627412015-01-21T00:46:00.000+02:002017-03-16T19:12:39.236+02:00Website Log-In Automation With VBA<div dir="ltr" style="text-align: left;" trbidi="on"><div dir="ltr" style="text-align: justify;" trbidi="on"><div><div class="separator" style="clear: both; text-align: center;"><img alt="Website Log-In Automation With VBA" border="0" src="https://2.bp.blogspot.com/-aoquU43sTjo/VL518oz9S8I/AAAAAAAACyQ/Bm7lCdsQvDI/s1600/Website%2BLog-In%2BAutomation%2BWith%2BVBA.jpg" title="Website Log-In Automation With VBA" /></div><div><br /></div><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><br />In the last few months I saw a lot of people struggling to automate the log-in procedure to various websites using VBA. To be honest, web-related tasks can be considered as advanced VBA topics, since in many cases require basic knowledge of HTML language. <span style="color: red;">In this post I will try to provide some insights about how to automate the log-in procedure via VBA.</span> More precisely, I will analyze the concept behind a reusable macro that I developed. The macro creates a new instance of Internet Explorer, navigates to the desired webpage, enters the username and password values in the corresponding text boxes of the webpage, and, finally, presses the sign-in button in order to complete the log-in procedure.</div></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><br /></div><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3 style="text-align: justify;"><span style="color: #3d85c6;"><b>VBA code</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><div style="text-align: justify;"><h4><span style="color: red;"><u>A. Code analysis</u></span></h4></div><div style="text-align: justify;">Almost half of the code of <span style="color: orange;">“WebsiteLogIn”</span> macro is used to start a new instance of Internet Explorer and navigate to the requested URL. Since late binding is used, it is necessary to use the CreateObject method and ensure that the object was created. The <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms633548%28v=vs.85%29.aspx">ShowWindow API</a> is used to maximize the Internet Explorer window. Before the main procedure starts, the <a href="http://www.myengineeringworld.net/2015/01/website-log-in-automation-vba-macro.html#IsURLValid">IsURLValid</a> function is used to find if the target URL exists (see more about IsURLValid <a href="http://www.myengineeringworld.net/2015/01/website-log-in-automation-vba-macro.html#IsURLValid">below</a>). If yes, the procedure continues, otherwise an error message pops up. </div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">The other half code is dominated by the usage of <a href="https://developer.mozilla.org/en-US/docs/Web/API/document.getElementById">getElementById method</a>, which returns a reference to an HTML element by using its ID. So, let’s say a few HTML things: When an HTML document is loaded into a web browser, it becomes a document object. The document object is the root node of the HTML document and the "owner" of all other nodes (element nodes, text nodes, attribute nodes, and comment nodes). The document object provides properties and methods to access all node objects (using JavaScript for example). One of the available document object methods, which is used in this example, is the getElementById method.</div><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; text-align: center;"><img alt="Finding Element ID Inside The HTML Code" border="0" src="https://3.bp.blogspot.com/-w9R_4AJZGVk/VL56u89FsNI/AAAAAAAACyg/6Qe35kPe3MA/s1600/Finding%2BElement%2BID%2BInside%2BThe%2BHTML%2BCode.jpg" title="Finding Element ID Inside The HTML Code" /></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><span style="color: orange;">The getElementById method is used to find 3 elements on the target webpage: the username and password text boxes, as well as the sign-in button.</span> This information is user-input, along with the URL of the webpage, the username and the password for logging-in. But, how to find the element IDs so as to use this macro? Fortunately, with nowadays web browsers this is not a difficult task; you just have to follow the next 7 steps:</div><div style="text-align: justify;"></div><div id="7step"><br /><ol><li>Open your favorite web browser and navigate to the website you want to automate the logging-in procedure.</li><li>Right click on the username/password text box or on the sign-in button.</li><li>On the context menu that pops up, select the “Inspect Element” option.</li><li>At the bottom of the webpage a new window will appear containing the HTML code of the webpage.</li><li>In the highlighted line(s) of the HTML code try to find the id property.</li><li>The desired element ID will be inside the quotation marks ("").</li><li>Repeat this procedure (steps 2 to 6) for all three elements (username text box, password text box and sign-in button).</li></ol></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">The getElementById method was preferred in the particular case instead of other document object methods, such as getElementsByName, getElementsByTagName and getElementsByClassName, because <span style="color: orange;">the majority of web developers almost always assign unique ID values to the HTML elements of the webpages that they design</span>. Returning to the "WebsiteLogIn" macro, if the getElementById finds the user-input ID of the element, it will assign a value if the element is a (username/password) text box, or it will invoke the click method if the element is a (sign-in) button. In any case, if the element ID is not found within the DOM (Document Object Model) of the webpage an error message will pop up, informing the user about the failure.</div><div style="text-align: justify;"><br /><h4><span style="color: red;"><u>B. WebsiteLogIn macro</u></span></h4></div><div style="text-align: justify;"><pre><span style="color: blue;">Option Explicit</span><br /><br /><span style="color: green;">'Declaring the necessary ShowWindow API function and the constant to maximize Internet Explorer window.</span><br />#<span style="color: blue;">If</span> VBA7 <span style="color: blue;">And</span> Win64 <span style="color: blue;">Then</span><br /> <br /> <span style="color: green;">'For 64 bit Excel.</span><br /> <span style="color: blue;">Public</span> <span style="color: blue;">Declare</span> PtrSafe <span style="color: blue;">Function</span> ShowWindow <span style="color: blue;">Lib</span> <span style="color: maroon;">"user32"</span> _<br /> (<span style="color: blue;">ByVal</span> hwnd <span style="color: blue;">As</span> LongPtr, _<br /> <span style="color: blue;">ByVal</span> nCmdShow <span style="color: blue;">As</span> <span style="color: blue;">Long</span>) <span style="color: blue;">As</span> <span style="color: blue;">Long</span><br /> <br />#<span style="color: blue;">Else</span><br /><br /> <span style="color: green;">'For 32 bit Excel.</span><br /> <span style="color: blue;">Public</span> <span style="color: blue;">Declare</span> <span style="color: blue;">Function</span> ShowWindow <span style="color: blue;">Lib</span> <span style="color: maroon;">"user32"</span> _<br /> (<span style="color: blue;">ByVal</span> hwnd <span style="color: blue;">As</span> <span style="color: blue;">Long</span>, _<br /> <span style="color: blue;">ByVal</span> nCmdShow <span style="color: blue;">As</span> <span style="color: blue;">Long</span>) <span style="color: blue;">As</span> <span style="color: blue;">Long</span><br /> <br />#<span style="color: blue;">End</span> <span style="color: blue;">If</span><br /><br /><span style="color: blue;">Public</span> <span style="color: blue;">Const</span> SW_MAXIMIZE = <span style="color: maroon;">3</span><br /><br /><span style="color: blue;">Sub</span> WebsiteLogIn(URL <span style="color: blue;">As</span> <span style="color: blue;">String</span>, UNElementID <span style="color: blue;">As</span> <span style="color: blue;">String</span>, UserName <span style="color: blue;">As</span> <span style="color: blue;">String</span>, _<br /> PWElementID <span style="color: blue;">As</span> <span style="color: blue;">String</span>, Password <span style="color: blue;">As</span> <span style="color: blue;">String</span>, SIElementID <span style="color: blue;">As</span> <span style="color: blue;">String</span>)<br /> <br /> <span style="color: green;">'--------------------------------------------------------------------------------------------------------------------------</span><br /> <span style="color: green;">'This macro can be used in order to log-in to a website automatically. It requires 6 parameters, which are analyzed below.</span><br /> <span style="color: green;">'The macro creates a new instance of Internet Explorer, navigates to the desired site, enters the username and password</span><br /> <span style="color: green;">'values in the corresponding text boxes and presses the sign-in button in order to log-in to the site.</span><br /> <span style="color: green;">'The code uses late binding, so no reference to external library is required.</span><br /> <br /> <span style="color: green;">'Required parameters:</span><br /> <span style="color: green;">'URL: The website URL you want to log-in. Example: https://login.yahoo.com/?.src=ym&done=https%3a//mail.yahoo.com</span><br /> <span style="color: green;">'UNElementID: The element ID of the text box, in which you write the UserName. Example: login-username</span><br /> <span style="color: green;">'UserName: The username that is used for log-in. Example: MyUserName</span><br /> <span style="color: green;">'PWElementID: The element ID of the text box, in which you write the Password. Example: login-passwd</span><br /> <span style="color: green;">'Password: The password that is used for log-in. Example: MyPassword</span><br /> <span style="color: green;">'SIElementID: The element ID of the button that you press in order to log-in. Example: login-signin</span><br /> <br /> <span style="color: green;">'NOTE: in order to specify the values of UNElementID, PWElementID and SIElementID parameters, navigate with your browser</span><br /> <span style="color: green;">'to the target page, select the username/password text boxes or the sign-in button, right click on it with the mouse and</span><br /> <span style="color: green;">'press the "Inspect Element" from the pop-up menu. In the new window that will appear at the bottom of the page find in</span><br /> <span style="color: green;">'the highlighted line of the HTML code the property id= and the ID of the element will be inside the quotation marks ("").</span><br /> <br /> <span style="color: green;">'Written By: Christos Samaras</span><br /> <span style="color: green;">'Date: 18/01/2015</span><br /> <span style="color: green;">'E-mail: xristos.samaras@gmail.com</span><br /> <span style="color: green;">'Site: http://www.myengineeringworld.net</span><br /> <span style="color: green;">'--------------------------------------------------------------------------------------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> IE <span style="color: blue;">As</span> <span style="color: blue;">Object</span><br /> <span style="color: blue;">Dim</span> IEPage <span style="color: blue;">As</span> <span style="color: blue;">Object</span><br /> <span style="color: blue;">Dim</span> IEPageElement <span style="color: blue;">As</span> <span style="color: blue;">Object</span><br /> <br /> <span style="color: green;">'Check if the requested URL is valid.</span><br /> <span style="color: blue;">If</span> IsURLValid(URL) = <span style="color: maroon;">False</span> <span style="color: blue;">Then</span><br /> MsgBox <span style="color: maroon;">"Sorry, the URL you provided is not valid!"</span>, vbCritical, <span style="color: maroon;">"URL Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Create a new Internet Explorer instance, make it visible and maximize its window.</span><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <span style="color: blue;">Set</span> IE = CreateObject(<span style="color: maroon;">"InternetExplorer.Application"</span>)<br /> IE.Visible = <span style="color: maroon;">True</span><br /> ShowWindow IE.hwnd, SW_MAXIMIZE<br /> <br /> <span style="color: green;">'Check if the ojbect was created.</span><br /> <span style="color: blue;">If</span> Err.Number <> <span style="color: maroon;">0</span> <span style="color: blue;">Then</span><br /> MsgBox <span style="color: maroon;">"Sorry, it was impossible to start Internet Explorer!"</span>, vbCritical, <span style="color: maroon;">"Internet Explorer Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Navigate to the requested URL.</span><br /> IE.navigate URL<br /> <br /> <span style="color: green;">'Wait until the web page is fully loaded.</span><br /> <span style="color: blue;">Do</span> <span style="color: blue;">Until</span> IE.readyState = <span style="color: maroon;">4</span> <span style="color: green;">'READYSTATE_COMPLETE in early binding</span><br /> DoEvents<br /> <span style="color: blue;">Loop</span><br /> <br /> <span style="color: green;">'Get the document of the URL.</span><br /> <span style="color: blue;">Set</span> IEPage = IE.document<br /> <br /> <span style="color: green;">'Find the UserName text box using the element ID.</span><br /> <span style="color: blue;">Set</span> IEPageElement = IEPage.getElementById(UNElementID)<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IEPageElement <span style="color: blue;">Is</span> <span style="color: blue;">Nothing</span> <span style="color: blue;">Then</span><br /> <span style="color: green;">'Pass the UserName value to the corresponding text box.</span><br /> IEPageElement.Value = UserName<br /> <span style="color: blue;">Set</span> IEPageElement = <span style="color: blue;">Nothing</span><br /> <span style="color: blue;">Else</span><br /> <span style="color: green;">'The element ID was not found, inform the user.</span><br /> MsgBox <span style="color: maroon;">"Coould not find the '"</span> & UNElementID & <span style="color: maroon;">"' element ID on the page!"</span>, vbCritical, <span style="color: maroon;">"Element ID Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Find the Password text box using the element ID.</span><br /> <span style="color: blue;">Set</span> IEPageElement = IEPage.getElementById(PWElementID)<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IEPageElement <span style="color: blue;">Is</span> <span style="color: blue;">Nothing</span> <span style="color: blue;">Then</span><br /> <span style="color: green;">'Pass the Password value to the corresponding text box.</span><br /> IEPageElement.Value = Password<br /> <span style="color: blue;">Set</span> IEPageElement = <span style="color: blue;">Nothing</span><br /> <span style="color: blue;">Else</span><br /> <span style="color: green;">'The element ID was not found, inform the user.</span><br /> MsgBox <span style="color: maroon;">"Coould not find the '"</span> & PWElementID & <span style="color: maroon;">"' element ID on the page!"</span>, vbCritical, <span style="color: maroon;">"Element ID Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Find the Sign-In button using the element ID.</span><br /> <span style="color: blue;">Set</span> IEPageElement = IEPage.getElementById(SIElementID)<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IEPageElement <span style="color: blue;">Is</span> <span style="color: blue;">Nothing</span> <span style="color: blue;">Then</span><br /> <span style="color: green;">'Click the Sign-In button to enter the site.</span><br /> IEPageElement.Click<br /> <span style="color: blue;">Else</span><br /> <span style="color: green;">'The element ID was not found, inform the user.</span><br /> MsgBox <span style="color: maroon;">"Coould not find the '"</span> & SIElementID & <span style="color: maroon;">"' element ID on the page!"</span>, vbCritical, <span style="color: maroon;">"Element ID Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Release the objects.</span><br /> <span style="color: blue;">Set</span> IEPageElement = <span style="color: blue;">Nothing</span><br /> <span style="color: blue;">Set</span> IEPage = <span style="color: blue;">Nothing</span><br /> <span style="color: blue;">Set</span> IE = <span style="color: blue;">Nothing</span><br /><br /><span style="color: blue;">End</span> <span style="color: blue;">Sub</span></pre></div><div style="text-align: justify;"><br /><h4 id="IsURLValid"><span style="color: red;"><u>C. IsURLValid function</u></span></h4></div><div class="separator" style="clear: both; text-align: center;"><img alt="IsURLValid Function Used In Worksheet" border="0" src="https://4.bp.blogspot.com/-o1ANS4QcGmI/VL57AAoajMI/AAAAAAAACyo/AKKXq1FUNCo/s1600/IsURLValid%2BFunction%2BUsed%2BIn%2BWorksheet.jpg" title="IsURLValid Function Used In Worksheet" /></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">The code for the IsURLValid function follows. Note that this function could be also used as a built-in function. <span style="color: red;">If you have various websites or individual webpages and you need to check if their URLs are valid you can use this function.</span> In the workbook that you will find on the <a href="http://www.myengineeringworld.net/2015/01/website-log-in-automation-vba-macro.html#downloads">Downloads</a> section I have implemented <a href="http://www.myengineeringworld.net/2013/07/add-description-to-custom-vba-function.html">this technique</a> in order to add the function’s description, so as to look more like a built-in function.</div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><pre><span style="color: blue;">Function</span> IsURLValid(URL <span style="color: blue;">As</span> <span style="color: blue;">String</span>) <span style="color: blue;">As</span> <span style="color: blue;">Boolean</span><br /> <br /> <span style="color: green;">'--------------------------------------------------------------------</span><br /> <span style="color: green;">'Checks if a URL is valid; returns True if exists and False if not.</span><br /> <br /> <span style="color: green;">'Written By: Christos Samaras</span><br /> <span style="color: green;">'Date: 18/01/2015</span><br /> <span style="color: green;">'E-mail: xristos.samaras@gmail.com</span><br /> <span style="color: green;">'Site: http://www.myengineeringworld.net</span><br /> <span style="color: green;">'--------------------------------------------------------------------</span><br /> <br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> Request <span style="color: blue;">As</span> <span style="color: blue;">Object</span><br /> <span style="color: blue;">Dim</span> Result <span style="color: blue;">As</span> <span style="color: blue;">String</span><br /><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Create the WinHttpRequest object and check if the object was created.</span><br /> <span style="color: blue;">Set</span> Request = CreateObject(<span style="color: maroon;">"WinHttp.WinHttpRequest.5.1"</span>)<br /> <span style="color: blue;">If</span> Err.Number <> <span style="color: maroon;">0</span> <span style="color: blue;">Then</span><br /> MsgBox <span style="color: maroon;">"Could not create the WinHttpRequest object!"</span>, vbCritical, <span style="color: maroon;">"WinHttpRequest Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <br /> <span style="color: green;">'Create the request using the input URL.</span><br /> Request.Open <span style="color: maroon;">"GET"</span>, URL, <span style="color: maroon;">False</span><br /> <br /> <span style="color: green;">'Send the request to the server.</span><br /> Request.send<br /> <br /> <span style="color: green;">'Read the request status.</span><br /> Result = Request.StatusText<br /> <br /> <span style="color: green;">'Check if the request status is "OK" - the URL exists.</span><br /> <span style="color: blue;">If</span> InStr(<span style="color: maroon;">1</span>, Result, <span style="color: maroon;">"OK"</span>, vbTextCompare) > <span style="color: maroon;">0</span> <span style="color: blue;">Then</span> IsURLValid = <span style="color: maroon;">True</span><br /> <br /> <span style="color: green;">'Release the WinHttpRequest object.</span><br /> <span style="color: blue;">Set</span> Request = <span style="color: blue;">Nothing</span><br /><br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span></pre></div><br /><div style="text-align: justify;"><h4><span style="color: red;"><u>D. Sample macros</u></span></h4></div><div style="text-align: justify;">Finally, there are 3 sample macros that use the “WebsiteLogIn” macro in order to log-in to Gmail, Yahoo mail and Facebook. In all macros (“GmailLogIn”, “YahooMailLogIn” and “OtherLogIn”) I used the <a href="http://www.myengineeringworld.net/2015/01/website-log-in-automation-vba-macro.html#7step">7-step procedure</a> that was described above in order to get the correct element IDs from the corresponding webpages. </div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><pre><span style="color: blue;">Option Explicit</span><br /><br /><span style="color: blue;">Sub</span> GmailLogIn()<br /> <br /> <span style="color: green;">'Sample usage of WebsiteLogIn macro for logging-in to Gmail.</span><br /> <br /> WebsiteLogIn <span style="color: maroon;">"https://accounts.google.com/ServiceLogin?service=mail&continue=https://mail.google.com/mail"</span>, _<br /> <span style="color: maroon;">"Email"</span>, Sheets(<span style="color: maroon;">"Log-In"</span>).Range(<span style="color: maroon;">"C6"</span>).Value, _<br /> <span style="color: maroon;">"Passwd"</span>, Sheets(<span style="color: maroon;">"Log-In"</span>).Range(<span style="color: maroon;">"C8"</span>).Value, <span style="color: maroon;">"signIn"</span><br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Sub</span><br /><br /><span style="color: blue;">Sub</span> YahooMailLogIn()<br /> <br /> <span style="color: green;">'Sample usage of WebsiteLogIn macro for logging-in to Yahoo email.</span><br /> <span style="color: green;">'Just compare the element IDs of the username and password text boxes, as well as</span><br /> <span style="color: green;">'the element ID of the sign-in button with the corresponding values for the Gmail case.</span><br /> <br /> WebsiteLogIn <span style="color: maroon;">"https://login.yahoo.com/?.src=ym&done=https%3a//mail.yahoo.com"</span>, _<br /> <span style="color: maroon;">"login-username"</span>, Sheets(<span style="color: maroon;">"Log-In"</span>).Range(<span style="color: maroon;">"C13"</span>).Value, _<br /> <span style="color: maroon;">"login-passwd"</span>, Sheets(<span style="color: maroon;">"Log-In"</span>).Range(<span style="color: maroon;">"C15"</span>).Value, <span style="color: maroon;">"login-signin"</span><br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Sub</span><br /><br /><span style="color: blue;">Sub</span> OtherLogIn()<br /> <br /> <span style="color: green;">'Sample usage of WebsiteLogIn macro for logging-in to any page, as long as the necessary</span><br /> <span style="color: green;">'parameters have been specified (in the Log-In sheet we use Facebook as an example).</span><br /> <br /> <span style="color: blue;">With</span> Sheets(<span style="color: maroon;">"Log-In"</span>)<br /> <br /> <span style="color: blue;">If</span> .Range(<span style="color: maroon;">"G20"</span>).Value = <span style="color: maroon;">False</span> <span style="color: blue;">Then</span><br /> MsgBox <span style="color: maroon;">"Sorry, but the URL you entered doesn't exist!"</span>, vbCritical, <span style="color: maroon;">"Invalid URL Error"</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Sub</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <br /> WebsiteLogIn .Range(<span style="color: maroon;">"C20"</span>).Value, .Range(<span style="color: maroon;">"C22"</span>).Value, .Range(<span style="color: maroon;">"C24"</span>).Value, _<br /> .Range(<span style="color: maroon;">"C26"</span>).Value, .Range(<span style="color: maroon;">"C28"</span>).Value, .Range(<span style="color: maroon;">"C30"</span>).Value<br /> <br /> <span style="color: blue;">End</span> <span style="color: blue;">With</span><br /> <br /><span style="color: blue;">End</span> <span style="color: blue;">Sub</span></pre></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><br /></div><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3 style="text-align: justify;"><span style="color: #3d85c6;"><b>How to use the sample workbook</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><div style="text-align: justify;"><br /></div><div style="text-align: justify;">The “Gmail” and “Yahoo” headings of the workbook I think that are self-explanatory. If you have an account to these email providers simply enter your username and password and press the corresponding log-in button. In the “Other” heading however, the things are a little bit more complicated:</div><div style="text-align: justify;"><br /></div><ul style="text-align: justify;"><li>Start by entering the URL of the website you need to log-in. The IsURLValid function at the adjacent cell will return TRUE if the URL you entered exists (the cell next to the URL will become either blue, or red – if the URL doesn’t exist).</li><li>Next, following the <a href="http://www.myengineeringworld.net/2015/01/website-log-in-automation-vba-macro.html#7step">7-step procedure</a> that was described in the VBA code section, find the element IDs of the username and password text boxes, as well as the ID of the sign-in button. Enter these ID values in the corresponding cells of the worksheet.</li><li>Finally, enter your username and password and press the Other Log-In button.</li></ul><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><span style="color: orange;">With the latter procedure you can quickly test any website you wish to log-in.</span> In the sample workbook for example I have included the IDs of the Facebook log-in webpage, just to demonstrate that the same approach can be used almost in every webpage that requires log-in. </div><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; text-align: center;"><img alt="Asterisks Format In The Cells" border="0" src="https://4.bp.blogspot.com/-MHF2pAHuWdg/VL5_BtJ_eSI/AAAAAAAACy0/f406Tz097KA/s1600/Asterisks%2BFormat%2BIn%2BThe%2BCells.jpg" title="Asterisks Format In The Cells" /></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">Finally, if you are wondered how the asterisks appear on the cells that contain usernames and passwords just right click on any of them and on the context menu that will appear select Format Cells → go to the Number tab → select the Custom category → look at the format type that is applied, which is this: <b><span style="color: red;">;;;**</span></b>. Nice trick, don’t you think? </div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3 style="text-align: left;"><span style="color: #3d85c6;"><b>Demonstration video</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;">The short video demonstrates the result of the "WebsiteLogIn" macro, as well as shows the <a href="http://www.myengineeringworld.net/2015/01/website-log-in-automation-vba-macro.html#7step">7-step procedure</a> that was described above.</div><div style="text-align: center;"><br /></div><div style="text-align: center;"><iframe allowfullscreen="" frameborder="0" height="338" src="//www.youtube.com/embed/aS2R28TeCM4" width="600"></iframe></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;"><br /></div><div id="downloads" style="text-align: justify;"><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /><h3><span style="color: #3d85c6;"><b>Downloads</b></span></h3><hr color="#3d85c6" size="1" style="margin-left: 0px; margin-right: 0px;" width="100%" /></div><div style="text-align: justify;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://goo.gl/5RTC9K"><img alt="Download" border="0" height="108" src="https://4.bp.blogspot.com/-JhKlqA-EMkM/UoVNR1_v4II/AAAAAAAABtM/Q1GE7I4707g/s1600/Download.jpg" title="Click to download the file" width="320" /></a></div><div style="text-align: justify;"><br /></div><div style="text-align: justify;">The file can be opened with <span style="color: red;">Excel 2007</span> or newer. Please enable macros before using it. </div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-90563453972124266442014-12-08T01:05:00.000+02:002017-03-16T19:10:49.698+02:00Get Public IP, Local IP & MAC Address Using VBA<div dir="ltr" style="text-align: left;" trbidi="on"><div class="separator" style="clear: both; text-align: center;"><img alt="Get Public IP, Local IP & MAC Address Using VBA" border="0" height="352" src="https://2.bp.blogspot.com/-4wjoJTVcC0E/VIXUg_J2nnI/AAAAAAAACkE/nZ0geFX7F0s/s1600/Get%2BPublic%2BIP%2C%2BLocal%2BIP%2B%26%2BMAC%2BAddress%2BUsing%2BVBA.jpg" title="Get Public IP, Local IP & MAC Address Using VBA" width="640" /></div><div style="text-align: justify;"><br /></div><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;"><span style="color: red;">As the title implies, today we will learn how to retrieve the local and the public IP address, as well as the MAC address of a computer using VBA.</span> First of all, the definition of IP address according to <a href="https://en.wikipedia.org/wiki/IP_address">Wikipedia</a> is the following: <i>“An Internet Protocol address (IP address) is a numerical label assigned to each device (e.g., computer, printer) participating in a computer network that uses the Internet Protocol for communication. An IP address serves two principal functions: host or network interface identification and location addressing. Its role has been characterized as follows: A name indicates what we seek. An address indicates where it is. A route indicates how to get there”</i>.<br /><br /><span style="color: orange;"><u>Public or external IP VS local or private IP</u></span><br />A public/external IP address is any valid address, or number, that can be accessed over the Internet. Internet standards groups, such as the Network Information Center (NIC) or the Internet Assigned Numbers Authority (IANA), are the organizations responsible for registering IP ranges and assigning them to organizations, such as Internet Service Providers (ISPs).<br /><br />On the other hand, a local/private IP address is any number or address assigned to a device on a private TCP/IP Local Area Network that is accessible only within the Local Area Network. For a resource inside the Local Area Network to be accessible over the Internet, a device within the Local Area Network must be connected to the Internet with a public IP address, and the networking must be appropriately configured. <br /><br /><span style="color: orange;"><u>MAC address (from <a href="https://en.wikipedia.org/wiki/MAC_address">Wikipedia</a>)</u></span><br /><i>“A media access control address (MAC address) is a unique identifier assigned to network interfaces for communications on the physical network segment. MAC addresses are used as a network address for most IEEE 802 network technologies, including Ethernet and WiFi. Logically, MAC addresses are used in the media access control protocol sublayer of the OSI reference model.</i><br /><i><br /></i><i>MAC addresses are most often assigned by the manufacturer of a network interface controller (NIC) and are stored in its hardware, such as the card's read-only memory or some other firmware mechanism. If assigned by the manufacturer, a MAC address usually encodes the manufacturer's registered identification number and may be referred to as the burned-in address (BIA). It may also be known as an Ethernet hardware address (EHA), hardware address or physical address. This can be contrasted to a programmed address, where the host device issues commands to the NIC to use an arbitrary address”</i>.<br /><br /><span style="color: orange;"><u>Simplistic definitions</u></span><br />When I am thinking of IP or MAC address, one simple term comes always to my mind: the national identity number – ID number. MAC address is the hardware’s ID number (usually the ID number of the network adapter), whereas IP address is the ID number of the computer over the local or the global network. <span style="color: red;">As the ID number is used by the governments of many countries as a means of tracking their citizens, similarly the MAC and IP address are used over a local or global network (internet) in order to track down different computers or devices.</span> </div><div style="text-align: justify;"><br /><br /></div><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Manual solution</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br />The short video below demonstrates two “manual” ways of retrieving the local IP and MAC address of your computer, as well as a single way to retrieve your public/external IP:</div><ol style="text-align: left;"><li>Network connection details (local IP and MAC address).</li><li>Command prompt – ipconfig and getmac (local IP and MAC address).</li><li>Internet (public IP).</li></ol><div style="text-align: justify;"><div style="text-align: center;"><iframe allowfullscreen="" frameborder="0" height="338" src="//www.youtube.com/embed/VmidmHRAFTM" width="600"></iframe></div><div style="text-align: center;"><br /></div>Moreover, the video also presents the results from the VBA functions that are given below.</div><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>VBA code</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;"><span style="color: red;">Unfortunately there is no way to get programmatically the public/external IP of a computer without communicating to another computer over the internet.</span> Thus, the <span style="color: orange;">GetMyPublicIP</span> function sends a request to <a href="http://myip.dnsomatic.com/">http://myip.dnsomatic.com</a> and returns the response text. The <span style="color: orange;">GetMyLocalIP</span> function on the other hand, uses WMI in order to get the IP addresses from the network adapters that have the property IPEnabled equal to true; then it returns the first non-empty IP. Finally, the <span style="color: orange;">GetMyMACAddress</span> follows a similar approach with GetMyLocalIP function and returns the MAC address of the first adapter that has a non-empty IP.<br /><br /><pre><span style="color: blue;">Option Explicit</span><br /> <br /><span style="color: green;">'----------------------------------------------------------------------------</span><br /><span style="color: green;">'This module contains 3 functions for determing the public IP, the local IP</span><br /><span style="color: green;">'and the MAC address of the computer that runs those functions.</span><br /><br /><span style="color: green;">'Written By: Christos Samaras</span><br /><span style="color: green;">'Date: 22/11/2014</span><br /><span style="color: green;">'E-mail: xristos.samaras@gmail.com</span><br /><span style="color: green;">'Site: http://www.myengineeringworld.net</span><br /><span style="color: green;">'----------------------------------------------------------------------------</span><br /> <br /><span style="color: blue;">Function</span> GetMyPublicIP() <span style="color: blue;">As</span> <span style="color: blue;">String</span><br /><br /> <span style="color: blue;">Dim</span> HttpRequest <span style="color: blue;">As</span> <span style="color: blue;">Object</span><br /> <br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">Resume</span> <span style="color: blue;">Next</span><br /> <span style="color: green;">'Create the XMLHttpRequest object.</span><br /> <span style="color: blue;">Set</span> HttpRequest = CreateObject(<span style="color: maroon;">"MSXML2.XMLHTTP"</span>)<br /><br /> <span style="color: green;">'Check if the object was created.</span><br /> <span style="color: blue;">If</span> Err.Number <> <span style="color: maroon;">0</span> <span style="color: blue;">Then</span><br /> <span style="color: green;">'Return error message.</span><br /> GetMyPublicIP = <span style="color: maroon;">"Could not create the XMLHttpRequest object!"</span><br /> <span style="color: green;">'Release the object and exit.</span><br /> <span style="color: blue;">Set</span> HttpRequest = <span style="color: blue;">Nothing</span><br /> <span style="color: blue;">Exit</span> <span style="color: blue;">Function</span><br /> <span style="color: blue;">End</span> <span style="color: blue;">If</span><br /> <span style="color: blue;">On</span> <span style="color: blue;">Error</span> <span style="color: blue;">GoTo</span> <span style="color: maroon;">0</span><br /> <br /> <span style="color: green;">'Create the request - no special parameters required.</span><br /> HttpRequest.Open <span style="color: maroon;">"GET"</span>, <span style="color: maroon;">"http://myip.dnsomatic.com"</span>, <span style="color: maroon;">False</span><br /> <br /> <span style="color: green;">'Send the request to the site.</span><br /> HttpRequest.Send<br /> <br /> <span style="color: green;">'Return the result of the request (the IP string).</span><br /> GetMyPublicIP = HttpRequest.ResponseText<br /><br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Function</span> GetMyLocalIP() <span style="color: blue;">As</span> <span style="color: blue;">String</span><br /><br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> strComputer <span style="color: blue;">As</span> <span style="color: blue;">String</span><br /> <span style="color: blue;">Dim</span> objWMIService <span style="color: blue;">As</span> <span style="color: blue;">Object</span><br /> <span style="color: blue;">Dim</span> colItems <span style="color: blue;">As</span> <span style="color: blue;">Object</span><br /> <span style="color: blue;">Dim</span> objItem <span style="color: blue;">As</span> <span style="color: blue;">Object</span><br /> <span style="color: blue;">Dim</span> myIPAddress <span style="color: blue;">As</span> <span style="color: blue;">String</span><br /> <br /> <span style="color: green;">'Set the computer.</span><br /> strComputer = <span style="color: maroon;">"."</span><br /> <br /> <span style="color: green;">'The root\cimv2 namespace is used to access the Win32_NetworkAdapterConfiguration class.</span><br /> <span style="color: blue;">Set</span> objWMIService = GetObject(<span style="color: maroon;">"winmgmts:\\"</span> & strComputer & <span style="color: maroon;">"\root\cimv2"</span>)<br /> <br /> <span style="color: green;">'A select query is used to get a collection of IP addresses from the network adapters that have the property IPEnabled equal to true.</span><br /> <span style="color: blue;">Set</span> colItems = objWMIService.ExecQuery(<span style="color: maroon;">"SELECT IPAddress FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True"</span>)<br /> <br /> <span style="color: green;">'Loop through all the objects of the collection and return the first non-empty IP.</span><br /> <span style="color: blue;">For</span> <span style="color: blue;">Each</span> objItem <span style="color: blue;">In</span> colItems<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IsNull(objItem.IPAddress) <span style="color: blue;">Then</span> myIPAddress = Trim(objItem.IPAddress(<span style="color: maroon;">0</span>))<br /> <span style="color: blue;">Exit</span> <span style="color: blue;">For</span><br /> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Return the IP string.</span><br /> GetMyLocalIP = myIPAddress<br /><br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span><br /><br /><span style="color: blue;">Function</span> GetMyMACAddress() <span style="color: blue;">As</span> <span style="color: blue;">String</span><br /><br /> <span style="color: green;">'Declaring the necessary variables.</span><br /> <span style="color: blue;">Dim</span> strComputer <span style="color: blue;">As</span> <span style="color: blue;">String</span><br /> <span style="color: blue;">Dim</span> objWMIService <span style="color: blue;">As</span> <span style="color: blue;">Object</span><br /> <span style="color: blue;">Dim</span> colItems <span style="color: blue;">As</span> <span style="color: blue;">Object</span><br /> <span style="color: blue;">Dim</span> objItem <span style="color: blue;">As</span> <span style="color: blue;">Object</span><br /> <span style="color: blue;">Dim</span> myMACAddress <span style="color: blue;">As</span> <span style="color: blue;">String</span><br /> <br /> <span style="color: green;">'Set the computer.</span><br /> strComputer = <span style="color: maroon;">"."</span><br /> <br /> <span style="color: green;">'The root\cimv2 namespace is used to access the Win32_NetworkAdapterConfiguration class.</span><br /> <span style="color: blue;">Set</span> objWMIService = GetObject(<span style="color: maroon;">"winmgmts:\\"</span> & strComputer & <span style="color: maroon;">"\root\cimv2"</span>)<br /> <br /> <span style="color: green;">'A select query is used to get a collection of network adapters that have the property IPEnabled equal to true.</span><br /> <span style="color: blue;">Set</span> colItems = objWMIService.ExecQuery(<span style="color: maroon;">"SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True"</span>)<br /> <br /> <span style="color: green;">'Loop through all the collection of adapters and return the MAC address of the first adapter that has a non-empty IP.</span><br /> <span style="color: blue;">For</span> <span style="color: blue;">Each</span> objItem <span style="color: blue;">In</span> colItems<br /> <span style="color: blue;">If</span> <span style="color: blue;">Not</span> IsNull(objItem.IPAddress) <span style="color: blue;">Then</span> myMACAddress = objItem.MACAddress<br /> <span style="color: blue;">Exit</span> <span style="color: blue;">For</span><br /> <span style="color: blue;">Next</span><br /> <br /> <span style="color: green;">'Return the IP string.</span><br /> GetMyMACAddress = myMACAddress<br /><br /><span style="color: blue;">End</span> <span style="color: blue;">Function</span></pre><br /><span style="color: red;"><u>Note:</u></span> in the functions I have not included a lot of lines for error handling since I just wanted to show the way that these tasks can be tackled via VBA. <span style="color: orange;">If you need to include these functions to a larger application consider adding error handling according to your project needs.</span></div><div style="text-align: justify;"><br /></div><br /><div style="text-align: justify;"><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Downloads</b></span></h3><hr color="#3d85c6" size="1" width="100%" /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://goo.gl/WdshBJ"><img alt="Download" border="0" height="108" src="https://4.bp.blogspot.com/-JhKlqA-EMkM/UoVNR1_v4II/AAAAAAAABtM/Q1GE7I4707g/s1600/Download.jpg" title="Click to download the file" width="320" /></a></div><br />The file can be opened with <span style="color: red;">Excel 2007</span> or newer. Please enable macros before using it. </div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-10268404347760659342014-11-19T00:34:00.002+02:002018-05-27T01:35:04.616+03:00Small Hydropower Plants – Useful Resources (Files & Links)<div dir="ltr" style="text-align: left;" trbidi="on"><div class="separator" style="clear: both; text-align: center;"><img alt="Small Hydropower Plants – Useful Resources (Files & Links)" border="0" height="640" src="https://2.bp.blogspot.com/-JfXjzDbRjd8/VGvE9IquS7I/AAAAAAAACjk/MrWhE0783RA/s1600/Small%2BHydropower%2BPlants%2B%E2%80%93%2BUseful%2BResources%2B(Files%2B%26%2BLinks).jpg" title="Small Hydropower Plants – Useful Resources (Files & Links)" width="634" /></div><div style="text-align: justify;"><br /></div><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Introduction</span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br />After long time - three and half years (!) – I decided to return to the subject of <a href="http://www.myengineeringworld.net/2011/05/how-to-develop-small-hydropower-site.html">small hydropower plants</a>. <span style="color: red;">This time I tried to collect some useful resources that have helped me in the past during the elaboration of several hydroelectric studies.</span> The files and the links that are given below will help you learn more about the potential and the advantages of small hydropower plants. Moreover, they might be a good starting point for a more thorough investigation of the subject. <br /><br /><div style="text-align: justify;"><br /></div><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;">Contents</span></h3><hr color="#3d85c6" size="1" width="100%" /><br />The zip file that you will find in the downloads section that follows contains the following PDF files:<br /><br /><ol><li>Blue Energy For A Green Europe</li><li>Brochure On Environmental Integration Of Small Hydropower Plants</li><li>Checkist On Small Hydropower</li><li>Innovative Aspects Of Small Hydro Development For Rural Development In Latin America</li><li>Proposal For A European Strategy Of Research, Development And Demonstration For Renewable Energy From SHP</li><li>Report On Statistics - Overview Of The Last Decade 1990-2001</li><li>Reserve Flow - Effects Of Additional Parameters On Depleted Stretch</li><li>Reserve Flow - Short Critical Review Of Methods Of Calculation</li><li>SHERPA - Small Hydropower Argument Sheets</li><li>Small Hydropower - General Framework For Legislation And Authorization Procedures In The EU</li><li>Small Hydropower Engineering - A Wide Area Of Innovations For Green Power Generation</li><li>Small Hydropower Fact Sheets</li><li>Small Hydropower Roadmap</li><li>Small Hydropower Situation In New European Member States And Candidate Countries</li><li>State Of The Art Of Small Hydropower In EU - 25</li><li>Statistical Releases From The Stream Map Project</li></ol><br />The files can be also found at <a href="http://www.esha.be/publications/publications.html">ESHA’s site</a>. I just gather them to a single folder and I created a small index file for easy navigation. Note that the “guide on how to develop a small hydropower plant” is NOT included in the zip file, but it can be downloaded from <a href="http://www.myengineeringworld.net/2011/05/how-to-develop-small-hydropower-site.html">here</a>.<br /><br /><br /><div style="text-align: justify;"><hr color="#3d85c6" size="1" width="100%" /><h3 style="text-align: left;"><span style="color: #3d85c6;"><b>More resources</b></span></h3><hr color="#3d85c6" size="1" width="100%" /></div><br />Apart from ESHA, more information about small hydropower plants can be found in the following links:<br /><ul><li><a href="http://www.canyonhydro.com/resources.html">http://www.canyonhydro.com/resources.html</a></li><li><a href="http://www.repowermap.org/">http://www.repowermap.org/</a></li><li><a href="http://practicalaction.org/docs/technical_information_service/micro_hydro_power.pdf">http://practicalaction.org/docs/technical_information_service/micro_hydro_power.pdf</a></li><li><strike>http://recap.apctt.org/Docs/MicroHydro.pdf</strike></li><li><a href="http://www.usbr.gov/power/edu/pamphlet.pdf">http://www.usbr.gov/power/edu/pamphlet.pdf</a></li><li><a href="http://water.usgs.gov/edu/wuhy.html">http://water.usgs.gov/edu/wuhy.html</a></li><li><a href="http://www.ipcc.ch/pdf/special-reports/srren/Chapter%205%20Hydropower.pdf">http://www.ipcc.ch/pdf/special-reports/srren/Chapter%205%20Hydropower.pdf</a></li><li><a href="http://reliefweb.int/sites/reliefweb.int/files/resources/40F3E613CFE321F1492576FC0023DE59-water-strge-hydropow-supp-grwth.pdf">http://reliefweb.int/sites/reliefweb.int/files/resources/40F3E613CFE321F1492576FC0023DE59-water-strge-hydropow-supp-grwth.pdf</a></li></ul><span style="color: orange;">If you have any other useful resource - file or link - to recommend, feel free to leave a comment and I will update this list as soon as possible. </span><br /><br /><br /><div style="text-align: justify;"><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Downloads</b></span></h3><hr color="#3d85c6" size="1" width="100%" /></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://goo.gl/DSvztM"><img alt="Download" border="0" height="108" src="https://4.bp.blogspot.com/-JhKlqA-EMkM/UoVNR1_v4II/AAAAAAAABtM/Q1GE7I4707g/s1600/Download.jpg" title="Click to download the file" width="320" /></a></div><br />The <span style="color: red;">zip</span> file contains the 16 PDF files that were listed above, plus an index (Excel) file. <br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Read also</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><a href="https://www.myengineeringworld.net/2011/05/how-to-develop-small-hydropower-site.html">How To Develop A Small Hydropower Site</a> <br /><a href="https://www.myengineeringworld.net/2013/09/the-necessary-steps-of-hydroelectric-study.html">Τα Απαιτούμενα Βήματα Για Την Μελέτη Ενός Υδροηλεκτρικού Έργου</a><a href="http://www.myengineeringworld.net/2014/07/insert-blocks-autocad-excel-vba.html"> </a></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.comtag:blogger.com,1999:blog-7473426129661064195.post-5930427673519300372014-11-11T01:42:00.000+02:002018-07-15T18:18:00.556+03:00CodeEval 2 – Mth To The Last Element<div dir="ltr" style="text-align: left;" trbidi="on"><div style="text-align: justify;"><div class="separator" style="clear: both; text-align: center;"><img alt="CodeEval 2 – Mth To Last Element" border="0" height="262" src="https://2.bp.blogspot.com/-HddBkWZzm_w/VGFMVApxOCI/AAAAAAAACjM/1ul7S5TZfyo/s1600/CodeEval%2B2%2B%E2%80%93%2BMth%2BTo%2BLast%2BElement.jpg" title="CodeEval 2 – Mth To Last Element" width="400" /></div></div><div style="text-align: justify;"><br /></div><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>About CodeEval post series</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><div style="text-align: justify;">CodeEval is a series of posts which are different than the typical engineering/Excel/VBA posts that are being published in this blog. <span style="color: red;">The purpose of this series is to demonstrate possible solutions on various <a href="https://www.codeeval.com/">CodeEval</a> programming challenges.</span> Each solution has already been submitted and accepted as valid on CodeEval platform, so if you try to submit the presented solution as it is, you will probably get a <i>“not unique solution”</i> result. <span style="color: orange;">The solutions will be presented in C# language, but the logic/algorithm behind them is similar despite the language you might use.</span></div><div style="text-align: justify;"><br /><br /></div><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Mth to the last element – challenge description</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br /><span style="color: orange;">Write a program which determines the Mth to the last element in a list.</span><br /><br /><div style="text-align: justify;"><span style="color: red;"><u>Input sample</u></span></div><div style="text-align: justify;"><br />The first argument is a path to a file. The file contains the series of space-delimited characters followed by an integer. The integer represents an index in the list (1-based), one per line. Example:<br /><br /><div style="text-align: center;"><span style="color: orange;">a b c d 4<br />e f g h 2</span></div><br /><span style="color: red;"><u>Output sample</u></span><br /><br />Print to stdout (usually Windows Console) the Mth element from the end of the list, one per line. If the index is larger than the number of elements in the list, ignore that input. Example:</div><div style="text-align: justify;"><br /><div style="text-align: center;"><span style="color: orange;">a<br />g</span></div><br />The particular challenge has a relatively low success rate (66.5% - 11/11/2014) and its level of difficulty is medium. More info you can find <a href="https://www.codeeval.com/open_challenges/10">here</a>.<br /><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Solution</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><div style="text-align: justify;"><br />Despite the medium level of difficulty, I would say that this challenge was probably an easy one! Below you will find a working solution:</div><br /><pre><span style="color: blue;">using</span> System;<br /><span style="color: blue;">using</span> System.Collections.Generic;<br /><span style="color: blue;">using</span> System.IO;<br /><span style="color: blue;">using</span> System.Linq;<br /><span style="color: blue;">using</span> System.Text;<br /><br /><span style="color: green;">/*<br />---------------------------------------------------------------------<br />The code below solves the CodeEval challenge - Mth to last element.<br /><br />Written by: Christos Samaras<br />Date: 27/06/2014<br />e-mail: xristos.samaras@gmail.com<br />site: http://www.myengineeringworld.net<br />---------------------------------------------------------------------<br />*/</span><br /><br /><span style="color: blue;">namespace</span> MthToLastElement<br />{<br /> <span style="color: blue;">class</span> Program<br /> {<br /> <span style="color: blue;">static</span> <span style="color: blue;">void</span> Main(<span style="color: blue;">string</span>[] args)<br /> {<br /> <span style="color: blue;">using</span> (StreamReader reader = File.OpenText(args[<span style="color: maroon;">0</span>])) <br /> <span style="color: blue;">while</span> (!reader.EndOfStream)<br /> {<br /> <span style="color: blue;">string</span> line = reader.ReadLine();<br /> <span style="color: blue;">if</span> (line != <span style="color: blue;">null</span>)<br /> {<br /> List<<span style="color: blue;">string</span>> myList = line.Split(<span style="color: maroon;">' '</span>).ToList();<br /> <span style="color: blue;">int</span> mth = <span style="color: blue;">int</span>.Parse(myList[myList.Count - <span style="color: maroon;">1</span>]);<br /> <span style="color: blue;">if</span> (myList.Count > mth)<br /> { <br /> myList.Reverse();<br /> Console.WriteLine(myList[mth]);<br /> }<br /> }<br /> } <br /> Console.ReadLine();<br /> }<br /> }<br />}</pre><br /><br /><hr color="#3d85c6" size="1" width="100%" /><h3><span style="color: #3d85c6;"><b>Points</b></span></h3><hr color="#3d85c6" size="1" width="100%" /><br />Here is the proof that the <span style="color: red;">solution works</span> and the points given by the CodeEval platform.<br /><br /><div class="separator" style="clear: both; text-align: center;"><img alt="CodeEval 2 – Mth To Last Element - Score" border="0" height="180" src="https://4.bp.blogspot.com/-bMYMQHmEznU/VGFLFjvYSxI/AAAAAAAACjE/83J4YQrzq0E/s1600/CodeEval%2B2%2B%E2%80%93%2BMth%2BTo%2BLast%2BElement%2B-%2BScore.jpg" title="CodeEval 2 – Mth To Last Element - Score" width="640" /></div></div></div>Christos Samarashttp://www.blogger.com/profile/18288000065546258696noreply@blogger.com