目次
サンプル・プログラム
earthquakewin.msi | インストーラ |
bin/earthquakewin.exe | 実行プログラム本体 |
bin/cwebpage.dll bin/libcurl.dll | 実行時に必要になるDLL |
bin/etc/help.chm | ヘルプ・ファイル |
sour/earthquakewin.cpp | ソース・プログラム |
sour/resource.h | リソース・ヘッダ |
sour/resource.rc | リソース・ファイル |
sour/mystrings.cpp | 汎用文字列処理関数など(ソース) |
sour/mystrings.h | 汎用文字列処理関数など(ヘッダ) |
sour/pahooGeocode.cpp | 住所・緯度・経度に関わるクラス(ソース) |
sour/pahooGeocode.hpp | 住所・緯度・経度に関わるクラス(ヘッダ) |
sour/apikey.cpp | APIキーの管理(ソース) |
sour/apikey.hpp | APIキーの管理(ヘッダ) |
sour/webbrowser.hpp | Webブラウザ・クラス(ヘッダ) |
sour/pahooCache.cpp | キャッシュ処理に関わるクラス(ソース) |
sour/pahooCache.hpp | キャッシュ処理に関わるクラス(ヘッダ) |
sour/makefile | ビルド |
バージョン | 更新日 | 内容 |
---|---|---|
3.4.5 | 2024/03/16 | 使用ライブラリ更新 |
3.4.4 | 2023/11/11 | 使用ライブラリ更新 |
3.4.3 | 2023/07/02 | 使用ライブラリ更新 |
3.4.2 | 2023/03/18 | 使用ライブラリ更新 |
3.4.1 | 2022/11/12 | 使用ライブラリ更新 |
バージョン | 更新日 | 内容 |
---|---|---|
1.6.0 | 2023/07/02 | getPointsGSI()メソッド追加 |
1.5 | 2022/09/03 | デバッグコード埋め込み |
1.4 | 2021/05/01 | makeMapLeaflet() 引数追加 |
1.3 | 2021/03/07 | tokyo_wgs84(), wgs84_tokyo()メソッド追加 |
1.2 | 2020/12/20 | leafletスクリプトの参照URL変更 |
バージョン | 更新日 | 内容 |
---|---|---|
1.0 | 2021/04/14 |
バージョン | 更新日 | 内容 |
---|---|---|
1.12 | 2021/01/31 | readWebContents() 引数post追加 |
1.11 | 2020/10/17 | htmlspecialchars() 追加 |
1.1 | 2020/10/17 | GetVersion2()追加,readWebContents() 引数ua追加 |
1.01 | 2020/10/03 | setClipboardData() bug-fix |
1.0 | 2020/09/22 |
使用ライブラリ
また、地図表示にWebブラウザ・コントロールを利用するため "cwebpage.dll" を利用する。codeproject からダウンロードできる。
リソースの準備
Eclipse を起動したら、新規プロジェクト earthquakewin を用意する。
ResEdit を起動したら、resource.rc を用意する。
Eclipse に戻り、ソース・プログラム "earthquakewin.cpp" を追加する。
リンカー・フラグを -Wl,--enable-stdcall-fixup -mwindows -lgdiplus -lole32 -static -lstdc++ -lgcc -lwinpthread -lcurl -lssl "C:\(libcurl.dllのフォルダ)\libcurl.dll" "C:\(cwebpage.dllのフォルダ)\cwebpage.dll" に設定する。
また、コマンド行パターンをアレンジし "${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${INPUTS} -luuid -loleaut32 -lole32" とする。
MSYS2 コマンドラインからビルドするのであれば、"makefile" を利用してほしい。
MSYS2 コマンドラインからビルドするのであれば、"makefile" を利用してほしい。
解説:定数など
40: #define MAKER "pahoo.org" //作成者
41: #define APPNAME "earthquakewin" //アプリケーション名
42: #define APPNAMEJP "最近の地震情報" //アプリケーション名(日本語)
43: #define APPVERSION "3.4.5" //バージョン
44: #define APPYEAR "2020-24" //作成年
45: #define REFERENCE "https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-12-01.shtm" // 参考サイト
46:
47: //char*バッファサイズ
48: #define SIZE_BUFF 512
49:
50: //現在のインターフェイス
51: HINSTANCE hInst;
52:
53: //アプリケーション・ウィンドウ
54: HWND hParent;
55:
56: //アプリケーション・ウィンドウ位置
57: unsigned hParent_X, hParent_Y;
58:
59: //エラー・メッセージ格納用【変更不可】
60: string ErrorMessage;
61:
62: //ListViewItemの最大文字長【変更不可】
63: #define MAX_LISTVIEWITEM 259
64:
65: //ヘルプ・ファイル
66: #define HELPFILE ".\\etc\\help.chm"
67:
68: //デフォルト保存ファイル名
69: #define SAVEFILE "eq_%04d%02d%02d_%02d%02d.csv"
96: //マップID
97: #define MAP_ID "map_id"
98: //地図の大きさ
99: #define MAP_WIDTH 600 //地図の幅(ピクセル)
100: #define MAP_HEIGHT 400 //地図の高さ(ピクセル)
101: //経度・緯度(初期値)
102: #define DEF_LONGITUDE 139.766667
103: double Longitude = DEF_LONGITUDE;
104: #define DEF_LATITUDE 35.681111
105: double Latitude = DEF_LATITUDE;
106: //地図拡大率(初期値)
107: #define DEF_ZOOM 6
108: int Zoom = DEF_ZOOM;
109: //地図の種類(初期値)
110: #define DEF_MAPTYPE "GSISTD"
111: string Maptype = DEF_MAPTYPE;
112: #define INFO_WIDTH (int)(MAP_WIDTH * 0.75) //情報ウィンドウの最大幅
113: #define INFO_OFFSET_X 0 //情報ウィンドウのオフセット位置(X)
114: #define INFO_OFFSET_Y -10 //情報ウィンドウのオフセット位置(Y)
115:
116: //地震情報の最大格納数
117: #define MAX_EARTHQUAKE 100
解説:データ構造
122: //地震情報を格納する構造体
123: struct _Earthquake {
124: int id = 0; //マッピングID
125: int year = 0; //西暦年
126: int month = 0; //月
127: int day = 0; //日
128: int hour = 0; //時
129: int minuite = 0; //分
130: wstring location = L""; //震源地
131: double latitude = 0.0; //緯度
132: double longitude = 0.0; //経度
133: double depth = 0.0; //深さ
134: double magnitude = -1.0; //マグニチュード
135: int maxintensity = -1; //最大震度
136: } Earthquake[MAX_EARTHQUAKE];
解説:キャッシュ・システム
- Atomフィード:高頻度フィード:地震火山
- Atomフィード:長期フィード:地震火山
- VXSE53
71: //キャッシュ・ディレクトリ
72: #define DIR_CACHE_FEED "pcache1\\"
73: #define DIR_CACHE_FEED_L "pcache2\\"
74: #define DIR_CACHE_DATA "pcache3\\"
75:
76: //キャッシュ保持時間(デフォルト;分)(0:キャッシュしない)
77: #define LIFE_CACHE_FEED 5 //高頻度フィードに対して
78: #define LIFE_CACHE_FEED_L 120 //長期フィードに対して
79: #define LIFE_CACHE_DATA 720 //地震情報に対して
配布ファイルは、新しい地震情報が入ってくることを考え、フィードの方を短く、地震情報の方は長くキャッシュ保持時間を設定してある。
解説:ワイド文字列中の改行を他の文字列に置換
315: /**
316: * ワイド文字列中の改行を他の文字列に置換する
317: * @param wstring str 置換対象の文字列
318: * @param wstring rep 置換文字列
319: * @return wstring 置換後の文字列
320: */
321: wstring wrepNL(wstring str, wstring rep) {
322: wstring strRet;
323: wstring::iterator ite = str.begin();
324: wstring::iterator iteEnd = str.end();
325:
326: if (0 < str.size()) {
327: wchar_t bNextChar = *ite++;
328: while (1) {
329: if (L'\r' == bNextChar) {
330: // 改行確定
331: strRet += rep;
332: // EOF判定
333: if (ite == iteEnd) {
334: break;
335: }
336: // 1文字取得
337: bNextChar = *ite++;
338: if (L'\n' == bNextChar) {
339: // EOF判定
340: if (ite == iteEnd) {
341: break;
342: }
343: // 1文字取得
344: bNextChar = *ite++;
345: }
346: } else if (L'\n' == bNextChar) {
347: // 改行確定
348: strRet += rep;
349: // EOF判定
350: if (ite == iteEnd) {
351: break;
352: }
353: // 1文字取得
354: bNextChar = *ite++;
355: if (L'\r' == bNextChar) {
356: // EOF判定
357: if (ite == iteEnd) {
358: break;
359: }
360: // 1文字取得
361: bNextChar = *ite++;
362: }
363: } else {
364: // 改行以外
365: strRet += bNextChar;
366: // EOF判定
367: if (ite == iteEnd) {
368: break;
369: }
370: // 1文字取得
371: bNextChar = *ite++;
372: }
373: };
374: }
375: return strRet;
376: }
解説:地震情報取得
489: /**
490: * 地震情報取得(気象庁防災情報XMLから)
491: * @param なし
492: * @return bool TRUE:取得成功/FALSE:失敗
493: */
494: bool getEarthquake(void) {
495: //年月日時分
496: regex re1("([0-9]+)\\-([0-9]+)\\-([0-9]+)T([0-9]+)\\:([0-9]+)");
497: //緯度・経度・深さ
498: regex re2("([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\-\\+\\/])([0-9]*)");
499: smatch mt1, mt2;
500: double lng, lat;
501: pahooCache* pCC; //pahooCacheオブジェクト
502:
503: //地震情報URLを取得
504: static string urls[MAX_EARTHQUAKE];
505: bool res = jma_getLastEarthquakeURLs(urls);
506: if (res == FALSE) {
507: return FALSE;
508: }
509:
510: static string contents = "";
511: pCC = new pahooCache(LIFE_CACHE_DATA, getMyPath(APPNAME) + DIR_CACHE_DATA, UserAgent);
512: for (int i = 0; i < MAX_EARTHQUAKE; i++) {
513: if (urls[i] == "") {
514: // cout << "count = " << i << endl;
515: break;
516: }
517: // readWebContents(urls[i], UserAgent, &contents);
518: if (pCC->load(urls[i], &contents) == FALSE) {
519: ErrorMessage = _WS(pCC->getError());
520: return FALSE;
521: }
522: //XML読み込み
523: try {
524: std::stringstream ss;
525: ss << contents;
526: ptree pt;
527: xml_parser::read_xml(ss, pt);
528:
529: //XML解釈
530: //地震発生日時の取得
531: if (optional<string>str = pt.get_optional<string>("Report.Body.Earthquake.OriginTime")) {
532: // cout << str.value() << " ";
533: if (regex_search(str.value(), mt1, re1)) {
534: Earthquake[i].year = stoi(mt1[1].str());
535: Earthquake[i].month = stoi(mt1[2].str());
536: Earthquake[i].day = stoi(mt1[3].str());
537: Earthquake[i].hour = stoi(mt1[4].str());
538: Earthquake[i].minuite = stoi(mt1[5].str());
539: }
540: }
541: //震源地の取得
542: if (optional<string>str = pt.get_optional<string>("Report.Body.Earthquake.Hypocenter.Area.Name")) {
543: Earthquake[i].location = _UW(str.value());
544: // cout << _WS(Earthquake[i].location) << " ";
545: }
546: if (optional<string>str = pt.get_optional<string>("Report.Body.Earthquake.Hypocenter.Area.jmx_eb:Coordinate")) {
547: if (regex_search(str.value(), mt2, re2)) {
548: pGC->tokyo_wgs84(stod(mt2[2].str()), stod(mt2[1].str()), &lng, &lat);
549: Earthquake[i].latitude = lat;
550: Earthquake[i].longitude = lng;
551: if (mt2[3].str() == "/") {
552: Earthquake[i].depth = (-1.0);
553: } else {
554: Earthquake[i].depth = stod(mt2[4].str());
555: }
556: // cout << "lat=" << lat << " lng=" << lng << " ";
557: }
558: }
559: //マグニチュードの取得
560: if (optional<string>str = pt.get_optional<string>("Report.Body.Earthquake.jmx_eb:Magnitude")) {
561: Earthquake[i].magnitude = stod(str.value());
562: // cout << "M=" << Earthquake[i].magnitude << " ";
563: }
564: //最大震度の取得
565: if (optional<string>str = pt.get_optional<string>("Report.Body.Intensity.Observation.MaxInt")) {
566: Earthquake[i].maxintensity = stoi(str.value());
567: // cout << "max=" << Earthquake[i].maxintensity << endl;
568: }
569: //読み込みエラー
570: } catch(xml_parser_error& e) {
571: ErrorMessage = "気象庁防災情報XMLにアクセスできません";
572: return FALSE;
573: }
574: contents.clear();
575: }
576: delete pCC;
577:
578: return TRUE;
579: }
XMLファイルの構造については、「PHPで直近の地震情報を表示する」をご覧いただきたい。
今回も、ワイド文字列に対する正規表現を使うことにした。ソースはSJISで書いているので、ユーザーマクロ関数 _SW を使ってワイド文字列に変換し、これを使って正規表現によるパターンマッチングを行う。
解説:マップを生成する
809: this->Ppoints[cnt].longitude = -d2;
810: } else if ((s2 == "N") || (s2 == "n")) {
811: this->Ppoints[cnt].latitude = d2;
812: } else if ((s2 == "S") || (s2 == "s")) {
813: this->Ppoints[cnt].latitude = -d2;
814: }
815: cnt++;
816: } else if (regex_search(query, mt1, re12)) {
817: cnt = 0;
818: string s1 = _WS(mt1[1].str());
819: string s2 = _WS(mt1[3].str());
820: double d1 = stod(mt1[2].str());
821: double d2 = stod(mt1[4].str());
822: if ((s1 == "E") || (s1 == "e")) {
823: this->Ppoints[cnt].longitude = d1;
824: } else if ((s1 == "S") || (s1 == "s")) {
825: this->Ppoints[cnt].longitude = -d1;
826: } else if ((s1 == "N") || (s1 == "n")) {
827: this->Ppoints[cnt].latitude = d1;
828: } else if ((s1 == "S") || (s1 == "s")) {
829: this->Ppoints[cnt].latitude = -d1;
830: }
831: if ((s2 == "E") || (s2 == "e")) {
832: this->Ppoints[cnt].longitude = d2;
833: } else if ((s2 == "S") || (s2 == "s")) {
834: this->Ppoints[cnt].longitude = -d2;
835: } else if ((s2 == "N") || (s2 == "n")) {
836: this->Ppoints[cnt].latitude = d2;
837: } else if ((s2 == "S") || (s2 == "s")) {
838: this->Ppoints[cnt].latitude = -d2;
839: }
840: cnt++;
841: } else if (regex_search(query, mt1, re13)) {
842: cnt = 0;
843: string s1 = _WS(mt1[1].str());
844: string s2 = _WS(mt1[3].str());
845: double d1 = stod(mt1[2].str());
846: double d2 = stod(mt1[4].str());
847: if ((s1 == "") || (s1 == "+")) {
848: this->Ppoints[cnt].latitude = d1;
849: } else if (s1 == "-") {
850: this->Ppoints[cnt].latitude = -d1;
851: }
852: if ((s2 == "") || (s2 == "+")) {
853: this->Ppoints[cnt].longitude = d2;
854: } else if (s2 == "-") {
855: this->Ppoints[cnt].longitude = -d2;
856: }
857: cnt++;
858:
859: //APIを自動選定
860: } else if (api == 0) {
861: for (int api : apilist) {
862: this->resetError();
863: cnt = this->__searchPoints(query, ua, api);
864: if (cnt > 0) break;
865: }
866: //API指定
867: } else {
868: cnt = this->__searchPoints(query, ua, api);
869: }
870:
871: return cnt;
872: }
873:
874: // 地図描画 =================================================================
875: /**
876: * 地図描画スクリプトを生成する
877: * @param string id マップID
878: * @param double longitude 中心座標:経度(世界測地系)
879: * @param double latitude 中心座標:緯度(世界測地系)
880: * @param int zoom 拡大率
881: * @param string type マップタイプ
882: * GSISTD:地理院地図(標準):省略時
883: * GSIPALE:地理院地図(淡色地図)
884: * GSIBLANK:地理院地図(白地図)
885: * GSIPHOTO:地理院地図(写真)
886: * OSM:OpenStreetMap
887: * GMRD:Googleマップ(ROADMAP);APIキー有効時
888: * @param ppoints_t* items 地点情報配列(省略可能)
889: * @param size_t size 地点情報配列の数(省略可能)
890: * @param string call1 イベント発生時にコールする関数(省略可)
891: * @param string call2 追加スクリプト(省略可)
892: * @param int max_width 情報ウィンドウの最大幅(省略時:200)
893: * @param int ofst_x 情報ウィンドウのオフセット位置(X)(省略時:0)
894: * @param int ofst_y 情報ウィンドウのオフセット位置(Y)(省略時:0)
895: * @return string 生成したスクリプト
896: */
897: string pahooGeocode::makeMapLeaflet(
898: string id, double longitude, double latitude, int zoom,
899: string type, ppoints_t* items, size_t size, string call1, string call2,
900: int max_width, int ofst_x, int ofst_y) {
901:
902: string ss1 = __GMAP_TILE1;
903: string ss2 = __GMAP_TILE2;
904:
905: //地点情報スクリプトの生成
906: char lat[SIZE_BUFF + 1], lng[SIZE_BUFF + 1];
907: char buff[SIZE_BUFF + 1];
908: string icode = "";
909: if (items != NULL) {
910: string icon = "";
911: string info = "";
912: for (size_t i = 0; i < size; i++) {
913: if ((items[i].icon) == "" && (i > 25)) break;
914: //アイコンURLなく 'Z'を超えたら打ち止め
915: string mark = {(char)(65 + i)};
916: if (items[i].icon == "") {
917: icon = "https://www.google.com/mapfiles/marker" + mark + ".png";
918: } else {
919: icon = items[i].icon;
920: }
921: info = "";
922: if (items[i].description != L"") {
923: snprintf(buff, SIZE_BUFF, "', {maxWidth: %d, offset: [%d, %d] });", max_width, ofst_x, ofst_y);
924: info = "marker_" + mark + ".bindPopup('" + _WS(items[i].description) + buff;
925: }
926: snprintf(lat, SIZE_BUFF, "%.5f", items[i].latitude);
927: snprintf(lng, SIZE_BUFF, "%.5f", items[i].longitude);
928: icode += (boost::format(R"(
929: var icon_%1% = new L.icon({
930: iconUrl: '%2%',
931: iconAnchor: [10, 10] //暫定
932: });
933: var marker_%1% = new L.Marker([%3%, %4%], {icon: icon_%1%}).addTo(map);
934: %5%
935: )")
936: %mark //マーカー識別子
937: % icon //マーカーURL
938: % lat //緯度
939: % lng //経度
940: % info //情報
941: ).str();
942: }
943: }
944:
945: //地図描画スクリプトの生成
946: string script = (boost::format(R"(
947: %6%
948: <link rel="stylesheet" href="https://unpkg.com/leaflet@latest/dist/leaflet.css" />
949: <script src="https://unpkg.com/leaflet@latest/dist/leaflet.js"></script>
950: %7%
951: <script>
952: window.onload = function() {
953: var map = L.map('%1%',{zoomControl:false});
954: map.setView([%2%, %3%], %4%);
955: L.control.scale({
956: maxWidth: 250,
957: position: 'bottomright',
958: imperial: false
959: }).addTo(map);
960: L.control.zoom({position:'topleft'}).addTo(map);
961:
962: //地理院地図:標準地図
963: var GSISTD = new L.tileLayer(
964: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
965: {
966: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
967: minZoom: 0,
968: maxZoom: 18,
969: name: 'GSISTD'
970: });
971: //地理院地図:淡色地図
972: var GSIPALE = new L.tileLayer(
973: 'https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png',
974: {
975: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
976: minZoom: 2,
977: maxZoom: 18,
978: name: 'GSIPALE'
979: });
980: //地理院地図:白地図
981: var GSIBLANK = new L.tileLayer(
982: 'https://cyberjapandata.gsi.go.jp/xyz/blank/{z}/{x}/{y}.png',
983: {
984: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
985: minZoom: 5,
986: maxZoom: 14,
987: name: 'GSIBLANK'
988: });
989: //地理院地図:写真
990: var GSIPHOTO = new L.tileLayer(
991: 'https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg',
992: {
993: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
994: minZoom: 2,
995: maxZoom: 18,
996: name: 'GSIPHOTO'
997: });
998: //OpenStreetMap
999: var OSM = new L.tileLayer(
1000: 'https://tile.openstreetmap.jp/{z}/{x}/{y}.png',
1001: {
1002: attribution: "<a href='https://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors",
1003: minZoom: 0,
1004: maxZoom: 18,
1005: name: 'OSM'
1006: });
1007: %12%
1008: %8%
1009:
1010: //baseMapsオブジェクトにタイル設定
このメソッドは、「地理院地図・OSM描画 -PHPで住所・ランドマークから最寄り駅を求める」で紹介した手法をそのまま移植した。無償のJavaScriptライブラリLeafletを利用している。
後述するように、Googleマップが利用できるときには、必要なスクリプトを変数 GoogleMap1~GoogleMap4 から追加するようにした。
解説:Googleマップを利用する
そこで本プログラムでは、ユーザーが APIキーを取得した場合、それをプログラムから入力・保存できるようにするダイアログを用意した。
102: /**
103: * AppDataのパスを取得
104: * @param char* appname アプリケーション名
105: * @return TCHAR* パス
106: */
107: TCHAR* pahooGeocode::getMyPath(const char* appname) {
108: static TCHAR myPath[MAX_PATH] = "";
109:
110: if (strlen(myPath) == 0) {
111: if (SHGetSpecialFolderPath(NULL, myPath, CSIDL_APPDATA, 0)) {
112: TCHAR *ptmp = _tcsrchr(myPath, _T('\\'));
113: if (ptmp != NULL) {
114: ptmp = _tcsinc(ptmp);
115: *ptmp = _T('\0');
116: }
117: strcat(myPath, _T("Roaming"));
118: CreateDirectory((LPCTSTR)myPath, NULL);
119: strcat(myPath, _T("\\pahoo.org"));
120: CreateDirectory((LPCTSTR)myPath, NULL);
121: strcat(myPath, _T("\\"));
122: strcat(myPath, _T(appname));
123: CreateDirectory((LPCTSTR)myPath, NULL);
124: strcat(myPath, _T("\\"));
125: } else {
126: }
127: }
128: return myPath;
129: }
なお、WiX を使って作ったアンインストーラーを実行すると、このフォルダを消去するようにしてある。
188: /**
189: * Google Cloud Platform APIキーを書き込む
190: * @param string key 書き込むAPIキー
191: * @return bool TRUE:書込成功/FALSE:失敗
192: */
193: bool pahooGeocode::writeGoogleApiKey(std::string key) {
194: bool ret = TRUE;
195: ofstream ofs;
196:
197: ofs.open((string)this->getMyPath(NULL) + FNAME_GOOGLE_API);
198: ofs << key;
199: if(ofs.bad()) {
200: this->errmsg = _SW("Google Cloud Platform APIキーの保存に失敗しました");
201: ret = FALSE;
202: }
203: ofs.close();
204: this->readGoogleApiKey();
205:
206: return ret;
207: }
132: /**
133: * Google Cloud Platform APIキーを読み込む
134: * @param なし
135: * @return bool TRUE:読込成功/FALSE:ファイルがない
136: */
137: bool pahooGeocode::readGoogleApiKey(void) {
138: string key;
139: bool ret = FALSE;
140:
141: ifstream ifs((string)this->getMyPath(NULL) + this->FNAME_GOOGLE_API);
142: //APIキー・ファイルが無ければ初期化
143: if (!ifs) {
144: this->GoogleAPIkey = "";
145: this->GoogleMap1 = "";
146: this->GoogleMap2 = "";
147: this->GoogleMap3 = "";
148: this->GoogleMap4 = "";
149: return ret;
150: }
151:
152: //APIキー読み込み
153: ifs >> key;
154: if(ifs.bad()) {
155: this->errmsg = _SW("Google Cloud Platform APIキーの読み込みに失敗しました");
156: ifs.close();
157: this->GoogleAPIkey = "";
158: this->GoogleMap1 = "";
159: this->GoogleMap2 = "";
160: this->GoogleMap3 = "";
161: this->GoogleMap4 = "";
162: return FALSE;
163: }
164: ifs.close();
165:
166: //APIキーなどの設定
167: if (key.length() > 0) {
168: this->GoogleAPIkey = key;
169: this->GoogleMap1 = "<script src='https://maps.googleapis.com/maps/api/js?key="
170: + key + "' async defer></script>";
171: this->GoogleMap2 =
172: "<script src='https://unpkg.com/leaflet.gridlayer.googlemutant@0.10.2/Leaflet.GoogleMutant.js'></script>";
173: this->GoogleMap3 =
174: "var GMRD = L.gridLayer.googleMutant({type:'roadmap', name:'GMRD'});\nvar GMST = L.gridLayer.googleMutant({type:'satellite', name:'GMST'});\nvar GMHB = L.gridLayer.googleMutant({type:'hybrid', name:'GMHB'});";
175: this->GoogleMap4 = ",'Googleマップ(標準)' : GMRD,\n'Googleマップ(写真)' : GMST,\n'Googleマップ(混合)' : GMHB";
176: ret = TRUE;
177: //APIキーが無ければ初期化
178: } else {
179: this->GoogleAPIkey = "";
180: this->GoogleMap1 = "";
181: this->GoogleMap2 = "";
182: this->GoogleMap3 = "";
183: this->GoogleMap4 = "";
184: }
185: return ret;
186: }
有効なキーがあれば、変数 GoogleAPIkey に代入する。また、前述の makeMapLeaflet メソッドにGoogleマップを追加するためのスクリプトを変数 GoogleMap1~GoogleMap4 に代入する。
解説:マップ表示用HTML生成
717: /**
718: * 地図表示用HTML生成
719: * @param string 表示するインフォメーション
720: * @return string 生成したHTML文
721: */
722: string makeMapHTML(wstring info) {
723: // if (pGC->GoogleAPIkey.length() == 0) {
724: // Maptype = DEF_MAPTYPE;
725: // }
726:
727: int cnt = info2points();
728: string script = pGC->makeMapLeaflet(MAP_ID, Earthquake[0].longitude, Earthquake[0].latitude, Zoom, Maptype, pGC->Ppoints, cnt, "", "", INFO_WIDTH, INFO_OFFSET_X, INFO_OFFSET_Y);
729:
730: string html = (boost::format(R"(<!DOCTYPE html>
731: <html lang="ja">
732: <head>
733: <meta charset="SJIS">
734: <title>%9%</title>
735: <meta name="author" content="studio pahoo" />
736: <meta name="copyright" content="studio pahoo" />
737: <meta name="ROBOTS" content="NOINDEX,NOFOLLOW" />
738: <meta http-equiv="pragma" content="no-cache">
739: <meta http-equiv="cache-control" content="no-cache">
740: <meta http-equiv="X-UA-Compatible" content="IE=edge">
741: %8%
742: </head>
743: <body>
744: <div id="%1%" style="width:%2%px; height:%3%px;"></div>
745: <form>
746: <input id="latitude" type="hidden" value="%4%" />
747: <input id="longitude" type="hidden" value="%5%" />
748: <input id="zoom" type="hidden" value="%6%" />
749: <input id="maptype" type="hidden" value="%7%" />
750: </form>
751: </body>
752: </html>
753: )")
754: %MAP_ID //地図ID
755: % MAP_WIDTH //地図の幅
756: % MAP_HEIGHT //地図の高さ
757: % Earthquake[0].latitude //緯度
758: % Earthquake[0].longitude //経度
759: % Zoom //地図拡大率
760: % Maptype //地図タイプ
761: % script //地図描画スクリプト
762: % APPNAMEJP //アプリケーション名
763: ).str();
764:
765: return html;
766: }
解説:ブラウザ・コントロール表示
796: /**
797: * ブラウザ・コントロールを表示
798: * @param wstring info 情報ウィンドウに表示するテキスト
799: * @param char* tmpname 読み込むHTMLファイル名
800: * @return なし
801: */
802: void viewBrowser(wstring info, const char* tmpname) {
803: string html;
804: string fname;
805: ofstream ofs;
806:
807: //表示用HTMLファイル作成
808: if ((ErrorMessage == "") && (! pGC->isError())) {
809: html = makeMapHTML(info);
810: } else {
811: html = makeErrorHTML();
812: }
813: ofs.open(tmpname);
814: ofs << html;
815: ofs.close();
816:
817: fname = (string)tmpname;
818: std::replace(fname.begin(), fname.end(), '\\', '/');
819: wBrowser.loadPage((char*)fname.c_str());
820: wBrowser.fitToParent();
821: }
テンポラリディレクトリにHTML文を保存し、これを表示するようにしている。
1118: //ブラウザ・コントロール作成
1119: hHTML = CreateWindowEx(0, WC_STATIC, "WebBrowser", WS_CHILD | WS_VISIBLE | ES_LEFT, 10, 10, MAP_WIDTH + 20, MAP_HEIGHT + 20, hDlg, NULL, hInst, NULL);
1120: wBrowser.create((char*)"", 0, 0, MAP_WIDTH + 20, MAP_HEIGHT + 20, hHTML);
WebBrowserコントロール・クラス WebBrowse は、Digital Point の "webbrowser.h" を参考にアレンジし、"webbrowser.hpp" とした。
解説:地図描画パラメータ
139: /**
140: * パラメータの初期化
141: * @param なし
142: * @return なし
143: */
144: void initParameter(void) {
145: Longitude = DEF_LONGITUDE;
146: Latitude = DEF_LATITUDE;
147: Zoom = DEF_ZOOM;
148: Maptype = DEF_MAPTYPE;
149: hParent_X = 0;
150: hParent_Y = 0;
151: }
216: /**
217: * パラメータの保存
218: * @param なし
219: * @return なし
220: */
221: void saveParameter(void) {
222: #ifndef CMDAPP
223: //アプリケーション・ウィンドウの位置取得
224: WINDOWINFO windowInfo;
225: windowInfo.cbSize = sizeof(WINDOWINFO);
226: GetWindowInfo(hParent, &windowInfo);
227: hParent_X = (unsigned)windowInfo.rcWindow.left;
228: hParent_Y = (unsigned)windowInfo.rcWindow.top;
229: if (hParent_X >= (unsigned)windowInfo.rcWindow.right) {
230: hParent_X = 0;
231: }
232: if (hParent_Y >= (unsigned)windowInfo.rcWindow.bottom) {
233: hParent_Y = 0;
234: }
235: #endif
236:
237: //ブラウザ・コントロールからパラメータを取り出す
238: wstring val;
239: if (wBrowser.getInputById(L"longitude", &val)) {
240: Longitude = stod(val);
241: }
242: if (wBrowser.getInputById(L"latitude", &val)) {
243: Latitude = stod(val);
244: }
245: if (wBrowser.getInputById(L"zoom", &val)) {
246: Zoom = stoi(val);
247: }
248: if (wBrowser.getInputById(L"maptype", &val)) {
249: Maptype = _WS(val);
250: }
251:
252: char lng[SIZE_BUFF + 1], lat[SIZE_BUFF + 1];
253: snprintf(lng, SIZE_BUFF, "%.5f", Longitude);
254: snprintf(lat, SIZE_BUFF, "%.5f", Latitude);
255:
256: //XMLファイルへ書き込む
257: ptree pt;
258: ptree& child1 = pt.add("parameter.param", lat);
259: child1.add("<xmlattr>.type", "latitude");
260: ptree& child2 = pt.add("parameter.param", lng);
261: child2.add("<xmlattr>.type", "longitude");
262: ptree& child3 = pt.add("parameter.param", to_string(Zoom));
263: child3.add("<xmlattr>.type", "zoom");
264: ptree& child4 = pt.add("parameter.param", Maptype);
265: child4.add("<xmlattr>.type", "maptype");
266: ptree& child5 = pt.add("parameter.param", (string)to_string(hParent_X));
267: child5.add("<xmlattr>.type", "wx");
268: ptree& child6 = pt.add("parameter.param", (string)to_string(hParent_Y));
269: child6.add("<xmlattr>.type", "wy");
270:
271: const int indent = 4;
272: write_xml(getMyPath(APPNAME) + APPNAME + ".xml", pt, std::locale(),
273: xml_writer_make_settings<std::string>(' ', indent));
274: }
ブラウザ・コントロールから、これらの値を取り出し、所定のXMLファイルへ保存するのが saveParameter である。
保存場所は、"[C:\Users\(ユーザー名)\AppData\Roaming\pahoo.org\(アプリケーション名)" である。
なお、INPUT要素を取得する方法は、「INPUT要素の取得 - C++で最寄駅を検索」で紹介したとおりだ。
153: /**
154: * パラメータの読み込み
155: * @param なし
156: * @return なし
157: */
158: void loadParameter(void) {
159: ptree pt;
160:
161: //初期値設定
162: initParameter();
163:
164: //XMLファイル読み込み
165: try {
166: xml_parser::read_xml(getMyPath(APPNAME) + APPNAME + ".xml", pt);
167:
168: //XML解釈
169: try {
170: //形式チェック
171: if (optional<string>str = pt.get_optional<string>("parameter")) {
172: } else {
173: return;
174: }
175: //パラメータ読み込み
176: for (auto it : pt.get_child("parameter")) {
177: string type= it.second.get_optional<string>("<xmlattr>.type").value();
178: if (type == "latitude") {
179: Latitude = stod(it.second.data());
180: } else if (type == "longitude") {
181: Longitude = stod(it.second.data());
182: } else if (type == "zoom") {
183: Zoom = stoi(it.second.data());
184: } else if (type == "maptype") {
185: Maptype = (string)it.second.data();
186: } else if (type == "wx") {
187: hParent_X = (unsigned)stoi(it.second.data());
188: } else if (type == "wy") {
189: hParent_Y = (unsigned)stoi(it.second.data());
190: }
191: }
192: //解釈失敗したら初期値設定
193: } catch (xml_parser_error& e) {
194: initParameter();
195: return;
196: }
197: //読み込み失敗したら初期値設定
198: } catch (xml_parser_error& e) {
199: initParameter();
200: return;
201: }
202:
203: //アプリケーション・ウィンドウの位置(デスクトップ範囲外なら原点移動)
204: HWND hDesktop = GetDesktopWindow();
205: WINDOWINFO windowInfo;
206: windowInfo.cbSize = sizeof(WINDOWINFO);
207: GetWindowInfo(hDesktop, &windowInfo);
208: if (hParent_X >= (unsigned)windowInfo.rcWindow.right) {
209: hParent_X = 0;
210: }
211: if (hParent_Y >= (unsigned)windowInfo.rcWindow.bottom) {
212: hParent_Y = 0;
213: }
214: }
参考サイト
- PHPで最近の地震情報を表示する:ぱふぅ家のホームページ
- WiX によるWindowsインストーラー作成:ぱふぅ家のホームページ
- C++ 開発環境の準備:ぱふぅ家のホームページ
- C++ 開発環境の準備 -MSYS2編-
(2024年3月16日)使用ライブラリ更新
(2023年11月11日)使用ライブラリ更新
(2023年7月2日)使用ライブラリ更新