目次
サンプル・プログラム
stationsearchwin.msi | インストーラ |
bin/stationsearchwin.exe | 実行プログラム本体 |
bin/cwebpage.dll bin/libcurl.dll | 実行時に必要になるDLL |
bin/etc/help.chm | ヘルプ・ファイル |
sour/stationsearchwin.cpp | ソース・プログラム |
sour/resource.h | リソース・ヘッダ |
sour/resource.rc | リソース・ファイル |
sour/application.ico | アプリケーション・アイコン |
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/makefile | ビルド |
バージョン | 更新日 | 内容 |
---|---|---|
1.1.4 | 2024/03/30 | 使用ライラリ更新 |
1.1.3 | 2023/11/23 | 使用ライラリ更新 |
1.1.2 | 2023/07/08 | 使用ライラリ更新 |
1.1.1 | 2023/04/01 | 使用ライラリ更新 |
1.1.0 | 2022/09/23 | UserAgent追加,ウィンドウ位置保存,ライブラリ更新 |
バージョン | 更新日 | 内容 |
---|---|---|
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.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 | 初版 |
使用ライブラリ
MSYS2 コマンドラインからビルドするのであれば、"makefile" を利用してほしい。
リソースの準備
Eclipse を起動し、新規プロジェクト stationsearchwin を用意する。
ResEdit を起動し、resource.rc を用意する。
Eclipse に戻り、ソース・プログラム "stationsearchwin.cpp" を追加する。
リンカー・フラグを -Wl,--enable-stdcall-fixup -mwindows -lgdiplus -static -lstdc++ -lgcc -lwinpthread -lcurl -lssl "(任意のパス)\libcurl.dll" "(任意のパス)\cwebpage.dll" "C:\Windows\system32\GdiPlus.dll" に設定する。
また、コマンド行パターンを ${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${INPUTS} -lole32 -loleaut32 -luuid にすること。
MSYS2 コマンドラインからビルドするのであれば、"makefile" を利用してほしい。
解説:定数など
36: // 定数など ==================================================================
37: #define MAKER "pahoo.org" //作成者
38: #define APPNAME "stationsearchwin" //アプリケーション名
39: #define APPNAMEJP "最寄駅検索" //アプリケーション名(日本語)
40: #define APPVERSION "1.1.4" //バージョン
41: #define APPYEAR "2020-24" //作成年
42: #define REFERENCE "https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-15-01.shtm" //参考サイト
43:
44: //ListViewItemの最大文字長:変更不可
45: #define MAX_LISTVIEWITEM 259
46:
47: //ヘルプ・ファイル
48: #define HELPFILE ".\\etc\\help.chm"
49:
50: //デフォルト保存ファイル名
51: #define SAVEFILE "stationsearchwin.csv"
52:
53: //現在のインターフェイス
54: HINSTANCE hInst;
55:
56: //アプリケーション・ウィンドウ
57: HWND hParent;
58:
59: //アプリケーション・ウィンドウ位置
60: unsigned hParent_X, hParent_Y;
61:
62: //検索キー格納用
63: string Query;
64:
65: //エラー・メッセージ格納用【変更不可】
66: string ErrorMessage;
67:
68: //ブラウザ・コントロール
69: WebBrowser wBrowser;
70:
71: //UserAgent
72: string UserAgent;
73:
74: //pahooGeocodeオブジェクト
75: pahooGeocode* pGC;
76:
77: //マップID
78: #define MAP_ID "map_id"
79: //地図の大きさ
80: #define MAP_WIDTH 530 //地図の幅(ピクセル)
81: #define MAP_HEIGHT 300 //地図の高さ(ピクセル)
82: //経度・緯度(初期値)
83: #define DEF_LONGITUDE 139.766667
84: double Longitude = DEF_LONGITUDE;
85: #define DEF_LATITUDE 35.681111
86: double Latitude = DEF_LATITUDE;
87: //地図拡大率(初期値)
88: #define DEF_ZOOM 13
89: int Zoom = DEF_ZOOM;
90: //地図の種類(初期値)
91: #define DEF_MAPTYPE "GSISTD"
92: string Maptype = DEF_MAPTYPE;
解説:検索の流れ
894: //検索
895: case IDM_EXEC:
896: case IDC_BUTTON_EXEC:
897: //検索キーがあればジオコードAPI呼び出し
898: Query = getStrEditBox(hDlg, IDC_EDIT_QUERY);
899: if (Query.length() > 0) {
900: if (pGC->searchPoints(_SW(Query), UserAgent, 0) > 0) {
901: Longitude = pGC->Ppoints[0].longitude;
902: Latitude = pGC->Ppoints[0].latitude;
903: }
904: //検索キーがなければ地図中心座標を取り出す
905: } else {
906: if (wBrowser.getInputById(L"longitude", &val)) {
907: Longitude = stod(val);
908: }
909: if (wBrowser.getInputById(L"latitude", &val)) {
910: Latitude = stod(val);
911: }
912: }
913: if (wBrowser.getInputById(L"zoom", &val)) {
914: Zoom = stoi(val);
915: }
916: if (wBrowser.getInputById(L"maptype", &val)) {
917: Maptype = _WS(val);
918: }
919: //カーソルを砂時計に
920: SetCursor(LoadCursor(NULL, IDC_WAIT));
921: //最寄駅検索
922: cnt = pHR->getResults_Heartrails(Latitude, Longitude);
923: if (cnt > 0) {
924: cnt = makeInformation(cnt);
925: }
926: //ブラウザ・コントロールに表示
927: viewBrowser(tmpname, cnt);
928: //最寄駅の一覧表示
929: makeListView(GetDlgItem(hDlg, IDC_LISTVIEW_STATIONS));
930: //エラー・リセット
931: ErrorMessage = "";
932: pGC->resetError();
933: break;
解説:INPUT要素の取得
JavaScriptであればgetElementByIdメソッドを使って取り出せるのだが、C++で同じことをやるのに骨が折れた。
191: /**
192: * Webブラウザ・コントロールにある要素を取り出す(ID指定)
193: * @param wstring id ID
194: * @param IHTMLElement** element 要素を格納
195: * @return bool TRUE:要素があった/なかった
196: */
197: bool getIHTMLElementById(std::wstring id, IHTMLElement** pElement) {
198: HWND iES = getIES();
199: static const UINT WM_HTML_GETOBJECT = ::RegisterWindowMessage(_T("WM_HTML_GETOBJECT"));
200: LRESULT res = 0;
201: SendMessageTimeout(iES, WM_HTML_GETOBJECT, 0, 0, SMTO_ABORTIFHUNG, 1000, reinterpret_cast<PDWORD_PTR>(&res));
202: if (! res) {
203: return FALSE;
204: }
205:
206: //DLL 内のエクスポート関数のアドレスを取得
207: HINSTANCE hInstance = LoadLibrary(_T("oleacc.dll"));
208: LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress(hInstance, "ObjectFromLresult" );
209: if (pfObjectFromLresult == NULL) {
210: return FALSE;
211: }
212:
213: //IHTMLDocument3 のポインタを取得
214: IHTMLDocument3* pHTMLDocument3;
215: (*pfObjectFromLresult)(res, IID_IHTMLDocument3, 0, (void **)&pHTMLDocument3);
216: if (! pHTMLDocument3) {
217: return FALSE;
218: }
219:
220: //IHTMLElement のポインタを取得
221: BSTR bstrId;
222: IHTMLElement* element;
223: bstrId = SysAllocString(id.c_str());
224: pHTMLDocument3->getElementById(bstrId, &element);
225:
226: *pElement = element;
227: if (! element) {
228: return FALSE;
229: }
230: return TRUE;
231: }
90: /**
91: * IES(InternetExplorer_Server)のハンドルを返す
92: * @param なし
93: * @return HWND ハンドル
94: */
95: HWND getIES(void) {
96: TCHAR className[128];
97: HWND hChild = GetWindow(control, GW_CHILD);
98: while (hChild) {
99: GetClassName(hChild, className, sizeof(className));
100: if (_tcscmp(_T("Internet Explorer_Server"), className) == 0) {
101: return hChild;
102: }
103: hChild = GetWindow(hChild, GW_CHILD);
104: }
105: return NULL;
106: }
HeartRails Geo API 最寄駅検索
URL |
---|
https://express.heartrails.com/api/xml |
フィールド名 | 要否 | 内 容 |
---|---|---|
method | 必須 | メソッド名:getStation(固定) |
x | 必須 | 最寄駅を取得したい場所の経度(世界測地系)。 |
y | 必須 | 最寄駅を取得したい場所の緯度(世界測地系)。 |
解説:HeartRails Geo APIの呼び出し
365: /**
366: * HeartRails Express API から必要な情報を配列に格納する
367: * @param double latitude 緯度(世界測地系)
368: * @param double longitude 経度(世界測地系)
369: * @return int ヒット数/(-1):エラー発生
370: */
371: int pahooHeartRailsExpress::getResults_Heartrails(double latitude, double longitude) {
372: char lng[SIZE_BUFF + 1], lat[SIZE_BUFF + 1];
373:
374: snprintf(lng, SIZE_BUFF, "%.5f", longitude);
375: snprintf(lat, SIZE_BUFF, "%.5f", latitude);
376: this->webapi = "http://express.heartrails.com/api/xml?method=getStations&x=" + (string)lng + "&y=" + (string)lat;
377: static string contents = "";
378: bool res = readWebContents(this->webapi, UserAgent, &contents);
379: if (res == FALSE) {
380: this->errmsg = _SW("HeartRails Express APIの接続エラーが発生しました");
381: return (-1);
382: }
383:
384: //配列の初期化
385: for (int i = 0; i < __SIZE_PPOINTS; i++) {
386: this->Pstations[i].id = 0;
387: this->Pstations[i].title = this->Pstations[i].line = this->Pstations[i].distance = L"";
388: this->Pstations[i].latitude = this->Pstations[i].longitude = 0.0;
389: }
390:
391: //XML読み込み
392: int cnt = 0;
393: try {
394: std::stringstream ss;
395: ss << contents;
396: ptree pt;
397: xml_parser::read_xml(ss, pt);
398:
399: //応答チェック
400: if (optional<string>str = pt.get_optional<string>("response.station")) {
401: } else {
402: this->errmsg = _SW("HeartRails Geo APIの応答エラーが発生しました");
403: return (-1);
404: }
405:
406: //XML解釈
407: try {
408: for (auto it : pt.get_child("response")) {
409: if (cnt >= __SIZE_PPOINTS) {
410: break;
411: }
412: //読み飛ばし
413: if (it.first != "station") {
414: continue;
415: }
416: //最寄駅名
417: if (optional<string>name = it.second.get_optional<string>("name")) {
418: this->Pstations[cnt].title = _UW(name.value());
419: }
420: //緯度
421: if (optional<string>lat = it.second.get_optional<string>("y")) {
422: this->Pstations[cnt].latitude = stod(lat.value());
423: }
424: //経度
425: if (optional<string>lng = it.second.get_optional<string>("x")) {
426: this->Pstations[cnt].longitude = stod(lng.value());
427: }
428: //路線
429: if (optional<string>line = it.second.get_optional<string>("line")) {
430: this->Pstations[cnt].line = _UW(line.value());
431: }
432: //距離
433: if (optional<string>distance = it.second.get_optional<string>("distance")) {
434: this->Pstations[cnt].distance = _UW(distance.value());
435: }
436: //識別子
437: this->Pstations[cnt].id = {(char)(65 + cnt)};
438: cnt++;
439: }
440: //XML解釈エラー
441: } catch(ptree_bad_path& e) {
442: this->errmsg = _SW("HeartRails Geo APIの検索エラーが発生しました");
443: return (-1);
444: }
445:
446: //読み込みエラー
447: } catch(xml_parser_error& e) {
448: this->errmsg = _SW("HeartRails Geo APIの接続エラーが発生しました");
449: return (-1);
450: }
451: contents.clear();
452:
453: return cnt;
454: }
解説:パラメータの保存と読み込み
133: /**
134: * パラメータの読み込み
135: * @param なし
136: * @return なし
137: */
138: void loadParameter(void) {
139: ptree pt;
140:
141: //初期値設定
142: initParameter();
143:
144: //XMLファイル読み込み
145: try {
146: xml_parser::read_xml(getMyPath(APPNAME) + APPNAME + ".xml", pt);
147:
148: //XML解釈
149: try {
150: //形式チェック
151: if (optional<string>str = pt.get_optional<string>("parameter")) {
152: } else {
153: return;
154: }
155: //パラメータ読み込み
156: for (auto it : pt.get_child("parameter")) {
157: string type= it.second.get_optional<string>("<xmlattr>.type").value();
158: if (type == "latitude") {
159: Latitude = stod(it.second.data());
160: } else if (type == "longitude") {
161: Longitude = stod(it.second.data());
162: } else if (type == "zoom") {
163: Zoom = stoi(it.second.data());
164: } else if (type == "maptype") {
165: Maptype = (string)it.second.data();
166: } else if (type == "wx") {
167: hParent_X = (unsigned)stoi(it.second.data());
168: } else if (type == "wy") {
169: hParent_Y = (unsigned)stoi(it.second.data());
170: } else if (type == "query") {
171: Query = utf8_sjis((string)it.second.data());
172: }
173: }
174: //解釈失敗したら初期値設定
175: } catch (xml_parser_error& e) {
176: initParameter();
177: return;
178: }
179: //読み込み失敗したら初期値設定
180: } catch (xml_parser_error& e) {
181: initParameter();
182: return;
183: }
184:
185: //アプリケーション・ウィンドウの位置(デスクトップ範囲外なら原点移動)
186: HWND hDesktop = GetDesktopWindow();
187: WINDOWINFO windowInfo;
188: windowInfo.cbSize = sizeof(WINDOWINFO);
189: GetWindowInfo(hDesktop, &windowInfo);
190: if (hParent_X >= (unsigned)windowInfo.rcWindow.right) {
191: hParent_X = 0;
192: }
193: if (hParent_Y >= (unsigned)windowInfo.rcWindow.bottom) {
194: hParent_Y = 0;
195: }
196: }
198: /**
199: * パラメータの保存
200: * @param なし
201: * @return なし
202: */
203: void saveParameter(void) {
204: #ifndef CMDAPP
205: //アプリケーション・ウィンドウの位置取得
206: WINDOWINFO windowInfo;
207: windowInfo.cbSize = sizeof(WINDOWINFO);
208: GetWindowInfo(hParent, &windowInfo);
209: hParent_X = (unsigned)windowInfo.rcWindow.left;
210: hParent_Y = (unsigned)windowInfo.rcWindow.top;
211: if (hParent_X >= (unsigned)windowInfo.rcWindow.right) {
212: hParent_X = 0;
213: }
214: if (hParent_Y >= (unsigned)windowInfo.rcWindow.bottom) {
215: hParent_Y = 0;
216: }
217: #endif
218: //ブラウザ・コントロールからパラメータを取り出す
219: wstring val;
220: if (wBrowser.getInputById(L"longitude", &val)) {
221: Longitude = stod(val);
222: }
223: if (wBrowser.getInputById(L"latitude", &val)) {
224: Latitude = stod(val);
225: }
226: if (wBrowser.getInputById(L"zoom", &val)) {
227: Zoom = stoi(val);
228: }
229: if (wBrowser.getInputById(L"maptype", &val)) {
230: Maptype = _WS(val);
231: }
232:
233: char lng[SIZE_BUFF + 1], lat[SIZE_BUFF + 1];
234: snprintf(lng, SIZE_BUFF, "%.5f", Longitude);
235: snprintf(lat, SIZE_BUFF, "%.5f", Latitude);
236:
237: //XMLファイルへ書き込む
238: ptree pt;
239: ptree& child1 = pt.add("parameter.param", lat);
240: child1.add("<xmlattr>.type", "latitude");
241: ptree& child2 = pt.add("parameter.param", lng);
242: child2.add("<xmlattr>.type", "longitude");
243: ptree& child3 = pt.add("parameter.param", to_string(Zoom));
244: child3.add("<xmlattr>.type", "zoom");
245: ptree& child4 = pt.add("parameter.param", Maptype);
246: child4.add("<xmlattr>.type", "maptype");
247: ptree& child5 = pt.add("parameter.param", (string)to_string(hParent_X));
248: child5.add("<xmlattr>.type", "wx");
249: ptree& child6 = pt.add("parameter.param", (string)to_string(hParent_Y));
250: child6.add("<xmlattr>.type", "wy");
251: ptree& child7 = pt.add("parameter.param", sjis_utf8(Query));
252: child7.add("<xmlattr>.type", "query");
253:
254: const int indent = 4;
255: write_xml(getMyPath(APPNAME) + APPNAME + ".xml", pt, std::locale(),
256: xml_writer_make_settings<std::string>(' ', indent));
257: }
参考サイト
- PHPで住所・ランドマークから最寄駅を求める:ぱふぅ家のホームページ
- WiX によるWindowsインストーラー作成:ぱふぅ家のホームページ
- C++ 開発環境の準備:ぱふぅ家のホームページ
「PHPで住所・ランドマークから最寄駅を求める」で作ったPHPプログラムをC++に移植したものである。
(2024年3月30日)使用ライラリ更新
(2023年11月23日)使用ライラリ更新
(2023年7月8日)検索に国土地理院ジオコーディングAPIを追加,使用ライラリ更新
(2023年4月1日)使用ライラリ更新