C++ で直近の地震情報を取得する

(1/1)
>C++で直近の地震情報を取得する
インターネット経由で気象庁地震情報サイトにアクセスし、直近の地震情報(発生日時、震源の位置・深さ、地震の規模)を地図上にマッピングしたり、その情報をファイル保存するアプリケーションを作る。「PHP で直近の地震情報を表示する(Windows アプリ版)」で作った PHP プログラムを C++に移植したものである。

目次

サンプル・プログラム

圧縮ファイルの内容
earthquakewin.msiインストーラ
bin/earthquakewin.exe実行プログラム本体
bin/cwebpage.dll
bin/libcrypto-1_1.dll
bin/libcurl.dll
bin/libssl-1_1.dll
実行時に必要になるDLL
bin/etc/help.chmヘルプ・ファイル
sour/earthquakewin.cppソース・プログラム
sour/resource.hリソース・ヘッダ
sour/resource.rcリソース・ファイル
sour/application.icoアプリケーション・アイコン

使用ライブラリ

気象庁地震情報サイトにアクセスするために、オープンソースのライブラリ Boost C++ライブラリcURL (カール)  および OpenSSL が必要になる。導入方法等については、「C++ 開発環境の準備」をご覧いただきたい。
また、地図表示に Web ブラウザ・コントロールを利用するため、オープンソースの cwebpage.dll および webbrowser.h が必要になる。cwebpage.dllcodeproject から、webbrowser.hDigital Point から、それぞれダウンロードできる。

リソースの準備

cwebpage.dll が 32 ビット対応であるため、今回は 32 ビット版の開発環境を用いる。
Eclipse を起動し、新規プロジェクト earthquakewin を用意する。
ResEdit を起動し、resource.rc を用意する。

Eclipse に戻り、ソース・プログラム "earthquakewin.cpp" を追加する。
リンカー・フラグを -mwindows -static -lstdc++ -lgcc -lwinpthread -lcurl -lssl -llzma -lz -lws2_32 "C:\pleiades\eclipse\mingw32\bin\libcurl.dll" "C:\pleiades\eclipse\mingw32\bin\cwebpage.dll" に設定する。

解説:ヘッダファイル等

0010: // 初期化処理 ======================================================
0011: #include <iostream>
0012: #include <fstream>
0013: #include <stdio.h>
0014: #include <stdlib.h>
0015: #include <tchar.h>
0016: #include <time.h>
0017: #include <sstream>
0018: #include <string>
0019: #include <iterator>
0020: #include <list>
0021: #include <regex>
0022: #include <locale>
0023: #include <winsock2.h>
0024: #include <windows.h>
0025: #include <shlobj.h>
0026: #include <commctrl.h>
0027: #include <richedit.h>
0028: #include <curl/curl.h>
0029: #include <boost/format.hpp>
0030: #include "webbrowser.h"
0031: #include "resource.h"
0032: 
0033: using namespace std;
0034: using namespace boost;
0035: 
0036: #define APPNAME     "直近の地震情報"        //アプリケーション名
0037: #define APPVERSION "1.0"                    //バージョン
0038: #define APPYEAR     "2020"                    //作成年
0039: #define REFERENCE "https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-12-01.shtm"  // 参考サイト
0040: 
0041: //char*バッファサイズ
0042: #define SIZE_BUFF 512
0043: 
0044: //現在のインターフェイス
0045: static HINSTANCE hInst;
0046: 
0047: //親ウィンドウ
0048: static HWND hParent;
0049: 
0050: //AppDataのフルパス
0051: TCHAR MyPath[MAX_PATH];
0052: #define MYAPPDATA "earthquakewin"
0053: 
0054: //エラー・メッセージ格納用
0055: string ErrorMessage;
0056: 
0057: //ヘルプ・ファイル
0058: #define HELPFILE ".\\etc\\help.chm"
0059: 
0060: //デフォルト保存ファイル名
0061: #define SAVEFILE "eq_%04d%02d%02d_%02d%02d.txt"

とくに注意記載が無い限り、定数は自由に変更できる。

解説:データ構造

0081: //地震情報
0082: wstring infoEarthquake;
0083: 
0084: //地震情報URL(気象庁):変更不可
0085: #define URL_EARTHQUAKE "https://www.jma.go.jp/jp/quake/"
0086: 
0087: //地震情報を格納する構造体
0088: struct _Earthquake {
0089:     int year            = 0;         //西暦年
0090:     int month           = 0;         //月
0091:     int day             = 0;         //日
0092:     int hour            = 0;         //時
0093:     int minuite         = 0;         //分
0094:     double latitude     = 0.0;            //緯度
0095:     double longitude    = 0.0;            //経度
0096:     double depth        = 0.0;            //深さ
0097:     double magnitude    = 0.0;            //マグニチュード
0098: Earthquake;

取得した地震情報は構造体 Earthquake に格納する。

解説:ワイド文字列中の改行を他の文字列に置換

0325: /**
0326:  * ワイド文字列中の改行を他の文字列に置換する
0327:  * @param  wstring str 置換対象の文字列
0328:  * @param  wstring rep 置換文字列
0329:  * @return wstring 置換後の文字列
0330: */
0331: wstring wrepNL(wstring strwstring rep) {
0332:     wstring strRet;
0333:     wstring::iterator ite    = str.begin();
0334:     wstring::iterator iteEnd = str.end();
0335: 
0336:     if (0 < str.size()) {
0337:         wchar_t bNextChar = *ite++;
0338:         while (1) {
0339:         if (L'\r' == bNextChar) {
0340:             // 改行確定
0341:             strRet += rep;
0342:             // EOF判定
0343:             if (ite == iteEnd) {
0344:                 break;
0345:             }
0346:             // 1文字取得
0347:             bNextChar = *ite++;
0348:             if (L'\n' == bNextChar) {
0349:                 // EOF判定
0350:                 if (ite == iteEnd) {
0351:                     break;
0352:                 }
0353:                 // 1文字取得
0354:                 bNextChar = *ite++;
0355:             }
0356:         } else if (L'\n' == bNextChar) {
0357:                 // 改行確定
0358:                 strRet += rep;
0359:                 // EOF判定
0360:                 if (ite == iteEnd) {
0361:                     break;
0362:                 }
0363:                 // 1文字取得
0364:                 bNextChar = *ite++;
0365:                 if (L'\r' == bNextChar) {
0366:                     // EOF判定
0367:                     if (ite == iteEnd) {
0368:                         break;
0369:                     }
0370:                     // 1文字取得
0371:                     bNextChar = *ite++;
0372:                 }
0373:             } else {
0374:                 // 改行以外
0375:                 strRet += bNextChar;
0376:                 // EOF判定
0377:                 if (ite == iteEnd) {
0378:                     break;
0379:                 }
0380:                 // 1文字取得
0381:                 bNextChar = *ite++;
0382:             }
0383:         };
0384:     }
0385:     return strRet;
0386: }

PHP の組み込み関数  nl2br  に相当する機能をワイド文字列用に拡張したのが wrepNL である。iterator を使ってワイド文字列を総なめにしている。

解説:日本語テキスト変換

0611: /**
0612:  * 日本語テキスト変換(Win32 API利用)
0613:  * @param wstring sour   変換元テキスト
0614:  * @param DWORD   method 変換方式
0615:  * @return wstring 変換後テキスト
0616: */
0617: wstring wconvString(wstring wsourDWORD method) {
0618:     //濁点・半濁点は全角→半角で2文字に増えるので変換後領域は余裕を持たせる
0619:     wchar_t wdest[wsour.length() * 2 + 1];
0620:     for (size_t i = 0; i < wsour.length() * 2 + 1; i++) {
0621:         wdest[i] = 0L;
0622:     }
0623:     //変換実行
0624:     LCMapStringW(LOCALE_SYSTEM_DEFAULTmethod, (LPWSTR)wsour.c_str(), wsour.length(), (LPWSTR)wdestwsour.length() * 2);
0625: 
0626:     return (wstring)wdest;
0627: }

PHP の組み込み関数  mb_convert_kana  に相当する機能をワイド文字列用に拡張したのが wconvString である。Win32API(Kernel32.dll)の機能を呼び出している。

解説:数字(wstring)を数値(double)に変換

0629: /**
0630:  * 数字(wstring)を数値(double)に変換
0631:  * @param wstring sour   変換元テキスト
0632:  * @return double  変換後数値
0633: */
0634: double w2f(wstring sour) {
0635:     return atof(_WS(wconvString(sourLCMAP_HALFWIDTH)).c_str());
0636: }

気象庁地震情報サイトにある情報には、全角数字であるものが多く、コンピュータで処理しやすいように半角数字に変換するために、前述の関数 wconvString を利用した関数 w2f を用意した。

解説:地震情報取得

0644: /**
0645:  * 地震情報取得(気象庁から)
0646:  * @param なし
0647:  * @return int (-1) : 取得失敗(ネットワーク接続異常)
0648:  *                0  : 情報無し
0649:  *              (+1) : 取得成功
0650: */
0651: int getEarthquake(void) {
0652:     //cURLによる結果取得
0653:     CURL *curl;
0654:     CURLcode res = (CURLcode)0;
0655:     curl = curl_easy_init();
0656:     string chunk;
0657:     string url = URL_EARTHQUAKE;
0658: 
0659:     //cURLによるコンテンツ取得
0660:     if (curl) {
0661:         curl_easy_setopt(curlCURLOPT_URLurl.c_str());
0662:         curl_easy_setopt(curlCURLOPT_SSL_VERIFYPEER, 0);
0663:         curl_easy_setopt(curlCURLOPT_WRITEFUNCTIONcallBackFunk);
0664:         curl_easy_setopt(curlCURLOPT_WRITEDATA, (string*)&chunk);
0665:         res = curl_easy_perform(curl);
0666:         curl_easy_cleanup(curl);
0667:     }
0668:     if (res != CURLE_OK) {
0669:         ErrorMessage = "気象庁サイトへの接続エラー";
0670:         return (-1);
0671:     }
0672: 
0673:     //コンテンツの解釈
0674:     setlocale(LC_ALL, "Japanese");
0675:     int ofst = 0;
0676:     stringstream ss;
0677:     string ss0;
0678:     wstring ws;
0679:     wsmatch mt1mt2;
0680:     wregex re1(_SW("震源・震度に関する情報(<br>)?(.+[0-9\\.0-9.]+と推定されます。)"));
0681:     wregex re2(_SW("(昭和|平成|令和)\\s*([0-90-9]+)年\\s*([0-90-9]+)月\\s*([0-90-9]+)日"));
0682:     wregex re3(_SW("([0-90-9]+)日([0-90-9]+)時([0-90-9]+)分ころ、地震がありました"));
0683:     wregex re4(_SW("北緯([0-9\\.0-9.]+)度、東経([0-9\\.0-9.]+)度"));
0684:     wregex re5(_SW("震源の深さは約([0-9\\.0-9.]+)km"));
0685:     wregex re6(_SW("地震の規模(マグニチュード)は([0-9\\.0-9.]+)と"));
0686: 
0687:     ss << chunk;
0688:     while (ss && getline(ssss0)) {
0689:         //1行をwstring変換
0690:         ws = _UW(ss0);
0691:         if (regex_search(wsmt1re1)) {
0692:             wstring sour = mt1[2].str();
0693:             sour = wstrip_tags(sour);
0694:             if (sour == L"")   return 0;
0695:             //年月
0696:             if (regex_search(sourmt2re2)) {
0697:                 if (mt2[1].str() == _SW("昭和")) {
0698:                     ofst = 1925;
0699:                 } else if (mt2[1].str() == _SW("平成")) {
0700:                     ofst = 1988;
0701:                 } else if (mt2[1].str() == _SW("令和")) {
0702:                     ofst = 2018;
0703:                 }
0704:                 Earthquake.year    = (int)w2f(mt2[2].str()) + ofst;
0705:                 Earthquake.month   = (int)w2f(mt2[3].str());
0706:             } else {
0707:                 return 0;
0708:             }
0709:             //日時分
0710:             if (regex_search(sourmt2re3)) {
0711:                 Earthquake.day     = (int)w2f(mt2[1].str());
0712:                 Earthquake.hour    = (int)w2f(mt2[2].str());
0713:                 Earthquake.minuite = (int)w2f(mt2[3].str());
0714:             } else {
0715:                 return 0;
0716:             }
0717:             //緯度・経度
0718:             if (regex_search(sourmt2re4)) {
0719:                 Earthquake.latitude  = w2f(mt2[1].str());
0720:                 Earthquake.longitude = w2f(mt2[2].str());
0721:             } else {
0722:                 return 0;
0723:             }
0724:             //深さ
0725:             if (regex_search(sourmt2re5)) {
0726:                 Earthquake.depth = w2f(mt2[1].str());
0727:             } else {
0728:                 return 0;
0729:             }
0730:             //マグニチュード
0731:             if (regex_search(sourmt2re6)) {
0732:                 Earthquake.magnitude = w2f(mt2[1].str());
0733:             } else {
0734:                 return 0;
0735:             }
0736:         }
0737:     }
0738:     return 1;
0739: }

気象庁地震情報サイトからコンテンツを取り込むには、これまで同様に、cURL 関数群を使って全コンテンツを変数 chunk に代入する。
次に、この内容をスクレイピングしていくのだが、今回も、ワイド文字列に対する正規表現を使うことにした。ソースは SJIS で書いているので、ユーザーマクロ関数 _SW を使ってワイド文字列に変換し、これを使って正規表現によるパターンマッチングを行う。

地震情報が記述されているのは 1行で、これにマッチするのが re1 である。あとは、この行に対してマッチングをかけていく。
スクレイピング用パターン
パターン名内容
re1地震情報が記述されている行
re2発表年月
re3発生日時分
re4震源の緯度・経度
re5震源の深さ
re6地震の規模

解説:マップを生成する

0768: /**
0769:  * マップを生成する
0770:  * @param string info 情報ウィンドウに表示するテキスト
0771:  * @return string HTML文
0772: */
0773: string makeMapLeaflet(string info) {
0774:     return (boost::format(R"(
0775: <!DOCTYPE html>
0776: <html lang=
"ja">
0777: <head>
0778: <meta charset=
"SJIS">
0779: <title>直近の地震情報</title>
0780: <meta name=
"author" content="studio pahoo" />
0781: <meta name=
"copyright" content="studio pahoo" />
0782: <meta name=
"ROBOTS" content="NOINDEX,NOFOLLOW" />
0783: <meta http-equiv=
"pragma" content="no-cache">
0784: <meta http-equiv=
"cache-control" content="no-cache">
0785: <meta http-equiv=
"X-UA-Compatible" content="IE=edge">
0786: %9%
0787: <link rel=
"stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" />
0788: <script src=
"https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"></script>
0789: %10%
0790: <script>
0791: window.onload = function() {
0792:     var map = L.map('%1%',{zoomControl:false});
0793:     map.setView([%2%, %3%], %4%);
0794:     L.control.scale({
0795:         maxWidth: 250,
0796:         position: 'bottomright',
0797:         imperial: false
0798:     }).addTo(map);
0799:     L.control.zoom({position:'topleft'}).addTo(map);
0800: 
0801:     //地理院地図:標準地図
0802:     var GSISTD = new L.tileLayer(
0803:         'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
0804:         {
0805:             attribution: 
"<a href='https://maps.gsi.go.jp/development/ichiran.htmltarget='_blank'>地理院タイル</a>",
0806:             minZoom: 0,
0807:             maxZoom: 18,
0808:             name: 'GSISTD'
0809:         });
0810:     //地理院地図:淡色地図
0811:     var GSIPALE = new L.tileLayer(
0812:         'https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png',
0813:         {
0814:             attribution: 
"<a href='https://maps.gsi.go.jp/development/ichiran.htmltarget='_blank'>地理院タイル</a>",
0815:             minZoom: 2,
0816:             maxZoom: 18,
0817:             name: 'GSIPALE'
0818:         });
0819:     //地理院地図:白地図
0820:     var GSIBLANK = new L.tileLayer(
0821:         'https://cyberjapandata.gsi.go.jp/xyz/blank/{z}/{x}/{y}.png',
0822:         {
0823:             attribution: 
"<a href='https://maps.gsi.go.jp/development/ichiran.htmltarget='_blank'>地理院タイル</a>",
0824:             minZoom: 5,
0825:             maxZoom: 14,
0826:             name: 'GSIBLANK'
0827:         });
0828:     //地理院地図:写真
0829:     var GSIPHOTO = new L.tileLayer(
0830:         'https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg',
0831:         {
0832:             attribution: 
"<a href='https://maps.gsi.go.jp/development/ichiran.htmltarget='_blank'>地理院タイル</a>",
0833:             minZoom: 2,
0834:             maxZoom: 18,
0835:             name: 'GSIPHOTO'
0836:         });
0837:     //OpenStreetMap
0838:     var OSM = new L.tileLayer(
0839:         'https://tile.openstreetmap.jp/{z}/{x}/{y}.png',
0840:         {
0841:             attribution: 
"<a href='https://osm.org/copyrighttarget='_blank'>OpenStreetMap</acontributors",
0842:             minZoom: 0,
0843:             maxZoom: 18,
0844:             name: 'OSM'
0845:         });
0846: %11%
0847: 
0848:     //baseMapsオブジェクトにタイル設定
0849:     var baseMaps = {
0850:         
"地理院地図" : GSISTD,
0851:         
"淡色地図" : GSIPALE,
0852:         
"白地図" : GSIBLANK,
0853:         
"写真地図" : GSIPHOTO,
0854:         
"オープンストリートマップ" : OSM
0855:         %12%
0856:     };
0857: 
0858:     //layersコントロールにbaseMapsオブジェクトを設定して地図に追加
0859:     L.control.layers(baseMaps).addTo(map);
0860:     %8%.addTo(map);
0861: 
0862:     //情報ウィンドウ
0863:     var icon_A = new L.icon({
0864:         iconUrl: '%5%',
0865:         iconAnchor: [10, 10]
0866:     });
0867:     var marker_A = new L.Marker([%2%, %3%], {icon: icon_A}).addTo(map);
0868:     marker_A.bindPopup('%13%', {maxWidth: 250});
0869: }
0870: </script>
0871: </head>
0872: <body>
0873: <div id=
"%1%" style="width:%6%pxheight:%7%px;"></div>
0874: </body>
0875: </html>
0876: )
")
0877: % "mapid"                   //地図ID
0878: Earthquake.latitude        //緯度
0879: Earthquake.longitude       //経度
0880: ZOOM                      //地図拡大率
0881: URL_MARKER                //マーカーURL
0882: MAP_WIDTH                 //地図の幅
0883: MAP_HEIGHT                //地図の高さ
0884: % "GSISTD"                       //デフォルトの地図タイプ
0885: GoogleMap1
0886: GoogleMap2
0887: GoogleMap3
0888: GoogleMap4
0889: info                      //地震情報
0890: ).str();
0891: }

地図描画には、「地理院地図・ OSM 描画 -PHP で住所・ランドマークから最寄り駅を求める」で紹介した手法をそのまま移植した。無償の JavaScript ライブラリ Leaflet を利用している。
後述するように、Google マップが利用できるときには、必要なスクリプトを変数 GoogleMap1GoogleMap4 から追加するようになっている。

解説:Googleマップを利用する

Google Cloud Platform - 各種WebAPI の登録方法」で紹介したように、Google マップを利用するには、API キーを取得する必要がある。Google Cloud Platform は利用量によって課金される。現在、Google マップ関連サービスは毎月 200 ドルまでは無料だが、それ以上の利用量があると課金対象となり、登録したクレジットカードに請求される。
そこで本プログラムでは、ユーザーが API キーを取得した場合、それをプログラムから入力・保存できるようにするダイアログを用意した。

0510: /**
0511:  * イベントハンドラ:APIキー入力ダイアログ
0512:  * @param HWND hDlg           ウィンドウ・ハンドラ
0513:  * @paramm UINT uMsg           メッセージ識別子
0514:  * @param WPARAM wParam       メッセージの最初のパラメータ
0515:  * @paramL PARAM lParam       メッセージの2番目のパラメータ
0516:  * @return INT_PTR CALLBACK   TRUE:メッセージ処理完了/FALSE:未完了
0517: */
0518: INT_PTR CALLBACK processAPIkey(HWND hDlgUINT uMsgWPARAM wParamLPARAM lParam) {
0519:     ofstream ofs;
0520: 
0521:     switch(uMsg){
0522:         //ダイアログ初期化
0523:         case WM_INITDIALOG:
0524:             CenterWindow(hDlg);
0525:             setStrEditBox(hDlgIDC_TEXT_APIKEY, "Google APIキーを入力・保存します.");
0526:             setStrEditBox(hDlgIDC_EDIT_APIKEYGoogleAPIkey);
0527:             break;
0528: 
0529:         //ボタン押下
0530:         case WM_COMMAND:
0531:              switch (LOWORD(wParam)) {
0532:                 //保存
0533:                 case IDC_BUTTON_APIKEY_SAVE:
0534:                     GoogleAPIkey = getStrEditBox(hDlgIDC_EDIT_APIKEY);
0535:                     ofs.open((string)MyPath + FNAME_GOOGLE_API);
0536:                     ofs << GoogleAPIkey;
0537:                     ofs.close();
0538:                     readGoogleApiKey();
0539:                 case IDC_BUTTON_APIKEY_CANCEL:
0540:                     EndDialog(hDlg, 0);
0541:                     break;
0542:                 default:
0543:                     return 1;
0544:             }
0545:             break;
0546:         //プログラム終了
0547:         case WM_CLOSE:
0548:             EndDialog(hDlg, 0);
0549:             break;
0550:     }
0551:     return 0;
0552: }

0554: /**
0555:  * APIキー入力ダイアログを作成
0556:  * @param  HWND hDlg      親ウィンドウ・ハンドラ
0557:  * @param  DLGPROC handler  イベント・ハンドラ
0558:  * @return なし
0559: */
0560: void createAPIkey(HWND hDlgDLGPROC handler) {
0561:     DialogBox(hInstMAKEINTRESOURCE(IDD_DIALOG_APIKEY), hDlg, (DLGPROC)handler);
0562: }

0101: /**
0102:  * AppDataのパスを取得
0103:  * @param なし
0104:  * @return なし
0105: */
0106: void getMyPath(void) {
0107:     if (SHGetSpecialFolderPathNULLMyPathCSIDL_APPDATA, 0)) {
0108:         TCHARptmp = _tcsrchr(MyPath_T('\\'));
0109:         if (ptmp != NULL) {
0110:             ptmp = _tcsinc(ptmp);
0111:             *ptmp = _T('\0');
0112:         }
0113:         strcat(MyPath_T("Roaming"));
0114:         CreateDirectory((LPCTSTR)MyPathNULL);
0115:         strcat(MyPath_T("\\pahoo.org"));
0116:         CreateDirectory((LPCTSTR)MyPathNULL);
0117:         strcat(MyPath_T("\\"));
0118:         strcat(MyPath_T(MYAPPDATA));
0119:         CreateDirectory((LPCTSTR)MyPathNULL);
0120:         strcat(MyPath_T("\\"));
0121:     } else {
0122:     }
0123: }

API キーの保存場所は、そのユーザーの AppData フォルダに "\pahoo.org\earthquakewin\" というディレクトリを用意し、定数 FNAME_GOOGLE_API で用意したファイル名に保存する。管理者権限がないと "Program Files" に書き込めないので、AppData を利用するという Windows のお作法に則った。
なお、WiX を使って作ったアンインストーラーを実行すると、このフォルダを消去するようにしてある。

0469: /**
0470:  * Google Cloud Platform APIキーを読み込む
0471:  * @param なし
0472:  * @return bool TRUE:読込成功/FALSE:ファイルがない
0473: */
0474: bool readGoogleApiKey() {
0475:     string key;
0476:     bool ret = FALSE;
0477: 
0478:     ifstream ifs((string)MyPath + FNAME_GOOGLE_API);
0479:     //APIキー・ファイルが無ければ初期化
0480:     if (! ifs) {
0481:         GoogleAPIkey = "";
0482:         GoogleMap1 = "";
0483:         GoogleMap2 = "";
0484:         GoogleMap3 = "";
0485:         GoogleMap4 = "";
0486:         return ret;
0487:     }
0488: 
0489:     ifs >> key;
0490:     //APIキーなどの設定
0491:     if (key.length() > 0) {
0492:         GoogleAPIkey = key;
0493:         GoogleMap1 = "<script src='https://maps.googleapis.com/maps/api/js?key=" + key + "' async defer></script>";
0494:         GoogleMap2 = "<script src='https://unpkg.com/leaflet.gridlayer.googlemutant@latest/Leaflet.GoogleMutant.js'></script>";
0495:         GoogleMap3 = "var GM = L.gridLayer.googleMutant({type: 'roadmap'});";
0496:         GoogleMap4 = ",'Googleマップ' : GM";
0497:         ret = TRUE;
0498:     //APIキーが無ければ初期化
0499:     } else {
0500:         GoogleAPIkey = "";
0501:         GoogleMap1 = "";
0502:         GoogleMap2 = "";
0503:         GoogleMap3 = "";
0504:         GoogleMap4 = "";
0505:     }
0506: 
0507:     return ret;
0508: }

ユーザー関数 readGoogleApiKey は、API キーを読み込んで、有効なデータがあれば、変数 GoogleAPIkey および GoogleMap1GoogleMap4 に代入する。
その他の関数、ヘルプファイルやインストーラー作成方法については、これまでの連載で説明してきたとおりである。

参考サイト

(この項おわり)
header