一个如何使用 Google Maps 的想法
Web 控件 - Google Maps
引言
这是我通过编程学习 C/C++ 的第二个项目,之前执行过 MASM 项目。第一个项目是《一个使用 RichEdit50W 进行语法高亮等的想法》。对于感兴趣的人,可以在我的网站 (minor28.divdev.se) 上阅读 ASM 代码。这两个项目的代码文件、函数和变量在很大程度上是一致的,因此很容易理解。
Application
此应用程序是一个主对话框应用程序。
::DialogBoxParam(hInstance,MAKEINTRESOURCE(IDD_MAIN),0,MainDlgProc,0);
应用程序的首次启动是使用默认值进行的,位置(斯德哥尔摩中央车站),谷歌地图模式为 hybrid
,缩放级别为 4
。
应用程序通过顶部工具栏的按钮和弹出菜单进行控制。时钟可以通过单击时钟刻度盘或弹出菜单中的 Set time
菜单项来设置。
按钮功能如下:
- Set Today 将时间重置为当前值。
- Open Solar Form 打开一个对话框,显示所选位置和所选日期时间的太阳信息。当前时间的太阳高度角和方位角将在两个单独的对话框中可视化。
- Show Azimuth 在地图图像上可视化太阳位置。
- Save as home 和 Save Map data 将地图数据保存到注册表。
- Go home 将标记恢复到主页位置。
- Street View 在地图视图和街景视图之间切换。
- - Zoom + 控制地图的缩放值。
- Open route planner 通过单击地图标记航点来启用路线规划。距离和航线将被计算并显示在出现在时钟上方的表格中。使用弹出菜单编辑路线。
- Connect GPS 搜索已连接 GPS 的串行端口。如果找到 GPS,则点亮按钮右侧的红色交通信号灯。当定位并 DOP 大于五时,黄色和绿色交通信号灯会亮起。当 DOP 小于五时,黄灯熄灭。
该应用程序有三种模式:时间模式、路线模式和 GPS 模式。
部分应用程序数据存储在注册表中 (HKEY_CURRENT_USER\Software\MINOR28\Solar)。
文件
- Solar.cpp
- ApplicationData.cpp
时区数据
在时间模式下,在地图上单击鼠标左键会将红色标记移动到新位置,并从 googleapis.com
中检索以下数据:
- 夏令时
- GMT 时间偏移
- 时区 ID
- 时区名称
这些是主要的几行代码:
::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
包含 googleapis.com 的 URL,而 pbstrBody
包含时区数据。
文件
- GetTimeZoneData.cpp
时间与日期
绘制时钟界面,并启动一个新线程来运行时钟。
//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;
}
启动时间从以下来源检索:
::GetSystemTime(&time);
我发现与 PC 时钟相比,应用程序时钟落后 1 到 2 秒。
文件
- Clock.cpp
- DrawTheClock.cpp
Web 浏览器
网页浏览器是 MS WebBrowser Control
(Internet Explorer)。谷歌地图突然停止支持 Internet Explorer 和 Firefox。它只在 Chrome 中工作。问题通过使用不高于 3.19 的 Java 版本(http://maps.google.com/maps/api/js?v=3.19)得到解决。
网页浏览器窗口是一个具有自定义类的静态控件。
//The webbrowser window
CreateWebClass();
::CreateWindowEx(0,"WebClass",0,WS_CHILD + WS_BORDER + WS_VISIBLE,
0,0,0,0,hWin,(HMENU)IDC_WEB,hInst,0);
这是 WebClass
窗口过程的一部分。
if (message == WM_CREATE){
hWeb = hWin;
CreateWebBrowser(hWin);
::EnumChildWindows(hWin,&EnumChildProc,0);
}
这些消息处理 GPS 绘图。
else if (message == WM_STARTPLOTT)
else if (message == WM_PLOTT)
else if (message == WM_ENDPLOTT)
此消息处理路线规划。
else if (message == WM_COMMAND)
这是网页浏览器控件。
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
要打开谷歌地图,您首先导航到一个空白页面以创建空的文档。
//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);
然后将 HTML 代码流式传输到浏览器文档。
//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);
}
ClientSite
曾经很棘手。这是我对 clientsite
的定义。
typedef struct tagCLIENTSITE
{
_IDispatch* pDispatch;
_IDispatch Dispatch;
_IOleClientSite* pOleClientSite;
_IOleClientSite OleClientSite;
_IOleInPlaceSite* pOleInPlaceSite;
_IOleInPlaceSite OleInPlaceSite;
_IDocHostUIHandler* pDocHostUIHandler;
_IDocHostUIHandler DocHostUIHandler;
LONG ref;
}CLIENTSITE, *LPCLIENTSITE;
应用程序与谷歌地图之间的所有通信都通过 ExecuteScript
函数进行。
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;
}
文件
- ClientSiteMethods.cpp
- WebBrowser.cpp
- WebWindow.cpp
太阳计算
我不是天文学家,所以计算是基于互联网搜索和我的理解,然后用汇编语言编写计算。现在我已经将它们翻译成 C/C++ 代码。数据主要来自 NOAA 的网站。
免责声明
//;;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.
计算结果是太阳曲线、太阳高度角和太阳方位角图像的基础。
文件
- DrawAzimuth.cpp
- DrawDiagram.cpp
- SolarAzimuthDlg.cpp
- SolarCalculations.cpp
- SolarDlg.cpp
- SolarElevationDlg.cpp
路线规划器
您可以创建一条路线并对其进行编辑,但无法保存。距离单位为米。
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
程序假定 GPS 的波特率为 4800,数据位为 8。
::memset(&dcb,0,sizeof(dcb));
dcb.DCBlength = sizeof(dcb);
dcb.BaudRate = 4800;
dcb.ByteSize = 8;
Connect GPS
按钮将开始搜索所有串行端口以查找已连接的 GPS。如果找到 GPS,则将此 COM 端口保存到注册表,并点亮红色交通信号灯。
创建一个单独的线程来处理来自 GPS 的数据。有一个菜单项可以启用对 NMEA 句子的监控。对于绘图操作,解析两个句子:推荐的最小 GPS 数据 ($GPRMC)
和 总体卫星数据 ($GPGSA)
。
$GPRMC
句子用于检索:
- 数据状态 A (活动 OK) 或 V (无效),
- 纬度格式为 5925.5148,
- 北半球 (N) 或南半球 (S),
- 经度格式为 01820.5842,以及
- 本初子午线以西 (W) 或以东 (E)。
$GPGSA
句子用于检索模式和 精度稀释 (DOP)
。
模式值:
- 无定位,红色交通信号灯;
- 2D 定位,黄色和绿色交通信号灯;
- 3D 定位,仅绿色交通信号灯。
文件
- GPS.cpp
汇编与 C/C++
最大的区别在于自动化编码。在 C++ 中更容易实现。为了方便汇编语言的自动化编码,我开发了一个工具(Automation Tool)和一个静态库(Automation static library)。
在我上一篇文章中,我写道 C++ 计算起来更容易。现在我修正了我的看法。最大的区别是 C++ 生成的文本行较少但较长,而 MASM 生成的文本行较多但较短。
结论
您可以按原样使用。该应用程序可能并非没有错误。我认为我已将文件和函数结构化,因此应该很容易根据您的需求调整代码。