C++ で週間天気予報を表示する

(1/1)
>C++で週間天気予報を表示する
本プログラムは、「PHPで天気予報を求める」「PHPで地図で指定した場所の天気予報を求める」で作ったPHPプログラムをC++に移植したものである。
インターネット経由で気象庁防災情報XMLにアクセスし、地図や住所、ランドマーク,郵便番号等で指定した地点の週間天気予報を一覧表示するアプリケーションを作る。一覧情報をクリップボードにコピーしたり、CSV形式ファイルに保存することができる。また、ユーザーがAPIキーを取得することでGoogleマップやGoogle住所検索も利用できるようになる。

(2024年3月30日)予報地点情報ファイルおよび使用ライブラリを更新.
(2023年12月30日)予報地点情報ファイルおよび使用ライブラリを更新.
(2023年11月23日)使用ライブラリを更新.
(2023年8月27日)予報地点情報ファイルおよび使用ライブラリを更新.
(2023年7月8日)使用ライブラリを更新.
(2023年3月18日)予報地点情報ファイルおよび使用ライブラリを更新.
(2023年2月11日)予報地点情報ファイルおよび使用ライブラリを更新.
を追加.

目次

サンプル・プログラム

圧縮ファイルの内容
weeklyweather.msiインストーラ
bin/weeklyweather.exe実行プログラム本体
bin/jmaweatherspots.xml予報地点情報ファイル
bin/cwebpage.dll
bin/libcurl.dll
実行時に必要になるDLL
bin/etc/help.chmヘルプ・ファイル
sour/weeklyweather.cppソース・プログラム
sour/resource.hリソース・ヘッダ
sour/resource.rcリソース・ファイル
sour/application.icoアプリケーション・アイコン
sour/mystrings.cpp汎用文字列処理関数など(ソース)
sour/mystrings.h汎用文字列処理関数など(ヘッダ)
sour/pahooGeocode.cpp住所・緯度・経度に関わるクラス(ソース)
sour/pahooGeocode.hpp住所・緯度・経度に関わるクラス(ヘッダ)
sour/pahooWeather.cpp気象情報に関わるクラス(ソース)
sour/pahooWeather.hpp気象情報に関わるクラス(ヘッダ)
sour/pahooCache.cppキャッシュ処理に関わるクラス(ソース)
sour/pahooCache.hppキャッシュ処理に関わるクラス(ヘッダ)
sour/makefileビルド
weeklyweather.cpp 更新履歴
バージョン 更新日 内容
2.5.7 2024/03/30 予報地点情報ファイルおよび使用ライブラリを更新
2.5.6 2023/12/30 予報地点情報ファイルおよび使用ライブラリを更新
2.5.5 2023/11/23 使用ライブラリを更新
2.5.4 2023/08/27 予報地点情報ファイルおよび使用ライブラリを更新
2.5.3 2023/07/08 使用ライブラリを更新
pahooWeather.cpp 更新履歴
バージョン 更新日 内容
2.2 2022/03/12 気象庁防災情報XMLのhttps化に対応
2.1 2021/04/14 キャッシュ・システム導入:pahooCacheクラス
2.0 2021/03/16 気象庁防災情報XMLから情報取得に変更
1.2 2020/12/20 bug-fix
1.1 2020/12/05 __jma_readWeeklyWeather() 月日取得方式変更
pahooGeocode.cpp 更新履歴
バージョン 更新日 内容
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変更
pahooCache.cpp 更新履歴
バージョン 更新日 内容
1.0 2021/04/14
mystrings.cpp 更新履歴
バージョン 更新日 内容
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 初版

使用ライブラリ

気象庁週間予報サイトにアクセスするために、オープンソースのライブラリ Boost C++ライブラリcURL (カール)  および OpenSSL が必要になる。導入方法等については、「C++ 開発環境の準備」をご覧いただきたい。

リソースの準備

今回は32ビット版の開発環境を用いる。
Eclipse を起動し、新規プロジェクト weeklyweather を用意する。
ResEdit を起動し、resource.rc を用意する。
Eclipse に戻り、ソース・プログラム "weeklyweather.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" を利用してほしい。

解説:定数など

  38: // 定数など ==================================================================
  39: #define MAKER       "pahoo.org"         //作成者
  40: #define APPNAME     "weeklyweather"     //アプリケーション名
  41: #define APPNAMEJP   "週間天気予\報"      //アプリケーション名(日本語)
  42: #define APPVERSION  "2.5.7"             //バージョン
  43: #define APPYEAR     "2020-24"           //作成年
  44: #define REFERENCE   "https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-16-01.shtm"   //参考サイト
  45: 
  46: //ヘルプ・ファイル
  47: #define HELPFILE    ".\\etc\\help.chm"
  48: 
  49: //デフォルト保存ファイル名
  50: #define SAVEFILE    "weeklyweather.csv"
  51: 
  52: //キャッシュ・ディレクトリ
  53: #define CACHEDIR_WEATHER    "pcache\\"
  54: 
  55: //キャッシュ保持時間(デフォルト;分)(0:キャッシュしない)
  56: #define LIFE_CACHE_WEATHER  (2 * 60)
  57: 
  58: //現在のインターフェイス
  59: HINSTANCE hInst;
  60: 
  61: //アプリケーション・ウィンドウ
  62: HWND hParent;
  63: 
  64: //アプリケーション・ウィンドウ位置
  65: unsigned hParent_X, hParent_Y;
  66: 
  67: //検索キー格納用
  68: string Query;
  69: 
  70: //エラー・メッセージ格納用【変更不可】
  71: string ErrorMessage;
  72: 
  73: //ブラウザ・コントロール
  74: WebBrowser wBrowser;
  75: 
  76: //UserAgent
  77: string UserAgent;
  78: 
  79: //pahooWeatherオブジェクト
  80: pahooWeather *pWT;
  81: 
  82: //pahooGeocodeオブジェクト
  83: pahooGeocode *pGC;
  84: 
  85: //pahooCacheオブジェクト
  86: pahooCache *pCC;
  87: 
  88: //予報表のID
  89: #define IDC_CAL_LABEL   1501        //日付
  90: #define IDC_RES_LABEL   1601        //天気予報
  91: #define IDC_RES_IMAGE   1701        //天気アイコン
  92: #define IDC_TEMP_LABEL  1801        //降水確率・最低・最高気温
  93: 
  94: //予報表の座標
  95: #define IDC_RES_X       10
  96: #define IDC_RES_Y       430
  97: #define IDC_RES_WIDTH   80
  98: 
  99: #define FORECAST_HEIGHT 140     //予報表の高さ
 100: 
 101: //マップに表示するマーカー画像URL
 102: #define URL_MARKER "https://maps.google.co.jp/mapfiles/ms/icons/yellow-dot.png"
 103: #define MAP_WIDTH       530     //地図の幅(ピクセル)
 104: #define MAP_HEIGHT      300     //地図の高さ(ピクセル)
 105: 
 106: //マップID
 107: #define MAP_ID          "map_id"
 108: //経度・緯度(初期値)
 109: #define DEF_LONGITUDE   139.766667
 110: double Longitude = DEF_LONGITUDE;
 111: #define DEF_LATITUDE    35.681111
 112: double Latitude  = DEF_LATITUDE;
 113: //地図拡大率(初期値)
 114: #define DEF_ZOOM    6
 115: int Zoom = DEF_ZOOM;
 116: //地図の種類(初期値)
 117: #define DEF_MAPTYPE     "GSISTD"
 118: string Maptype = DEF_MAPTYPE;

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

解説:pahooWeatherクラスとデータ構造

今回は、気象庁防災情報XMLを解析することで天気予報情報を取り出すのだが、必要なメソッドとデータ構造をPHPプログラムから移植し、クラス pahooWeather クラス としてコーディングした。ソースは "pahooWeather.cpp" ヘッダは "pahooWeather.hpp" である。

  25: //天気予報情報
  26: typedef struct _jmaWeeklyWeather {
  27:     int year;                       //西暦年
  28:     int month;                      //月
  29:     int day;                        //日
  30:     std::wstring day_of_week;       //曜日
  31:     std::wstring weather;           //天気予報
  32:     std::string  image;             //天気予報アイコンURL
  33:     std::string  rainy;             //降水確率
  34:     std::string  temp_max;          //最高気温
  35:     std::string  temp_min;          //最低気温
  36:     std::string  stationCode;       //予報地点コード
  37:     std::wstring stationName;       //予報地点名
  38:     std::wstring location;          //所在地
  39: jmaWeeklyWeather_t;

1日分の予報情報は、構造体 forecast_t に格納する。これを7日分、配列として管理する。

  39: //予報地点情報格納用クラス ===================================================
  40: #define FILE_VERSION "2.2"          //予報値地点情報ファイルのバージョン
  41: #define SIZE_SPOTS  500             //格納上限
  42: 
  43: class _Spots {
  44: public:
  45:     string  code        = "";       //電文コード
  46:     string  page        = "";       //ページ番号
  47:     string  regionCode  = "";       //地方コード
  48:     wstring regionName  = L"";      //地方名
  49:     string  prefCode    = "";       //都道府県コード
  50:     wstring prefName    = L"";      //都道府県名
  51:     string  areaCode    = "";       //地域コード
  52:     wstring areaName    = L"";      //地域名
  53:     string  stationCode = "";       //予報地点コード
  54:     wstring stationName = L"";      //予報地点名
  55:     wstring location    = L"";      //所在地
  56:     double  latitude    = 0.0;      //緯度
  57:     double  longitude   = 0.0;      //経度
  58: };
  59: static unique_ptr<_Spots> Spots[SIZE_SPOTS] = {};

予報地点の情報は、あらかじめ FILE_SPOTS で示されるXMLファイルに格納しておく。このファイルは、「PHPで天気予報を求める」で紹介したプログラムを使って作成することができる。

 115: /**
 116:  * 予報地点情報を読み込む
 117:  * @param   なし
 118:  * @return  int 読み込んだ地点情報数/(-1):読み込み失敗
 119:  */
 120: int pahooWeather::readJmaSpots(void) {
 121:     int cnt = -1;
 122:     try {
 123:         //XMLファイル読み込み
 124:         ptree pt;
 125:         xml_parser::read_xml(FILE_SPOTS, pt);
 126:         //XML解釈
 127:         ptree tree;
 128:         for (auto it : pt.get_child("jmaweatherspots")) {
 129:             cnt++;
 130:             Spots[cnt] = make_unique<_Spots>();
 131:             //電文コード
 132:             if (optional<string>code = it.second.get_optional<string>("code")) {
 133:                 Spots[cnt]->code = code.value();
 134:             }
 135:             //ページ番号
 136:             if (optional<string>page = it.second.get_optional<string>("page")) {
 137:                 Spots[cnt]->page = page.value();
 138:             }
 139:             //地方コード
 140:             if (optional<string>regionCode = it.second.get_optional<string>("regionCode")) {
 141:                 Spots[cnt]->regionCode = regionCode.value();
 142:             }
 143:             //地方名
 144:             if (optional<string>regionName = it.second.get_optional<string>("regionName")) {
 145:                 Spots[cnt]->regionName = _UW(regionName.value());
 146:             }
 147:             //都道府県コード
 148:             if (optional<string>prefCode = it.second.get_optional<string>("prefCode")) {
 149:                 Spots[cnt]->prefCode = prefCode.value();
 150:             }
 151:             //都道府県名
 152:             if (optional<string>prefName = it.second.get_optional<string>("prefName")) {
 153:                 Spots[cnt]->prefName = _UW(prefName.value());
 154:             }
 155:             //地域コード
 156:             if (optional<string>areaCode = it.second.get_optional<string>("areaCode")) {
 157:                 Spots[cnt]->areaCode = areaCode.value();
 158:             }
 159:             //地域名
 160:             if (optional<string>areaName = it.second.get_optional<string>("areaName")) {
 161:                 Spots[cnt]->areaName = _UW(areaName.value());
 162:             }
 163:             //予報地点コード
 164:             if (optional<string>stationCode = it.second.get_optional<string>("stationCode")) {
 165:                 Spots[cnt]->stationCode = stationCode.value();
 166:             }
 167:             //予報地点名
 168:             if (optional<string>stationName = it.second.get_optional<string>("stationName")) {
 169:                 Spots[cnt]->stationName = _UW(stationName.value());
 170:             }
 171:             //所在地
 172:             if (optional<string>location = it.second.get_optional<string>("location")) {
 173:                 Spots[cnt]->location = _UW(location.value());
 174:             }
 175:             //
 176:             //経度
 177:             if (optional<string>longitude = it.second.get_optional<string>("longitude")) {
 178:                 Spots[cnt]->longitude = stod(longitude.value());
 179:             }
 180:             //緯度
 181:             if (optional<string>latitude = it.second.get_optional<string>("latitude")) {
 182:                 Spots[cnt]->latitude = stod(latitude.value());
 183:             }
 184:         }
 185:     //読み込みエラー
 186:     } catch(xml_parser_error& e) {
 187:         errmsg = _SW("予\報地点情報ファイルを読み込めません");
 188:         return (-1);
 189:     }
 190: 
 191:     return cnt;
 192: }

予報地点の情報は、メソッド readJmaSpots を使い、前述の構造体配列 Spots へ格納する。

 235: /**
 236:  * 指定した緯度・経度に最も近い予報地点コードを返す
 237:  * @param   double latitude  緯度(世界測地系)
 238:  * @param   double longitude 経度(世界測地系)
 239:  * @param   int    forecast  0:天気予報,1:週間天気予報(省略時:1)
 240:  * @return  string stationCode 予報地点コードを格納
 241:  */
 242: string pahooWeather::getJmaNearSpot(double longitude, double latitude, int forecast=1) {
 243:     string stationCode = "";
 244:     double d0  = 999999999.9;
 245:     //電文コード
 246:     string code = "";
 247:     if (forecast == 0) {
 248:         code = "VPFD51";
 249:     } else {
 250:         code = "VPFW50";
 251:     }
 252: 
 253:     for (int i = 0i < SIZE_SPOTSi++) {
 254:         if (Spots[i] == NULL) {
 255:             break;
 256:         }
 257:         if (Spots[i]->code == code) {
 258:             double d1 = this->distance(Spots[i]->longitude, Spots[i]->latitude, longitude, latitude);
 259:             if (d1 < d0) {
 260:                 stationCode = Spots[i]->stationCode;
 261:                 d0 = d1;
 262:             }
 263:         }
 264:     }
 265:     return stationCode;
 266: }

メソッド getJmaNearSpot は、緯度・経度を引数として、前述の予報地点情報を収めた構造体配列 Spots を参照し、一番近い予報地点コードを返す。

 524: /**
 525:  * 気象庁防災情報XMLから天気予報情報を読み込む
 526:  * @param   string station  予報地点コード
 527:  * @param   int    forecast 0:天気予報,1:週間天気予報(省略時:1)
 528:  *                  0のとき‥‥jmaWeeklyWeather[0](本日)〜[6](6日後)に代入
 529:  *                  1のとき‥‥jmaWeeklyWeather[0](本日)〜[2](2日後)に代入
 530:  * @return  bool TRUE:成功/FALSE:失敗
 531: */
 532: bool pahooWeather::jmaGetWeatherForecast(string station, int forecast) {
 533:     //曜日
 534:     static wstring week_name[] = {_SW("日"), _SW("月"), _SW("火"), _SW("水"), _SW("木"), _SW("金"), _SW("土")};
 535: 
 536:     //マッチングパターン
 537:     regex re11("([0-9]+)\\-([0-9]+)\\-([0-9]+)");               //年月日
 538:     regex re12("([0-9]+)\\-([0-9]+)\\-([0-9]+)T([0-9]+)\\:");   //年月日時
 539:     wregex re21(_SW("から"));                                   //降水確率
 540:     wregex re31(_SW("最低気温"));                               //最低気温
 541:     wregex re32(_SW("^日中の最高気温"));                        //最高気温
 542:     regex re41("^[0-9]+$");                                     //正数
 543:     smatch  mt1;
 544:     wsmatch mt2;
 545:     int n;
 546: 
 547:     //電文コード
 548:     string code;
 549:     if (forecast == 0) {
 550:         code = "VPFD51";
 551:     } else {
 552:         code = "VPFW50";
 553:     }
 554: 
 555:     //予報地点コードの取得
 556:     bool res = FALSE;
 557:     int id;
 558:     for (id = 0id < SIZE_SPOTSid++) {
 559:         if (Spots[id] == NULL) {
 560:             break;
 561:         }
 562:         if ((Spots[id]->code == code&& (Spots[id]->stationCode == station)) {
 563:             res = TRUE;
 564:             break;
 565:         }
 566:     }
 567:     if (res == FALSE) {
 568:         errmsg = _SW("予\報地点コードが見つかりません");
 569:         return FALSE;
 570:     }
 571: 
 572:     //初日のみ初期化
 573:     time_t now = time(NULL);
 574:     struct tm* pnow = localtime(&now);
 575:     namespace gr = boost::gregorian;
 576:     gr::date dt(pnow->tm_year + 1900, pnow->tm_mon + 1, pnow->tm_mday);
 577:     int i = 0, dd = 0, d2 = 0;
 578:     //天気予報アイコンを夜間にするかどうか
 579:     int mode = 0;
 580:     if (pnow->tm_hour >18) {
 581:         mode = 1;
 582:     }
 583: 
 584:     this->jmaWeeklyWeather[i].year  = dt.year();
 585:     this->jmaWeeklyWeather[i].month = dt.month();
 586:     this->jmaWeeklyWeather[i].day   = dt.day();
 587:     this->jmaWeeklyWeather[i].day_of_week = week_name[dt.day_of_week()];
 588:     this->jmaWeeklyWeather[i].stationName = Spots[id]->stationName;
 589:     this->jmaWeeklyWeather[i].weather  = L"";
 590:     this->jmaWeeklyWeather[i].image    = "";
 591:     this->jmaWeeklyWeather[i].rainy    = "";
 592:     this->jmaWeeklyWeather[i].temp_max = "";
 593:     this->jmaWeeklyWeather[i].temp_min = "";
 594: 
 595:     //最新の週間天気予報情報URLを取得
 596:     long page           = stol(Spots[id]->page);
 597:     string areaCode     = Spots[id]->areaCode;
 598:     string stationCode  = Spots[id]->stationCode;
 599:     wstring stationName = Spots[id]->stationName;
 600:     wstring location    = Spots[id]->location;
 601: 
 602:     string vpfd51="", vpfw50="";
 603:     jmaGetWeatherForecastURL(page, &vpfd51, &vpfw50);
 604:     if (errmsg !L"") {
 605:         return FALSE;
 606:     }
 607: 
 608:     //VPFW51(府県週間天気予報)の解析
 609:     string contents = "";
 610:     if (forecast == 1) {
 611:         //XML読み込み
 612: //      readWebContents(vpfw50, UserAgent, &contents);
 613:         if (pCC->load(vpfw50, &contents) == FALSE) {
 614:             errmsg = pCC->getError();
 615:             return FALSE;
 616:         }
 617:         try {
 618:             std::stringstream ss;
 619:             ss << contents;
 620:             ptree pt;
 621:             xml_parser::read_xml(ss, pt);
 622: 
 623:             //XML解釈
 624:             try {
 625:                 //年月日取得
 626:                 for (auto it : pt.get_child("Report.Body.MeteorologicalInfos.TimeSeriesInfo.TimeDefines")) {
 627:                     if (optional<string>dti = it.second.get_optional<string>("DateTime")) {
 628:                         if (regex_search(dti.value(), mt1, re11)) {
 629:                             optional<string>tid = it.second.get_optional<string>("<xmlattr>.timeId");
 630: //                          cout << tid.value() << " : " << mt1[0].str() << endl;
 631:                             //最初の情報は何時か
 632:                             i = stoi(tid.value());
 633:                             gr::date dt2(stoi(mt1[1].str()), stoi(mt1[2].str()), stoi(mt1[3].str()));
 634: 
 635:                             if (i == 1) {
 636:                                 if (dt < dt2) {
 637:                                     dd = 0;         //明日
 638:                                 } else if (dt == dt2) {
 639:                                     dd = (-1);      //今日
 640:                                 } else {
 641:                                     dd = (-2);      //明日
 642:                                 }
 643:                             }
 644:                             i +dd;
 645:                             if (i < 0) {
 646:                                 continue;
 647:                             }
 648:                             //予報1日分の初期化
 649:                             gr::date dt(stoi(mt1[1].str()), stoi(mt1[2].str()), stoi(mt1[3].str()));
 650:                             this->jmaWeeklyWeather[i].year  = stoi(mt1[1].str());
 651:                             this->jmaWeeklyWeather[i].month = stoi(mt1[2].str());
 652:                             this->jmaWeeklyWeather[i].day   = stoi(mt1[3].str());
 653:                             this->jmaWeeklyWeather[i].day_of_week = week_name[dt.day_of_week()];
 654:                             this->jmaWeeklyWeather[i].stationName = Spots[id]->stationName;
 655:                             this->jmaWeeklyWeather[i].weather  = L"";
 656:                             this->jmaWeeklyWeather[i].image    = "";
 657:                             this->jmaWeeklyWeather[i].rainy    = "";
 658:                             this->jmaWeeklyWeather[i].temp_max = "";
 659:                             this->jmaWeeklyWeather[i].temp_min = "";
 660:                         }
 661:                     }
 662:                 }
 663: 
 664:                 for (auto it : pt.get_child("Report.Body")) {
 665:                     //天気・降水確率の取得
 666:                     if (optional<string>type = it.second.get_optional<string>("<xmlattr>.type")) {
 667:                         if (_UW(type.value()) == _SW("区域予\報")) {
 668:                             for (auto it2 : it.second.get_child("TimeSeriesInfo")) {
 669:                                 if (optional<string>acode = it2.second.get_optional<string>("Area.Code")) {
 670:                                     if (acode.value() !areaCode) {
 671:                                         continue;
 672:                                     }
 673:                                 }
 674:                                 for (auto it3 : it2.second.get_child("")) {
 675:                                     if (optional<string>type = it3.second.get_optional<string>("Property.Type")) {
 676:                                         if (_UW(type.value()) == _SW("天気")) {
 677:                                             //天気予報の取得
 678:                                             for (auto it4 : it3.second.get_child("Property.WeatherPart")) {
 679:                                                 if (optional<string>we = it4.second.get_optional<string>("")) {
 680:                                                     i = stoi(it4.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
 681:                                                     if (i < 0) {
 682:                                                         continue;
 683:                                                     }
 684:                                                     this->jmaWeeklyWeather[i].weather = jmaShortWeather(_UW(we.value()));
 685:                                                 }
 686:                                             }
 687:                                             //天気予報用テロップ番号の取得
 688:                                             for (auto it4 : it3.second.get_child("Property.WeatherCodePart")) {
 689:                                                 if (optional<string>we = it4.second.get_optional<string>("")) {
 690:                                                     i = stoi(it4.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
 691:                                                     if (i < 0) {
 692:                                                         continue;
 693:                                                     }
 694:                                                     if (i == 0) {
 695:                                                         this->jmaWeeklyWeather[i].image = jma_telop2url(stoi(we.value()), mode);
 696:                                                     } else {
 697:                                                         this->jmaWeeklyWeather[i].image = jma_telop2url(stoi(we.value()), 0);
 698:                                                     }
 699:                                                 }
 700:                                             }
 701:                                         } else if (_UW(type.value()) == _SW("降水確率")) {
 702:                                             //降水確率の取得
 703:                                             for (auto it4 : it3.second.get_child("Property.ProbabilityOfPrecipitationPart")) {
 704:                                                 if (optional<string>we = it4.second.get_optional<string>("")) {
 705:                                                     i = stoi(it4.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
 706:                                                     if (i < 0) {
 707:                                                         continue;
 708:                                                     }
 709:                                                     this->jmaWeeklyWeather[i].rainy = we.value();
 710:                                                 }
 711:                                             }
 712:                                         }
 713:                                     }
 714:                                 }
 715:                             }
 716:                         } else if (_UW(type.value()) == _SW("地点予\報")) {
 717:                             for (auto it2 : it.second.get_child("TimeSeriesInfo")) {
 718:                                 if (optional<string>scode = it2.second.get_optional<string>("Station.Code")) {
 719:                                     if (scode.value() !stationCode) {
 720:                                         continue;
 721:                                     }
 722:                                 }
 723:                                 for (auto it3 : it2.second.get_child("")) {
 724:                                     for (auto it4 : it3.second.get_child("")) {
 725:                                         if (optional<string>type = it4.second.get_optional<string>("Type")) {
 726:                                             if (_UW(type.value()) == _SW("最低気温")) {
 727:                                                 //最低気温の取得
 728:                                                 for (auto it5 : it4.second.get_child("TemperaturePart")) {
 729:                                                     if (optional<string>we = it5.second.get_optional<string>("")) {
 730:                                                         i = stoi(it5.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
 731:                                                         if (i < 0) {
 732:                                                             continue;
 733:                                                         }
 734:                                                         this->jmaWeeklyWeather[i].temp_min = we.value();
 735:                                                     }
 736:                                                 }
 737:                                             } else if (_UW(type.value()) == _SW("最高気温")) {
 738:                                                 //最高気温の取得
 739:                                                 for (auto it5 : it4.second.get_child("TemperaturePart")) {
 740:                                                     if (optional<string>we = it5.second.get_optional<string>("")) {
 741:                                                         i = stoi(it5.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
 742:                                                         if (i < 0) {
 743:                                                             continue;
 744:                                                         }
 745:                                                         this->jmaWeeklyWeather[i].temp_max = we.value();
 746:                                                     }
 747:                                                 }
 748:                                             }
 749:                                         }
 750:                                     }
 751:                                 }
 752:                             }
 753:                         }
 754:                     }
 755:                 }
 756: 
 757:             //XML解釈エラー
 758:             } catch(ptree_bad_path& e) {
 759:                 errmsg = _SW("府県週間天気予\報情報を取得できません");
 760:                 return FALSE;
 761:             }
 762:         //読み込みエラー
 763:         } catch(xml_parser_error& e) {
 764:             errmsg = _SW("府県週間天気予\報情報を取得できません");
 765:             return FALSE;
 766:         }
 767:     }
 768: 
 769:     //VPFD51(府県天気予報 R1)の解析
 770:     code = "VPFD51";
 771:     //予報地点コードの取得
 772:     res = FALSE;
 773:     for (id = 0id < SIZE_SPOTSid++) {
 774:         if (Spots[id] == NULL) {
 775:             break;
 776:         }
 777:         if ((Spots[id]->code == code&& (Spots[id]->stationCode == station)) {
 778:             res = TRUE;
 779:             break;
 780:         }
 781:     }
 782:     if (res == FALSE) {
 783:         errmsg = _SW("予\報地点コードが見つかりません");
 784:         return FALSE;
 785:     }
 786:     areaCode = Spots[id]->areaCode;
 787: 
 788:     string rain_table[8] = { "-", "-", "-", "-", "-", "-", "-", "-", };
 789:     int temp_table[5] = { 0, 0, 0, 0, 0 };
 790:     //XML読み込み
 791:     string contents2 = "";
 792: //  readWebContents(vpfd51, UserAgent, &contents2);
 793:     if (pCC->load(vpfd51, &contents2) == FALSE) {
 794:         errmsg = pCC->getError();
 795:         return FALSE;
 796:     }
 797:     try {
 798:         std::stringstream ss;
 799:         ss << contents2;
 800:         ptree pt;
 801:         xml_parser::read_xml(ss, pt);
 802: 
 803:         //XML解釈
 804:         try {
 805:             //年月日取得
 806:             for (auto it : pt.get_child("Report.Body")) {
 807:                 if (optional<string>type = it.second.get_optional<string>("<xmlattr>.type")) {
 808:                     if (_UW(type.value()) == _SW("区域予\報")) {
 809:                         //日時
 810:                         for (auto it2 : it.second.get_child("")) {
 811:                             for (auto it3 : it2.second.get_child("")) {
 812:                                 for (auto it4 : it3.second.get_child("")) {
 813:                                     if (optional<string>name = it4.second.get_optional<string>("Name")) {
 814:                                         wstring ws = _UW(name.value());
 815: 
 816:                                         if (regex_search(ws, mt2, re21)) {
 817:                                             string ss = it4.second.get_optional<string>("DateTime").value();
 818:                                             regex_search(ss, mt1, re12);
 819:                                             optional<string>tid = it4.second.get_optional<string>("<xmlattr>.timeId");
 820: //                                          cout << tid.value() << " : " << mt1[0].str() << endl;
 821:                                             //最初の情報は何時か
 822:                                             i = stoi(tid.value());
 823:                                             gr::date dt2(stoi(mt1[1].str()), stoi(mt1[2].str()), stoi(mt1[3].str()));
 824:                                             if (i == 1) {
 825:                                                 if (dt < dt2) {
 826:                                                     dd = 0;         //明日
 827:                                                 } else if (dt == dt2) {
 828:                                                     dd = (-1);      //今日
 829:                                                 } else {
 830:                                                     dd = (-2);      //明日
 831:                                                 }
 832:                                                 d2 = stoi(mt1[4].str()) / 6;        //6時間毎
 833:                                                 if (dd == (-2)) {
 834:                                                     d2 = d2 - 4 + 1;
 835:                                                 }
 836:                                             }
 837:                                         } else if (optional<string>s2 = it4.second.get_optional<string>("DateTime")) {
 838:                                             string ss = s2.value();
 839:                                             if (regex_search(ss, mt1, re11)) {
 840:                                                 //最初の情報は何時か
 841:                                                 optional<string>tid = it4.second.get_optional<string>("<xmlattr>.timeId");
 842:                                                 i = stoi(tid.value());
 843:                                                 gr::date dt2(stoi(mt1[1].str()), stoi(mt1[2].str()), stoi(mt1[3].str()));
 844:                                                 if (i == 1) {
 845:                                                     if (dt < dt2) {
 846:                                                         dd = 0;         //明日
 847:                                                     } else if (dt == dt2) {
 848:                                                         dd = (-1);      //今日
 849:                                                     } else {
 850:                                                         dd = (-2);      //明日
 851:                                                     }
 852:                                                 }
 853:                                             }
 854:                                         }
 855:                                     }
 856:                                 }
 857:                             }
 858:                             //地方コードは上何桁で判定するか
 859:                             bool flag = FALSE;
 860:                             for (n = 5n >1n--) {
 861:                                 for (auto it3 : it2.second.get_child("")) {
 862:                                     if (optional<string>code = it3.second.get_optional<string>("Area.Code")) {
 863:                                         if (code.value().substr(0, n!areaCode.substr(0, n)) {
 864:                                             continue;
 865:                                         }
 866:                                         flag = TRUE;
 867:                                     }
 868:                                 }
 869:                                 if (flag) {
 870:                                     break;
 871:                                 }
 872:                             }
 873: //                          cout <<  "N = " << n << endl;
 874: //                          cout <<  "areaCode = " << areaCode << endl;
 875: 
 876:                             //天気予報
 877:                             for (auto it3 : it2.second.get_child("")) {
 878:                                 if (optional<string>code = it3.second.get_optional<string>("Area.Code")) {
 879: 
 880:                                     if (code.value().substr(0, n!areaCode.substr(0, n)) {
 881:                                         continue;
 882:                                     }
 883:                                 }
 884: //                              cout << it3.first << endl;
 885:                                 if (optional<string>type = it3.second.get_optional<string>("Kind.Property.Type")) {
 886:                                     if (_UW(type.value()) == _SW("天気")) {
 887:                                         //天気予報の取得
 888:                                         for (auto it4 : it3.second.get_child("Kind.Property.WeatherPart")) {
 889:                                             i = stoi(it4.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
 890:                                             if (i < 0) {
 891:                                                 continue;
 892:                                             }
 893:                                             this->jmaWeeklyWeather[i].weather = jmaShortWeather(_UW(it4.second.get_optional<string>("").value()));
 894:                                         }
 895:                                         //天気予報用テロップ番号の取得
 896:                                         for (auto it4 : it3.second.get_child("Kind.Property.WeatherCodePart")) {
 897:                                             i = stoi(it4.second.get_optional<string>("<xmlattr>.refID").value()) + dd;
 898:                                             if (i < 0) {
 899:                                                 continue;
 900:                                             }
 901:                                             if (i == 0) {
 902:                                                 this->jmaWeeklyWeather[i].image = jma_telop2url(stoi(it4.second.get_optional<string>("").value()), mode);
 903:                                             } else {
 904:                                                 this->jmaWeeklyWeather[i].image = jma_telop2url(stoi(it4.second.get_optional<string>("").value()), 0);
 905:                                             }
 906:                                         }
 907: 
 908:                                     } else if (_UW(type.value()) == _SW("降水確率")) {
 909:                                         //降水確率の取得
 910:                                         for (auto it4 : it3.second.get_child("Kind.Property.ProbabilityOfPrecipitationPart")) {
 911:                                             i = stoi(it4.second.get_optional<string>("<xmlattr>.refID").value()) + dd + d2;
 912:                                             if (i < 0) {
 913:                                                 continue;
 914:                                             }
 915:                                             rain_table[i] = it4.second.get_optional<string>("").value();
 916:                                         }
 917:                                     }
 918:                                 }
 919:                             }
 920:                         }
 921:                     } else if (_UW(type.value()) == _SW("地点予\報")) {
 922:                         //日時
 923:                         int day0 = 0;
 924:                         for (auto it2 : it.second.get_child("")) {
 925:                             for (auto it3 : it2.second.get_child("")) {
 926:                                 for (auto it4 : it3.second.get_child("")) {
 927:                                     if (optional<string>s2 = it4.second.get_optional<string>("DateTime")) {
 928:                                         string ss = s2.value();
 929:                                         if (regex_search(ss, mt1, re11)) {
 930:                                             //最初の情報は何時か
 931:                                             optional<string>tid = it4.second.get_optional<string>("<xmlattr>.timeId");
 932:                                             i = stoi(tid.value());
 933:                                             gr::date dt2(stoi(mt1[1].str()), stoi(mt1[2].str()), stoi(mt1[3].str()));
 934:                                             if (i == 1) {
 935:                                                 if (dt < dt2) {
 936:                                                     dd = 0;         //明日
 937:                                                 } else if (dt == dt2) {
 938:                                                     dd = (-1);      //今日
 939:                                                 } else {
 940:                                                     dd = (-2);      //明日
 941:                                                 }
 942:                                                 temp_table[i] = 0;
 943:                                                 day0 = stoi(mt1[3]);
 944:                                             } else {
 945:                                                 if (day0 == stoi(mt1[3])) {
 946:                                                     temp_table[i] = 0;
 947:                                                 } else {
 948:                                                     temp_table[i] = 1;
 949:                                                 }
 950:                                                 day0 = stoi(mt1[3]);
 951:                                             }
 952:                                         }
 953:                                     }
 954:                                 }
 955:                             }
 956:                             //最低気温・最高気温
 957:                             i = 1 + dd;
 958:                             for (auto it3 : it2.second.get_child("")) {
 959:                                 if (optional<string>code = it3.second.get_optional<string>("Station.Code")) {
 960: 
 961:                                     if (code.value() !stationCode) {
 962:                                         continue;
 963:                                     }
 964:                                 }
 965:                                 //最高気温・最低気温
 966:                                 for (auto it4 : it3.second.get_child("")) {
 967:                                     for (auto it5 : it4.second.get_child("")) {
 968:                                         if (optional<string>ts = it5.second.get_optional<string>("Type")) {
 969:                                             wstring ws = _UW(ts.value());
 970: //                                          cout << _WS(ws) << endl;
 971:                                             if (regex_search(ws, mt2, re31)) {
 972:                                                 int i2 = stoi(it5.second.get_optional<string>("TemperaturePart.jmx_eb:Temperature.<xmlattr>.refID").value());
 973:                                                 i +temp_table[i2];
 974:                                                 if (i < 0) {
 975:                                                     continue;
 976:                                                 }
 977:                                                 this->jmaWeeklyWeather[i].temp_min = it5.second.get_optional<string>("TemperaturePart.jmx_eb:Temperature").value();
 978:                                             } else if (regex_search(ws, mt2, re32)) {
 979:                                                 int i2 = stoi(it5.second.get_optional<string>("TemperaturePart.jmx_eb:Temperature.<xmlattr>.refID").value());
 980:                                                 i +temp_table[i2];
 981:                                                 if (i < 0) {
 982:                                                     continue;
 983:                                                 }
 984:                                                 this->jmaWeeklyWeather[i].temp_max = it5.second.get_optional<string>("TemperaturePart.jmx_eb:Temperature").value();
 985:                                             }
 986:                                         }
 987:                                     }
 988:                                 }
 989:                             }
 990:                         }
 991:                     }
 992:                 }
 993:             }
 994: 
 995:         //XML解釈エラー
 996:         } catch(ptree_bad_path& e) {
 997:             errmsg = _SW("府県天気予\報情報を取得できません");
 998:             return FALSE;
 999:         }
1000:     //読み込みエラー
1001:     } catch(xml_parser_error& e) {
1002:         errmsg = _SW("府県天気予\報情報を取得できません");
1003:         return FALSE;
1004:     }
1005: 
1006:     //降水確率を天気予報情報へ
1007:     for (int j = 0j < 8j++) {
1008:         if ((dd == (-2)) && (j >4))   break;
1009:         i = floor(j / 4.0);
1010:         if (regex_search(jmaWeeklyWeather[i].rainy, mt1, re41)) {
1011:             this->jmaWeeklyWeather[i].rainy = "";
1012:         }
1013:         this->jmaWeeklyWeather[i].rainy +rain_table[j];
1014:         if (j % 4 !3) {
1015:             this->jmaWeeklyWeather[i].rainy +"/";
1016:         }
1017:     }
1018: 
1019: /** debug
1020:     for (int j = 0; j <= 7; j++) {
1021:         cout << j << ":"
1022:                 << _WS(this->jmaWeeklyWeather[j].stationName) << ":"
1023:                 << this->jmaWeeklyWeather[j].year << "/"
1024:                 << this->jmaWeeklyWeather[j].month << "/"
1025:                 << this->jmaWeeklyWeather[j].day << "("
1026:                 << _WS(this->jmaWeeklyWeather[j].day_of_week) << ") "
1027:                 << _WS(this->jmaWeeklyWeather[j].weather) << " "
1028:                 << this->jmaWeeklyWeather[j].image << " "
1029:                 << this->jmaWeeklyWeather[j].rainy << "% "
1030:                 << this->jmaWeeklyWeather[j].temp_max << "/"
1031:                 << this->jmaWeeklyWeather[j].temp_min << "℃ "
1032:         << endl;
1033:     }
1034: **/
1035: 
1036:     contents.clear();
1037:     contents2.clear();
1038: 
1039:     return TRUE;
1040: }

メソッド jmaGetWeatherForecast は、予報地点コードを引数として、その週間天気予報情報を読み込む。

解説:pahooCacheクラスとデータ構造

1週間分の予報を表示するには、気象庁防災情報XMLから、
  1. Atomフィード(長期フィード:定時)
  2. VPFW50
  3. VPFD51
の3つのXMLファイルを読み込む必要がある。このうちAtomフィード(長期フィード:定時)は約4Mバイトと大きく、毎回、ロードすることは気象庁サイトへ負荷を掛けることになる。そこで、PHPプログラムの場合と同様、一定時間、自サイトにXMLファイルを保持しておきキャッシュ・システムを導入することにする。

  52: //キャッシュ・ディレクトリ
  53: #define CACHEDIR_WEATHER    "pcache\\"
  54: 
  55: //キャッシュ保持時間(デフォルト;分)(0:キャッシュしない)
  56: #define LIFE_CACHE_WEATHER  (2 * 60)

キャッシュ保持時間は定数 LIFE_CACHE_WEATHER に分で指定する。キャッシュ・ファイルを保存するディレクトリは、定数 CACHEDIR_WEATHER で指定する。このディレクトリは、各種パラメータの保存と同様、ユーザーのAppData配下に作成する。

たとえば120分(=2時間)を指定した場合、気象庁防災情報XMLへのアクセスは
 1日24時間÷2=12回
だけ行えばいい。VPFW50やVPFD51のXMLファイルのサイズは約16Kバイト。VPFW50の対象地点が70箇所、VPFD51の対象地点が170箇所であることから、1日あたりのアクセス量の最大値は
 4.0M×12+16K×70×12+16×170×12=141Mバイト
となる。

   1: /** pahooCache.cpp
   2:  * キャッシュ処理に関わるクラス:C++ソース
   3:  *
   4:  * @copyright   (c)studio pahoo
   5:  * @author      パパぱふぅ
   6:  * @動作環境    MinGW C++ + cURL + OpenSSL + Boost C++ Libraries
   7:  * @参考URL     https://www.pahoo.org/e-soul/webtech/cpp01-16-01.shtm
   8:  */
   9: #include <iostream>
  10: #include <fstream>
  11: #include <stdio.h>
  12: #include <stdlib.h>
  13: #include <tchar.h>
  14: #include <time.h>
  15: #include <direct.h>
  16: #include <sys/stat.h>
  17: #include <dirent.h>
  18: #include <sstream>
  19: #include <string>
  20: #include <regex>
  21: #include <locale>
  22: #include <math.h>
  23: #include <boost/property_tree/xml_parser.hpp>
  24: #include <boost/date_time/gregorian/gregorian.hpp>
  25: #include <boost/format.hpp>
  26: #include <boost/algorithm/hex.hpp>
  27: #include <boost/uuid/detail/md5.hpp>
  28: #include "mystrings.h"
  29: #include "pahooCache.hpp"
  30: 
  31: using namespace std;
  32: using namespace boost;
  33: using namespace boost::property_tree;
  34: using namespace boost::uuids::detail;
  35: 
  36: /**
  37:  * コンストラクタ
  38:  * @param   unsigned int life キャッシュ保持時間(分)
  39:  * @param   string dir        キャッシュ・ディレクトリ
  40:  * @param   string ua         UserAgent
  41:  * @return  なし
  42:  */
  43: pahooCache::pahooCache(unsigned int life, string dir, const string ua) {
  44:     lifeCache = life;
  45: 
  46:     //dirの最後にディレクトリ区切り文字がなければ追加
  47:     smatch mt;
  48:     regex re("\\\\$");
  49:     if (regex_search(dir, mt, re) == FALSE) {
  50:         dir = dir + "\\";
  51:     }
  52:     errmsg = L"";
  53:     lifeCache = life;
  54:     dirCache  = dir;
  55:     userAgent = ua;
  56: 
  57:     //キャッシュ・ディレクトリが無ければ作成
  58:     struct stat statBuf;
  59:     if (stat(dirCache.c_str(), &statBuf!0) {
  60:         if (_mkdir(dirCache.c_str()) !0) {
  61:             errmsg = _SW("キャッシュ・ディレクトリ \""+ _SW(dirCache+ _SW("\" の作成に失敗しました");
  62:         }
  63:     }
  64: }

キャッシュ保持時間とキャッシュ・ファイルを保存するディレクトリはコンストラクタで指定する。ディレクトリが無い場合、コンストラクタ内で生成するようにしてある。

 107: /**
 108:  * キャッシュの削除
 109:  * @param   string pat 削除ファイル名(正規表現指定可能)
 110:  * @param   unsigned int life  キャッシュ保持時間(分)
 111:  * @return  unsigned int 削除したファイル数
 112: */
 113: unsigned int pahooCache::delCache(string pat, unsigned int life) {
 114:     int cnt = 0;
 115:     string fullname;
 116:     struct stat st;
 117:     smatch mt;
 118:     regex re(pat);
 119: 
 120:     //現在時刻
 121:     time_t t;
 122:     time(&t);
 123: 
 124:     //ディレクトリ検索
 125:     DIR *dp;
 126:     dp = opendir(dirCache.c_str());
 127:     if (dp == NULL) {
 128:         return 0;
 129:     }
 130:     dirent* entry = readdir(dp);
 131:     while (entry !NULL){
 132:         if (entry !NULL) {
 133:             string fname = (string)entry->d_name;
 134:             //patに一致するファイル
 135:             if (regex_search(fname, mt, re)) {
 136:                 fullname = dirCache + fname;
 137:                 if (stat(fullname.c_str(), &st) == 0) {
 138:                     //保持時間を過ぎたキャッシュ・ファイルは削除
 139:                     if ((long unsigned int)t - (long unsigned int)st.st_ctime > (life * 60)) {
 140:                         unlink(fullname.c_str());
 141:                         cnt++;
 142:                     }
 143:                 }
 144:             }
 145:         }
 146:         entry = readdir(dp);
 147:     }
 148:     closedir(dp);
 149: 
 150:     return cnt;
 151: }

 153: /**
 154:  * cURLによるコンテンツ取得
 155:  * @param   string url URL
 156:  * @param   string* contents コンテンツを格納
 157:  * @return  bool TRUE:読み込み成功/FALSE:失敗
 158: */
 159: bool pahooCache::cLoad(string url, string* contents) {
 160:     bool res = readWebContents(url, (const string)userAgent, contents);
 161:     if (res == FALSE) {
 162:         errmsg  = _SW("\""+ _SW(url+ _SW("\" が取得できません");
 163:         return FALSE;
 164:     }
 165:     return TRUE;
 166: }

 168: /**
 169:  * MD5ハッシュ値を返す
 170:  * @param   string str 文字列
 171:  * @return  string MD5ハッシュ値
 172: */
 173: string pahooCache::md5(string str) {
 174:     boost::uuids::detail::md5 hash;
 175:     md5::digest_type digest;
 176:     hash.process_bytes(str.data(), str.size());
 177:     hash.get_digest(digest);
 178: 
 179:     const auto intDigest = reinterpret_cast<const int *>(&digest);
 180:     std::string result;
 181:     boost::algorithm::hex(intDigest, intDigest + (sizeof(md5::digest_type) / sizeof(int)), std::back_inserter(result));
 182: 
 183:     return result;
 184: }

 186: /**
 187:  * ネットからコンテンツを読み込む
 188:  * @param   string url   URL
 189:  * @param   string* contents コンテンツを格納
 190:  * @return  bool TRUE:読み込み成功/FALSE:失敗
 191: */
 192: bool pahooCache::forceLoad(string url, string* contents) {
 193:     //キャッシュするファイル名
 194:     string fname = this->md5(url);
 195:     string fullname = dirCache + fname;
 196: 
 197:     bool ret = this->cLoad(url, contents);
 198:     ofstream fout;
 199:     fout.open(fullname, ios::out);
 200:     fout << *contents;
 201: 
 202:     if (ret == FALSE) {
 203:         errmsg = _SW("キャッシュファイル \""+ _SW(fullname+ _SW("\" の書き込み失敗しました");
 204:         return FALSE;
 205:     }
 206:     debug +url + "\n";
 207: 
 208:     return TRUE;
 209: }

 211: /**
 212:  * コンテンツを読み込む
 213:  * @param   string url   URL
 214:  * @param   string* contents コンテンツを格納
 215:  * @return  bool TRUE:読み込み成功/FALSE:失敗
 216: */
 217: bool pahooCache::load(string url, string* contents) {
 218:     bool res = FALSE;
 219:     string fname, fullname;
 220:     string buff;
 221:     ifstream fin;
 222: 
 223:     //キャッシュ有効
 224:     if (lifeCache > 0) {
 225:         this->delCache("[0-9a-f]+", lifeCache);     //古いキャッシュを削除
 226:         fname = this->md5(url);                     //キャッシュするファイル名
 227:         fullname = dirCache + fname;
 228:         //キャッシュが存在する
 229:         *contents = "";
 230:         fin.open(fullname, ios::binary);
 231:         if (fin) {
 232: //          cout << "from file: " << fname << endl;
 233:             getline(fin, *contents, '\0');
 234:             if (contents->length() <0) {
 235:                 errmsg = _SW("キャッシュファイル \""+ _SW(fullname+ _SW("\" の読み込みに失敗しました");
 236: //              cout << _WS(errmsg) << endl;
 237:                 return FALSE;
 238:             }
 239:             debug +fname + "\n";
 240:             res = TRUE;
 241:         //ネットから取得
 242:         } else {
 243: //          cout << "from URL: " << url << endl;
 244:             res = this->forceLoad(url, contents);
 245:         }
 246: 
 247:     //キャッシュ無効
 248:     } else {
 249: //      cout << "uncache from URL: " << url << endl;
 250:         res = this->forceLoad(url, contents);
 251:     }
 252: 
 253:     return res;
 254: }

コンテンツを読み込むメソッドは load である。
キャッシュが有効の時は、まず、delCache メソッドを使って古いキャッシュを削除する。stat関数を使ってファイルのタイムスタンプを取得し、それを現在日時と比較することで削除するかどうかを判定している。
ッシュへ書き込む。

キャッシュ・ファイル名は、URLからMD5ハッシュを使って生成する。
キャッシュが存在すれば、getline関数を使って読み込む。
無ければ、forceLoad メソッドを使ってネットから読み込み、キャッシュへ書き込む。ネットからの読み込みは cLoad メソッドで、readWebContents関数を使って読み込んでいる。
その他の関数、マップ描画、ジオコーディング処理、ブラウザ・コントロール、ヘルプファイルやインストーラー作成方法については、これまでの連載で説明してきたとおりである。

参考サイト

(この項おわり)
header