This program grabs the Google Maps corner coordinates and its image.
Although the code looks fairly straight forward it has been a long and tedious process of finding the correct working (!) code; a lot of trial and even more error.
References to PB-forum discussions on several topics are given in the header of the code.
Although the code looks fairly straight forward it has been a long and tedious process of finding the correct working (!) code; a lot of trial and even more error.
References to PB-forum discussions on several topics are given in the header of the code.
Code:
' Grab Google Maps Corner Coordinates and Image ' ' October 2, 2016 by Henk Spoelstra ' ' Based on example from Dean Goodman: ' https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/53945-google-earth-by-using-cwindow ' ' Code from José Roca for the Webbrowser Control: ' https://forum.powerbasic.com/forum/jose-s-corner/source-code-ab/50858-using-cwindow-to-embed-the-webbrowser-control?p=601436#post601436 ' ' Code from Gary Beene/José Roca for retrieving Webbrowser Events with CLASS CDWebBrowserEvents2: ' https://forum.powerbasic.com/forum/jose-s-corner/discussion/747425-get-text-from-table-cell?p=747484#post747484 ' ' Code from José Roca to use the METHOD DocumentComplete instead of DownloadComplete: ' https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/753161-addwebbrowsercontrol-fires-three-times-with-pwbevents ' ' Other references "how-to" from the internet are shown in the code ' ' Sizes of the Dialog window and Google map may be adjusted by changing the values in this code. ' Google Map does not size with the Dialog window. This should be programmed by yourself. ' ' The grabbed Google Map can be stored by right clicking on the pop-up window. ' For high resolution images consult: https://developers.google.com/maps/documentation/static-maps/intro ' ' The corner coordinates are obtained in the METHOD onclick ' and further processed in the SUB-routine Process_Googlemap in which also ' the image is captured ' ' The display of the image window and handling should be tailored to your own application ' #COMPILE EXE #DIM ALL %UNICODE = 1 %USEWEBBROWSER = 1 %USEOLECON = 1 ' // Use the OLE container #INCLUDE ONCE "CWindow.inc" ' // CWindow class #INCLUDE ONCE "MSHTML.INC" ' // MSHTML %IDC_WEBBROWSER = 1001 'Handle for the Webbrowser with the Google Map %IDC_CapImg = 1002 'Handle for the window with the Google map image FUNCTION PBMAIN '---------- PBMAIN ' // Create an instance of the class GLOBAL pWindow AS IWindow pWindow = CLASS "CWindow" IF ISNOTHING(pWindow) THEN EXIT FUNCTION ' // Initialize lat/lon as center point for the map LOCAL C_Lng, C_Lat AS DOUBLE 'Center coordinates for the initial Google Map LOCAL Zoom AS LONG 'Zoom for the initial Google Map C_Lng= -85.9517238890354 C_Lat= 40.1265386298385 Zoom = 18 'Set amount of zoom of the Google Map ' // Set size of DIALOG, Browser and Google map LOCAL xSzDlg , ySzDlg AS LONG 'Width and height of the Dialog window GLOBAL xSzMap , ySzMap AS LONG 'x-size (width) and y-size (height)of the map in the browser window (is needed in the Javascript and in the SUB Process_GoogleMap) xSzDlg=700 : ySzDlg=700 'Set the x-size (width) and y-size (height) of the Dialog window xSzMap = xSzDlg* 0.9 'Calculate x-size (width) of the map in the Browser window. Is used in the Javascript ySzMap = ySzDlg*0.78 'Calculate y-size (height) of the map in the Browser window. Is used in the Javascript ' // Design of the Javascript code for retrieving the Google Map Corner Coordinates DIM s AS WSTRING s = "<!DOCTYPE html>" 'The <!DOCTYPE> declaration represents the document type, and helps browsers to display web pages correctly. 'It must only appear once, at the top of the page (before any HTML tags). s +="<html>"'& $CRLF 'The <html> element is the root element of an HTML page s +="<head>"'& $CRLF 'The <head> element contains meta information about the document s +="<script"& $CRLF '1st script s +="src='http://maps.googleapis.com/maps/api/js'>"& $CRLF 'invoke googleapi maps s +="</script>" & $CRLF s +="</head>"& $CRLF s +="<script>"& $CRLF '2nd script s += "var map;"& $CRLF s += "function initialize() "& $CRLF 'Set map parameters s += "{"& $CRLF s += "var mapOpt = {"& $CRLF s += " center:new google.maps.LatLng(" & FORMAT$(C_Lat) & "," & FORMAT$(C_Lng) & "),"& $CRLF s += " zoom:"& LTRIM$(STR$(Zoom)) & ","& $CRLF s += " mapTypeId:google.maps.MapTypeId.SATELLITE "& $CRLF s += " };"& $CRLF s += "map=new google.maps.Map(document.getElementById('googleMap'),mapOpt); "& $CRLF 'Make map with settings ('googleMap' is defined further on under the <body> tag) s += "google.maps.event.addDomListener(map, 'center_changed' , function() "& $CRLF 'addDOMLIsterner must be placed here under the function "initialize" otherwise it won't work s += "{" & $CRLF s += "var bounds = map.getBounds();" & $CRLF ' http://stackoverflow.com/questions/6910847/get-boundaries-longitude-and-latitude-from-current-zoom-google-maps s += "var ne = bounds.getNorthEast();"& $CRLF ' LatLng of the north-east corner s += "var sw = bounds.getSouthWest();"& $CRLF ' LatLng of the south-west corder s += "document.getElementById('ne').value = ne;"& $CRLF '.value is needed here instead of .innerHTML see: http://www.w3schools.com/js/tryit.asp?filename=tryjs_function_return s += "document.getElementById('sw').value = sw;"& $CRLF s += "});"& $CRLF s += "}" s +="</script>"& $CRLF s +="<script>" & $CRLF '3rd script s +="function getCorners() "& $CRLF 'Make a "Grab Coordinates"-button and only when clicking this button the coordinates are grabbed. s +="{" & $CRLF s += "var bounds = map.getBounds();"& $CRLF 'http://stackoverflow.com/questions/6910847/get-boundaries-longitude-and-latitude-from-current-zoom-google-maps s += "var ne_c = bounds.getNorthEast(); "& $CRLF ' Get LatLng of the north-east corner s += "var sw_c = bounds.getSouthWest();"& $CRLF ' Get LatLng of the south-west corder s += "document.getElementById('ne_coord').value = ne_c;"& $CRLF s += "document.getElementById('sw_coord').value = sw_c;"& $CRLF s += "var zm = map.getZoom();"& $CRLF ' Get the actual zoom s += "document.getElementById('zoom').value = zm;"& $CRLF s +="}" & $CRLF s +="</script>"& $CRLF s +="<script>"& $CRLF '4th script 'https://www.w3.org/TR/DOM-Level-2-Events/events.html s += "google.maps.event.addDomListener(window, 'load', initialize); "& $CRLF 'see https://developers.google.com/maps/documentation/javascript/events Topic "Listening to DOM Events" s +="</script>"& $CRLF 'or https://developers.google.com/maps/documentation/javascript/examples/event-domListener 'or tutorial http://www.w3schools.com/js/js_htmldom.asp for more info on DOM s +="<body>"& $CRLF s +="<div id='googleMap' style='width:" & LTRIM$(STR$(xSzMap)) & "px;height:" & LTRIM$(STR$(ySzMap)) & "px;margin-left:0px;margin-top:0px'></div>"& $CRLF s +="<br>"& $CRLF s +="<input type='text' id='ne' name='ne' size='75' value='ne'>"& $CRLF 'https://www.experts-exchange.com/questions/28499086/Visual-Studio-2013-Get-latitude-Longitude-from-google-Map-in-windows-project.html#answer40294233 s +="<input type='text' id='sw' name='sw' size='75' value='sw'>"& $CRLF s +="<input type='button' id='Grab coordinates' name='button' value='Grab coordinates' onclick='getCorners()'>"& $CRLF 'MUST BE ONCLICK AS IN THE "CLASS CHTMLDocumentEvents2" THE METHOD is defined as ONCLICK !!! s +="<br>"& $CRLF s +=" <input type='hidden' id='ne_coord' name='ne_result' size='75' value='ne-coordinates'>"& $CRLF s +=" <input type='hidden' id='sw_coord' name='sw_result' size='75' value='sw-coordinates'>"& $CRLF s +=" <input type='hidden' id='zoom' name='zm' size='20' value='zoom'>"& $CRLF s +="</body> "& $CRLF s +="</html> "& $CRLF ' // Save the Javascript as a temporary file LOCAL bstrTempFileName AS WSTRING bstrTempFileName = AfxSaveTempFile(s, "", "TMP", "html", 1) ' // Set the CLASS for capturing EVENTS in the Webbrowser LOCAL pWBEvents AS DWebBrowserEvents2Impl pWBEvents = CLASS "CDWebBrowserEvents2" ' // Create the Dialog/WebBrowser control with the embedded map GLOBAL hDlg AS DWORD 'Reference to Dialog window (which is the webbrowser window) DIALOG NEW PIXELS, 0, "PB with Webbrowser", , , xSzDlg,ySzDlg, %WS_OVERLAPPEDWINDOW TO hDlg 'Make Dialog window pWindow.AddWebBrowserControl(hDlg, %IDC_WEBBROWSER, bstrTempFileName, pWBEvents, 0, 0, xSzDlg, ySzDlg) 'AddWebBrowserControl is a METHOD defined in CWindow.INC 'pWBEvents is a call to the Events '// Show Dialog and start the Callback Processor DIALOG SHOW MODAL hDlg, CALL DlgProc END FUNCTION '---------- END PBMAIN ' ======================================================================================== ' Main Dialog CALLBACK procedure ' ======================================================================================== CALLBACK FUNCTION DlgProc() AS LONG SELECT CASE CB.MSG CASE %WM_INITDIALOG DIALOG ENABLE hDlg 'CASE %WM_COMMAND ' SELECT CASE AS LONG CB.CTL ' CASE %IDC_BUTTON ' IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN ' MSGBOX "CLICK" ' END IF ' ... 'END SELECT CASE %WM_SIZE IF CB.WPARAM <> %SIZE_MINIMIZED THEN ' ' When RESIZING the browser window. The Google Map size stays the same. ' If the Google Map need to be resized as well then teh Javascript must be run again. ' END IF CASE %WM_DESTROY hdlg=0 END SELECT END FUNCTION ' ======================================================================================== ' ######################################################################################## ' Class CDWebBrowserEvents2 ' Interface name = DWebBrowserEvents2 ' IID = {34A715A0-6587-11D0-924A-0020AFC7AC4D} ' Web Browser Control events interface ' Attributes = 4112 [&H1010] [Hidden] [Dispatchable] ' ######################################################################################## CLASS CDWebBrowserEvents2 GUID$("{700B73A2-CCFC-4FE0-B9AC-D5853D71B7B9}") AS EVENT INSTANCE pIWebBrowser2 AS IWebBrowser2 INSTANCE pHTMLDocumentEvents2 AS HTMLDocumentEvents2Impl ' ===================================================================================== CLASS METHOD DESTROY ' Disconnect events IF ISOBJECT(pHTMLDocumentEvents2) THEN EVENTS END pHTMLDocumentEvents2 END METHOD ' ===================================================================================== ' ======================================================================================== ' Implementation of the interface ' ======================================================================================== INTERFACE DWebBrowserEvents2Impl GUID$("{34A715A0-6587-11D0-924A-0020AFC7AC4D}") AS EVENT INHERIT IDISPATCH ' ===================================================================================== ' Note (October 2, 2016): ' The METHOD DocumentComplete has been placed here as the original METHOD DownloadComplete ' fires three times. See: ' https://forum.powerbasic.com/forum/user-to-user-discussions/powerbasic-for-windows/753161-addwebbrowsercontrol-fires-three-times-with-pwbevents ' However the DocumentComplete does not fire when the Webbrowser is not visible. ' See: BUG: DocumentComplete Does Not Fire When WebBrowser Is Not Visible ' http://support.microsoft.com/kb/q259935/ ' ===================================================================================== METHOD DocumentComplete <259> ( _ BYVAL pDisp AS IDISPATCH _ ' __in IDispatch* pDisp , BYREF vURL AS VARIANT _ ' __in VARIANT* URL ) ' void ' Get a reference to the IHTMLDocument2 interface LOCAL pHTMLDocument2 AS IHTMLDocument2 pHTMLDocument2 = pIWebBrowser2.Document IF ISNOTHING(pHTMLDocument2) THEN EXIT METHOD ' Connect to the events fired by the page pHTMLDocumentEvents2 = CLASS "CHTMLDocumentEvents2" IF ISNOTHING(pHTMLDocumentEvents2) THEN EXIT METHOD EVENTS FROM pHTMLDocument2 CALL pHTMLDocumentEvents2 END METHOD ' ===================================================================================== ' ===================================================================================== METHOD BeforeNavigate2 <250> ( _ BYVAL pDisp AS IDISPATCH _ ' __in IDispatch* pDisp , BYREF vURL AS VARIANT _ ' __in VARIANT* URL , BYREF vFlags AS VARIANT _ ' __in VARIANT* Flags , BYREF vTargetFrameName AS VARIANT _ ' __in VARIANT* TargetFrameName , BYREF vPostData AS VARIANT _ ' __in VARIANT* PostData , BYREF vHeaders AS VARIANT _ ' __in VARIANT* Headers , BYREF bCancel AS INTEGER _ ' __in_out VARIANT_BOOL* Cancel ) ' void ' Get a reference to the WebBrowser control IF ISNOTHING(pIWebBrowser2) THEN pIWebBrowser2 = pDisp IF ISNOTHING(pIWebBrowser2) THEN EXIT METHOD ' If there was a previous loaded page, disconnect from the events IF ISOBJECT(pHTMLDocumentEvents2) THEN EVENTS END pHTMLDocumentEvents2 pHTMLDocumentEvents2 = NOTHING END IF END METHOD ' ===================================================================================== END INTERFACE END CLASS ' ######################################################################################## ' ######################################################################################## ' Class CHTMLDocumentEvents2 ' Interface name = HTMLDocumentEvents2 ' IID = {3050F613-98B5-11CF-BB82-00AA00BDCE0B} ' Attributes = 4112 [&H1010] [Hidden] [Dispatchable] ' ######################################################################################## CLASS CHTMLDocumentEvents2 GUID$("{1FFB0071-8BCC-4BBD-BC29-A662FAE87C82}") AS EVENT INTERFACE HTMLDocumentEvents2Impl GUID$("{3050F613-98B5-11CF-BB82-00AA00BDCE0B}") AS EVENT INHERIT IDISPATCH ' ===================================================================================== METHOD onclick <-600> ( _ BYVAL pEvtObj AS IHTMLEventObj _ ' __in IHTMLEventObj* pEvtObj ) ' void LOCAL pElement AS IHTMLElement ' // Element that has fired the event LOCAL pHTMLDocument2 AS IHTMLDocument2 ' // Document object LOCAL bstrId AS WSTRING ' // Identifier of the element that has fired the event LOCAL bstrValue AS WSTRING ' // Value of the property ' // Get a reference to the element that has fired the event IF ISOBJECT(pEvtObj) THEN pElement = pEvtObj.srcElement IF ISNOTHING(pElement) THEN EXIT METHOD ' // Get a reference to the IHTMLDocument2 interface pHTMLDocument2 = pElement.document IF ISNOTHING(pHTMLDocument2) THEN EXIT METHOD ' // Get the identifier of the element that has fired the event bstrId = pElement.id 'MSGBOX bstrId LOCAL NE_Coord_STR AS STRING 'Grabbed NE Coordinates as string LOCAL SW_Coord_STR AS STRING 'Grabbed SW Coordinates as string LOCAL Zoom_STR AS STRING 'Grabbed Zoom as string SELECT CASE bstrId 'MSGBOX bstrId CASE "Grab coordinates" bstrValue = IHTMLDocument_getElementValueById(pHTMLDocument2, "ne_coord") NE_Coord_STR=bstrValue bstrValue = IHTMLDocument_getElementValueById(pHTMLDocument2, "sw_coord") SW_Coord_STR=bstrValue bstrValue = IHTMLDocument_getElementValueById(pHTMLDocument2, "zm") Zoom_STR=bstrValue CALL Process_GoogleMap(NE_Coord_STR,SW_Coord_STR,Zoom_STR) END SELECT END METHOD ' ===================================================================================== END INTERFACE END CLASS ' ######################################################################################## '---------------- SUB Process_GoogleMap ' ' Retrieves lat/lng from string coordinates ' Grabs the GoogleMap image ' SUB Process_GoogleMap(BYVAL NE_Coord_STR AS STRING, BYVAL SW_Coord_STR AS STRING, BYVAL Zoom_STR AS STRING) LOCAL CommaPos AS LONG LOCAL NE_LatStr, NE_LngStr, SW_LatStr, SW_LngStr AS STRING LOCAL NE_Lat, NE_lng, SW_Lat, SW_Lng AS DOUBLE CommaPos = INSTR(NE_Coord_STR, ",") 'Find the comma in the string NE_LatStr = LEFT$(NE_Coord_STR,Commapos-1) 'Take the left portion up to the comma NE_Lat = VAL(MID$(Ne_LatStr,2,LEN(NE_LatStr))) NE_LngStr = RIGHT$(NE_Coord_STR,Commapos) 'Take the right portion from the comma to the end NE_Lng = VAL(MID$(Ne_LngStr,1,LEN(NE_LngStr)-1)) SW_LatStr = LEFT$(SW_Coord_STR,Commapos-1) 'Take the left portion up to the comma SW_Lat = VAL(MID$(SW_LatStr,2,LEN(SW_LatStr))) SW_LngStr = RIGHT$(SW_Coord_STR,Commapos) 'Take the right portion from the comma to the end SW_Lng = VAL(MID$(SW_LngStr,1,LEN(SW_LngStr)-1)) ' // Google Map position stays the same when the Dialog is enlarged (in this version of the program). ' // Calculate center of the map (Center coordinates needed to grab the image) LOCAL C_Lat, C_Lng AS DOUBLE C_Lat=(SW_Lat+NE_Lat)/2 C_Lng=(SW_Lng+NE_Lng)/2 'For Lng around +180/-180 another calculation is needed (not implemented yet) ' // Define and set size of captured image window LOCAL xPosCapImg, yPosCapImg, xSzCapImg, ySzCapImg AS LONG xPosCapImg=0 'x position of the captured image yPosCapImg=0 'y position of the captured image xSzCapImg=1.1*xSzMap 'x-size (width) of the captured image window. Factor 1.1 just taken to show the image inside the popup window ySzCapImg=1.1*ySzMap 'y-size (height) of the captured image window 'Make a Javascript for catching a static Google Map LOCAL s AS WSTRING s +="<!DOCTYPE html>" 'Script from https://seydahatipoglu.wordpress.com/2012/10/26/export-google-map-to-image/ s +="<html>"& $CRLF s +="<head runat='server'>"& $CRLF s +="</head>"& $CRLF s +="<body>"& $CRLF s +="<script>"& $CRLF '1st script s +="function refreshMapUrl()"& $CRLF s +="{"& $CRLF s += "var mapImage = document.getElementById('imgMap');"& $CRLF s += "var mapUrl = 'http://maps.googleapis.com/maps/api/staticmap?" 'See for options: https://developers.google.com/maps/documentation/static-maps/intro s += "center=" & FORMAT$(C_Lat) & "," & FORMAT$(C_Lng) &"&" s += "zoom=" & Zoom_STR & "&" s += "size=" & FORMAT$(xSzMap) & "x" & FORMAT$(ySzMap)&"&" s += "&maptype=satellite&" s += "sensor=false';"& $CRLF s += "mapImage.src=mapUrl;"& $CRLF s +="}"& $CRLF s +="</script>"& $CRLF s +="<img alt='' src='' id='imgMap' />"& $CRLF s +="<script>"& $CRLF '2nd script calls the defined function in the 1st script s +="refreshMapUrl();"& $CRLF s +="</script>"& $CRLF s +="</body>"& $CRLF s +="</html>"& $CRLF ' // Save the Javascript for grabbing the image as a temporary file LOCAL bstrTempFileName AS WSTRING bstrTempFileName = AfxSaveTempFile(s, "", "TMP", "html", 1) '//Create popup window with the grabbed Google Map image DIM hImg AS GLOBAL DWORD 'Handle for the image window (is used in the CALLBACK and SUB Process_Googlemap DIALOG NEW PIXELS, hImg, "Grabbed Google Map image",-10 ,10 , xSzCapImg,ySzCapImg, %WS_CAPTION OR %WS_SYSMENU TO hImg 'Make Captured Image window pWindow.AddWebBrowserControl(hImg, %IDC_CapImg , bstrTempFileName, NOTHING, 0, 0, xSzCapImg, ySzCapImg) 'AddWebBrowserControl is a METHOD defined in CWindow.INC DIALOG SHOW MODAL hImg, CALL DlgProc 'Show Dialog and start the Callback Processor END SUB '---------------- END Process_GoogleMap ' ======================================================================================== ' Image CALLBACK procedure (not used) ' ======================================================================================== CALLBACK FUNCTION ImgProc() AS LONG SELECT CASE CB.MSG CASE %WM_INITDIALOG CASE %WM_COMMAND SELECT CASE AS LONG CB.CTL ' CASE %IDC_BUTTON ' IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN ' MSGBOX "CLICK" ' END IF ' ... END SELECT 'CASE %WM_SIZE 'IF CB.WPARAM <> %SIZE_MINIMIZED THEN ' LOCAL xSzDlgNew, ySzDlgNew AS LONG ' DIALOG GET CLIENT CB.HNDL TO xSzDlgNew, ySzDlgNew ' CONTROL SET SIZE CB.HNDL, %IDC_WEBBROWSER, xSzBrowNew, ySzBrowNew 'END IF CASE %WM_DESTROY PostQuitMessage 0 END SELECT END FUNCTION ' ========================================================================================
Comment