Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

An Idea How to Use Google Maps

4.95/5 (20 votes)
29 Oct 2015CPOL5 min read 24.8K   1.4K  
Web Control - Google Maps

Image 1

Introduction

This is my second project to learn C/C++ by programming previously performed MASM projects. The first was An Idea How to Use RichEdit50W for Syntax Highlighting, etc. For those interested, you can read the ASM code on my website (minor28.divdev.se). The two project's files, functions and variables are on the whole consistent so it would be quite easy to follow.

Application

This application is a main dialog application.

C++
::DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_MAIN),0,MainDlgProc,0);

The first start of the application is done with the default values for position (Stockholm Central Station), Google maps hybrid, Zoom 4.

The application is controlled with the buttons in the upper toolbar and with popup menus. The clock is set by either clicking on the clock face gradation or with the Set time menu item in a popup menu.

The buttons functionality are:

  • Set Today resets time and date to the current value.
  • Open Solar Form opens a dialog with the solar for the selected position and selected date and time. The solar elevation and azimuth at current time is visualized in two separate dialog boxes.
  • Show Azimuth visualizes the solar location on the map image.
  • Save as home and Save Map data saves map data to the registry.
  • Go home restores the marker to home position.
  • Street View toggles between Map View and Street View.
  • - Zoom + controls the zooming value of the map.
  • Open route planner enables planning of routes by clicking on the map to mark waypoints. Distance and course are calculated and are presented in a table that appears over the clock. Editing the route is done using a popup menu.
  • Connect GPS searches for a serial port where the GPS is connected. If a GPS is found, illuminates the red traffic light to the right of the button. The yellow and green traffic light illuminates when a fix is found and the DOP is greater than five. The yellow light goes out when DOP is less than five.

The application has three modes, Time, Route and GPS mode.

Some application data are stored in the registry (HKEY_CURRENT_USER\Software\MINOR28\Solar).

Files:

  • Solar.cpp
  • ApplicationData.cpp

Time Zone Data

In time mode, a left button click on the map will move the red marker to the new position and following data are retrieved from googleapis.com:

  • daylight saving time
  • GMT time offset
  • time zone id
  • time zone name

These are the main code lines:

C++
::CLSIDFromProgID(L"Msxml2.XMLHTTP.6.0",&clsid)
::CoCreateInstance(clsid,0,CLSCTX_INPROC_SERVER,IID_IUnknown,(LPVOID*)&pXMLHTTPrequest)
pXMLHTTPrequest->open(L"GET",(BSTR)&bufferW,var,var0,var0)
pXMLHTTPrequest->send(var)
pXMLHTTPrequest->get_responseText(&pbstrBody)

bufferW contains the URL to googleapis.com and pbstrBody contains the time zone data.

Files:

  • GetTimeZoneData.cpp

Time and Date

The clock face is drawn and a new thread is started to run the clock.

C++
//The analog clock
hClock = ::CreateWindowEx(0,"static",0,SS_BITMAP + WS_CHILD + WS_VISIBLE,
		15,150,220,220,hWin,(HMENU)IDC_PIC1,hInst,0);
//start the clock
SetClockToday();
DWORD ThreadId;
::CloseHandle(::CreateThread(0,0,UpdateClock,0,0,&ThreadId));

DWORD WINAPI UpdateClock(LPVOID Param)
{
	if (ClockTimer != 0){
		::KillTimer(hMainDlg,ClockTimer);
	}
	ClockTimer = ::SetTimer(hMainDlg,ID_TIMER,1000,(TIMERPROC)&Clock);

    return 0;
}
VOID CALLBACK Clock(HWND hWin,UINT uMsg,UINT idEvent,DWORD dwTime)
{
	if (uMsg == WM_TIMER){
		Adjustment(&UTCDate);
		Adjustment(&LocalDate);
		DrawClockHandles();
	}
	return;
}

Startup time is retrieved from:

C++
::GetSystemTime(&time);

I have found that in comparison to the pc clock the application clock is 1 to 2 seconds after.

Files:

  • Clock.cpp
  • DrawTheClock.cpp

Web Browser

The web browser is the MS WebBrowser Control (Internet Explorer). Google maps suddenly stopped working with Internet Explorer and Firefox. It worked only with Chrome. The problem was solved by using a Java version no higher than 3.19 (http://maps.google.com/maps/api/js?v=3.19).

The webbrowser window is a static control with a custom class.

C++
//The webbrowser window
CreateWebClass();
::CreateWindowEx(0,"WebClass",0,WS_CHILD + WS_BORDER + WS_VISIBLE,
		0,0,0,0,hWin,(HMENU)IDC_WEB,hInst,0);

This is part of the WebClass window process.

C++
if (message == WM_CREATE){
	hWeb = hWin;
	CreateWebBrowser(hWin);
	::EnumChildWindows(hWin,&EnumChildProc,0);
}

These messages handle GPS plotting:

C++
else if (message == WM_STARTPLOTT)
else if (message == WM_PLOTT)
else if (message == WM_ENDPLOTT)

This message handles route planning:

C++
else if (message == WM_COMMAND)

This is the webbrowser control:

C++
VOID CreateWebBrowser(HWND hWin)
{
	//Create a pointer to CoClass WebBrowser
	if (::CLSIDFromString(L"{8856F961-340A-11D0-A96B-00C04FD705A2}",&clsid) != S_OK
	if (::CoCreateInstance(clsid,0,CLSCTX_INPROC_SERVER,IID_IUnknown,
			(LPVOID*)&pWebBrowser) != S_OK
	//Set pointer to WebBrowser implemented interface IWebBrowser2
	pWebBrowser->QueryInterface(IID_IWebBrowser2,(LPVOID*)&pWebBrowser2);
	//Connect the browser to IOleObject
	if (pWebBrowser2->QueryInterface(IID_IOleObject,(LPVOID*)&pOleObject) != S_OK
	//ClientSite
	if (pOleObject->SetClientSite((IOleClientSite*)&ClientSite.pOleClientSite) != S_OK

To open Google Maps, you first navigate to a blank page to create an empty document.

C++
//Open a blank page
VARIANT var;
var.vt = VT_BSTR;
var.bstrVal = ::SysAllocString(L"about:blank");
pWebBrowser2->Navigate2(&var,0,0,0,0);
::SysFreeString(var.bstrVal);

Then stream the html-code to the browser document.

C++
//Create the stream from HTML code
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE + GMEM_NODISCARD,0);
if (hMem){
	if (CreateHTMLCode(&hMem)){
		IStream* pStream;
		if (::CreateStreamOnHGlobal(hMem,TRUE,&pStream) == S_OK){
			//Load the stream
			if (pPersistStreamInit->Load(pStream) == S_OK){
				//Make the browser connectable
				AdviseEvent(pWebBrowser2,DIID_DWebBrowserEvents2);
				pPersistStreamInit->Release();
			}
			pStream->Release();
		}
	}
	::GlobalFree(hMem);
}

The ClientSite was tricky. This is my definition of the clientsite:

C++
typedef struct tagCLIENTSITE
{
	_IDispatch* pDispatch;
	_IDispatch Dispatch;
	_IOleClientSite* pOleClientSite;
	_IOleClientSite OleClientSite;
	_IOleInPlaceSite* pOleInPlaceSite;
	_IOleInPlaceSite OleInPlaceSite;
	_IDocHostUIHandler* pDocHostUIHandler;
	_IDocHostUIHandler DocHostUIHandler;
	LONG ref;
}CLIENTSITE, *LPCLIENTSITE;

All communication between the application and Google Maps is done with the ExecuteScript function.

C++
VOID ExecuteScript(LPSTR pszScript,LPSTR pszRetVal)
{
	IHTMLDocument *pHtmlDocument;
	if (pWebBrowser2->get_Document((IDispatch**)&pHtmlDocument) == S_OK){
		DispHTMLDocument* pDispHTMLDocument;
		if (pHtmlDocument->QueryInterface(DIID_DispHTMLDocument,
		    (VOID**)&pDispHTMLDocument) == S_OK){
			pDispHTMLDocument->AddRef();
			OLECHAR* member = L"parentWindow";
			LCID lcid = ::GetThreadLocale();
			DISPID memid;
			if (pDispHTMLDocument->GetIDsOfNames(IID_NULL,&member,1,lcid,&memid) == S_OK){
				DISPPARAMS dp;
				::memset(&dp,0,sizeof(dp));
				VARIANT Window;
				if (pDispHTMLDocument->Invoke(memid,IID_NULL,lcid,
						DISPATCH_PROPERTYGET,&dp,&Window,0,0) == S_OK){
					IHTMLWindow2* pHtmlWindow2 = (IHTMLWindow2*)Window.pdispVal;
					WCHAR buffer[256];
					::MultiByteToWideChar(CP_ACP,0,pszScript,-1,
								(LPWSTR)&buffer,sizeof(buffer));
					VARIANT RetVal;
					//pHtmlWindow2->execScript(...) causes a runtime error!!!!!
					//pHtmlWindow2->execScript((LPWSTR)&buffer,L"JavaScript",&RetVal);
					//Another solution below.
					member = L"execScript";
					if (pHtmlWindow2->GetIDsOfNames(IID_NULL,&member,1,lcid,&memid) == S_OK){
						dp.cArgs = 2;
						dp.rgvarg = (VARIANTARG*)::malloc(512);
						dp.rgvarg[0].vt = VT_BSTR;
						dp.rgvarg[0].bstrVal = ::SysAllocString(L"JavaScript");
						dp.rgvarg[1].vt = VT_BSTR;
						dp.rgvarg[1].bstrVal =
								::SysAllocString((const OLECHAR*)&buffer);
						pHtmlWindow2->Invoke(memid,IID_NULL,lcid,
										DISPATCH_METHOD,&dp,&RetVal,0,0);
						::SysFreeString(dp.rgvarg[0].bstrVal);
						::SysFreeString(dp.rgvarg[1].bstrVal);
						::free(dp.rgvarg);
					}
					pHtmlWindow2->Release();
				}
			}
			pDispHTMLDocument->Release();

			if (pszRetVal){
				RetrieveInputValue(pszRetVal);
			}
		}
		pHtmlDocument->Release();
	}
	return;
}

Files:

  • ClientSiteMethods.cpp
  • WebBrowser.cpp
  • WebWindow.cpp

Solar Calculations

I'm not an astronomer so the calculation is based on Internet searches and my interpretations to write the calculations in the assembly language. Now I have translated them into C/C++ code. The data are mainly derived from NOAA's website.

Disclaimer:

C++
//;;The calculations are based on equations from NOAA which are
//;; based on equations from Astronomical Algorithms, by Jean Meeus.
//;;
//;;The calculations are provided 'as-is', without any warranty
//;;or support, expressed or implied. In no event will the author
//;;be held liable for any damages arising from the use of
//;;these calculations.

The results of the calculations are the basis for the images of solar curve, solar altitude and solar azimuth.

Files:

  • DrawAzimuth.cpp
  • DrawDiagram.cpp
  • SolarAzimuthDlg.cpp
  • SolarCalculations.cpp
  • SolarDlg.cpp
  • SolarElevationDlg.cpp

Route Planner

You can create a route and edit it, but not save it. Distances in meter.

Sample Image - maximum width is 600 pixels

C++
VOID CalcCourseDistance(UINT curMarkerIndex)
{
	UINT row = curMarkerIndex + 1;
	double k = DegToRad;

	//Calculate distance
	double dLat = CurDistMarker.Lat - PrevDistMarker.Lat;
	double dLong = CurDistMarker.Lng - PrevDistMarker.Lng;
	double A = pow(sin(dLat/2 *k),2) + cos(CurDistMarker.Lat * k)
		* cos(PrevDistMarker.Lat * k) * pow(sin(dLong / 2 * k),2);
	double C = 2 * atan2(sqrt(A),sqrt(1 - A));
	double Dist = C * 6371000;
	CHAR buf[128];
	::wsprintf((LPSTR)&buf,"%d",(int)Dist);
	ThousandsSeparator((LPSTR)&buf);
	putTextMatrix(hLSV1,row,2,(LPSTR)&buf);

	//Calculate course
	A = atan2(dLong / dLat, 1) * RadToDeg;
	if (dLat < 0){
		A += 180;
	}
	else if (dLong < 0){
		A += 360;
	}
	::sprintf_s((LPSTR)&buf,sizeof(buf),"%0.1f",A);
	putTextMatrix(hLSV1,row,1,(LPSTR)&buf);

	return;
}

GPS

The program presupposes a GPS with baud rate 4800 and bytesize 8.

C++
::memset(&dcb,0,sizeof(dcb));
dcb.DCBlength = sizeof(dcb);
dcb.BaudRate = 4800;
dcb.ByteSize = 8;

The Connect GPS button will start a search in all serial ports for a connected GPS. If a GPS is found, this com port is saved to registry and the red trafficlight is illuminated.

A separate thread is created to handle the data from the GPS. There is a menu item to enable monitoring of NMEA sentences. For plotting actions, two sentences are parsed. Recommended minimum GPS data ($GPRMC) and Overall satellite data ($GPGSA).

$GPRMC sentence is used to retrieve:

  • data status A (active OK) or V (void),
  • latitude in format 5925.5148,
  • north (N) or south (S) hemisphere,
  • longitude in format 01820.5842 and
  • west (W) or east (E) of prime meridian.

$GPGSA sentence is used to retrieve mode and dilution of precision (DOP).

Mode values:

  1. no fix and red trafficlight,
  2. 2D fix and yellow and green trafficlight and
  3. 3D fix and just the green trafficlight.

Files:

  • GPS.cpp

Assembly vs C/C++

The big difference is the Automation coding. Much easier in C++. To facilitate automation coding in assembly, I have developed a tool (Automation Tool) with a static library (Automation static library).

In my last article, I wrote that calculations were much easier to carry out in C++. Now I have revised that opinion. The biggest difference is that C++ generates fewer but longer lines of text and MASM many but shorter lines of text.

Concluding Remarks

You may take it as it is. The application is probably not free from bugs. I think I have structured files and functions so it should be easy to adapt the code to your own needs.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)