C++ で撮影場所をマッピング

(1/1)
>C++で撮影場所をマッピング
スマホやデジカメ等で撮影したJPEG画像ファイルを読み込み、撮影場所を地図上にマッピングしたり、露出やレンズ情報などを記録したExif情報を一覧表示するアプリケーションをC++を使って作る。ユーザーがAPIキーを取得することでGoogleマップも利用できる。「PHPで撮影場所をマッピング」で作ったPHPプログラムをC++に移植したものである。
注意:2021年(令和3年)11月現在、GoogleマップAPIがInternetExplorer11のサポートを終了したため、Googleマップは利用できなくなった。

(2024年3月23日)使用ライブラリ更新
(2023年9月20日)使用ライブラリ更新
(2023年7月2日)使用ライブラリ更新

目次

サンプル・プログラム

圧縮ファイルの内容
photomapwin.msiインストーラ
bin/photomapwin.exe実行プログラム本体
bin/cwebpage.dll
bin/libcurl.dll
実行時に必要になるDLL
bin/etc/help.chmヘルプ・ファイル
sour/photomapwin.cppソース・プログラム
sour/resource.hリソース・ヘッダ
sour/resource.rcリソース・ファイル
sour/application.icoアプリケーション・アイコン
sour/mystrings.cpp汎用文字列処理関数など(ソース)
sour/mystrings.h汎用文字列処理関数など(ヘッダ)
sour/pahooGeocode.cpp住所・緯度・経度に関わるクラス(ソース)
sour/pahooGeocode.hpp住所・緯度・経度に関わるクラス(ヘッダ)
sour/apikey.cppAPIキーの管理(ソース)
sour/apikey.hppAPIキーの管理(ヘッダ)
sour/TinyEXIF.hTinyEXIFソース・プログラム
sour/TinyEXIF.cppTinyEXIFソース・プログラム
sour/tinyxml2.htinyxml2ソース・プログラム
sour/tinyxml2.cpptinyxml2ソース・プログラム
photomapwin.cpp 更新履歴
バージョン 更新日 内容
1.5.5 2024/03/23 使用ライブラリ更新
1.5.4 2023/09/20 使用ライブラリ更新
1.5.3 2023/07/02 使用ライブラリ更新
1.5.2 2023/03/18 使用ライブラリ更新
1.5.1 2022/11/20 使用ライブラリ更新
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変更
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++ライブラリが必要になる。導入方法等については、「C++ 開発環境の準備」をご覧いただきたい。
また、地図表示にWebブラウザ・コントロールを利用するため、オープンソースの cwebpage.dll および webbrowser.h が必要になる。cwebpage.dll は [https://www.codeproject.com/Articles/3365/Embed-an-HTML-control-in-your-own-window-using-pla] から、webbrowser.hDigital Point から、それぞれダウンロードできる。
Exif情報を解釈するのに、Seacave氏による TinyEXIF と、このモジュールが呼び出す inyxml2 を、https://github.com/cdcseacave/TinyEXIFhttps://github.com/leethomason/tinyxml2 から、それぞれダウンロードする。

リソースの準備

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

Eclipse に戻り、ソース・プログラム "photomapwin.cpp" を追加する。
リンカー・フラグを -Wl,--enable-stdcall-fixup -mwindows -lgdiplus -lole32 -static -lstdc++ -lgcc -lwinpthread -lcurl -lssl "(任意のパス)\libcurl.dll" "C:\Windows\system32\GdiPlus.dll" "(任意のパス)\cwebpage.dll" に設定する。

解説:定数など

  41: // 定数など ==================================================================
  42: #define APPNAME     "photomapwin"           //アプリケーション名
  43: #define APPNAMEJP   "撮影場所をマッピング"  //アプリケーション名(日本語)
  44: #define APPVERSION  "1.5.5"                 //バージョン
  45: #define APPYEAR     "2020-2024"             //作成年
  46: #define REFERENCE   "https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-13-01.shtm"   // 参考サイト
  47: 
  48: //char*バッファサイズ
  49: #define SIZE_BUFF       512
  50: 
  51: //ListViewItemの最大文字長:変更不可
  52: #define MAX_LISTVIEWITEM    258
  53: 
  54: //AppDataのフルパス
  55: TCHAR MyPath[MAX_PATH];
  56: #define MYAPPDATA   "photomapwin"
  57: 
  58: //ヘルプ・ファイル
  59: #define HELPFILE    ".\\etc\\help.chm"
  60: 
  61: //デフォルト保存ファイル名
  62: #define SAVEFILE    "photomapwin.csv"
  63: 
  64: //エラー・メッセージ格納用
  65: string ErrorMessage;
  66: 
  67: //現在のインターフェイス
  68: HINSTANCE hInst;
  69: 
  70: //アプリケーション・ウィンドウ
  71: HWND hParent;
  72: 
  73: //アプリケーション・ウィンドウ位置
  74: unsigned hParent_X, hParent_Y;
  75: 
  76: //ブラウザ・コントロール
  77: WebBrowser wBrowser;
  78: 
  79: //pahooGeocodeオブジェクト
  80: pahooGeocode* pGC;
  81: 
  82: //マップID
  83: #define MAP_ID          "map_id"
  84: //地図の大きさ
  85: #define MAP_WIDTH       630     //地図の幅(ピクセル)
  86: #define MAP_HEIGHT      325     //地図の高さ(ピクセル)
  87: //経度・緯度(初期値)
  88: #define DEF_LONGITUDE   139.766667
  89: double Longitude = DEF_LONGITUDE;
  90: #define DEF_LATITUDE    35.681111
  91: double Latitude  = DEF_LATITUDE;
  92: //地図拡大率(初期値)
  93: #define DEF_ZOOM    13
  94: int Zoom = DEF_ZOOM;
  95: //地図の種類(初期値)
  96: #define DEF_MAPTYPE     "GSISTD"
  97: string Maptype = DEF_MAPTYPE;

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

解説:データ構造

 121: //Exif格納クラス
 122: #define SIZE_EXIFS      100     //最大格納数
 123: class _Exif {
 124: public:
 125:     wstring label = L"";        //データ項目
 126:     wstring data  = L"";        //データ本体
 127: Exif[SIZE_EXIFS];

取得したExif情報はクラス配列 Exif に格納する。

解説:フルパスからファイル名を取り出す

 343: /**
 344:  * ファイル名(フルパス)からファイル名(拡張子を除く)を取り出す
 345:  * @param   wstring path ファイル名(フルパス)
 346:  * @return  wstring ファイル名(拡張子を除く)
 347: */
 348: wstring wgetBasename(wstring path) {
 349:     wstring basename = L"";
 350: 
 351:     //最後のディレクトリ識別子を探す
 352:     size_t pos = path.rfind(_SW("\\"));
 353:     if (pos == string::npos) {
 354:         return basename;
 355:     }
 356:     //ファイル名+拡張子
 357:     wstring fname = path.substr(pos + 1);
 358: 
 359:     //拡張子を除く
 360:     pos = fname.rfind(_SW("."));
 361:     if (pos == string::npos) {
 362:         basename = fname;
 363:     } else {
 364:         basename = fname.substr(0, pos);
 365:     }
 366:     return basename;
 367: }

Exif情報をCSVファイルに保存する際、ファイル名を読み込んだ画像ファイル名に合わせようと考え、この関数を用意した。
Boost C++ライブラリにも同様関数があるが、うまくコンパイルできなかったので、自力でコーディングした。シフトJIS文字列では、ディレクトリ識別子 ¥\0x5C)を含む全角文字があるため、ファイル名はワイド文字列に変換して処理する。

解説:画像ファイルを読み込む

 503: /**
 504:  * 画像ファイルの表示
 505:  * @param   HWND  hWnd 表示するウィンドウ・ハンドル
 506:  * @param   const char *fname 画像ファイル名
 507:  * @return  bool TRUE:表示成功/FALSE:失敗
 508: */
 509: bool onPaint(HWND hWnd, const char *fname) {
 510:     HDC hdc;
 511:     PAINTSTRUCT ps;
 512:     static HDC hdcBMP;
 513:     static int ix,iy;
 514:     int sx, sy;
 515:     RECT rect;
 516:     bool ret = TRUE;
 517: 
 518:     //画像ファイル読み込み
 519:     hdc = GetDC(hWnd);
 520:     if(loadImageOnMemory((TCHAR*)fname, &hdcBMP, hdc, &ix, &iy) == FALSE) {
 521:         ErrorMessage = "画像ファイル " + (string)fname + " の読み込みに失敗";
 522:         return FALSE;
 523:     }
 524:     InvalidateRect(hWnd, 0, TRUE);
 525:     hdc = BeginPaint(hWnd, &ps);
 526:     GetClientRect(hWnd, &rect);
 527: 
 528:     //領域クリア
 529:     HPEN   hNewPen   = (HPEN)CreatePen(PS_INSIDEFRAME, 4, RGB(255, 255, 255));
 530:     HPEN   hOldPen   = (HPEN)SelectObject(hdc,hNewPen);
 531:     HBRUSH hNewBrush = (HBRUSH)CreateSolidBrush(RGB(255, 255, 255));
 532:     HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, hNewBrush);
 533:     Rectangle(hdc, 0, 0, rect.right, rect.bottom);
 534:     DeleteObject(SelectObject(hdc, hOldBrush));
 535:     DeleteObject(SelectObject(hdc, hOldPen));
 536: 
 537:     //縮小表示
 538:     reSizeImage(&sx, &sy, ix, iy, (rect.right - rect.left), (rect.bottom - rect.top));
 539:     SetStretchBltMode(hdc, HALFTONE);
 540:     StretchBlt(hdc, 0, 0, sx, sy, hdcBMP, 0, 0, ix, iy, SRCCOPY);
 541:     EndPaint(hWnd, &ps);
 542:     ReleaseDC(hWnd, hdc);
 543: 
 544:     return ret;
 545: }

 424: /**
 425:  * 画像ファイルをGDI+を使って読み込む
 426:  * @param   TCHAR* szFile   画像ファイル名
 427:  * @param   HDC* hdcBMP     オフスクリーン用デバイスコンテキストを格納
 428:  * @param   HDC  hdc        デバイスコンテキスト
 429:  * @param   int* ix, iy     読み込んだ画像の横・縦ピクセル数
 430:  * @return  なし
 431: */
 432: bool loadImageOnMemory(TCHAR* szFile, HDC* hdcBMP, HDC hdc, int* ix, int* iy) {
 433:     Image* imageP;
 434:     WCHAR wTitle[MAX_PATH];
 435:     BITMAPINFOHEADER bmih;
 436:     HBITMAP hBitmap;
 437:     BYTE *pBits;
 438: 
 439:     //読み込むファイル名
 440:     MultiByteToWideChar(932, 0, szFile, -1, wTitle, sizeof(wTitle) / sizeof(TCHAR));
 441:     imageP = Bitmap::FromFile(wTitle);
 442:     if (imageP == 0) {
 443:         *ix = 0;
 444:         *iy = 0;
 445:         *hdcBMP = 0;
 446:         return FALSE;
 447:     }
 448: 
 449:     bmih.biSize = sizeof(bmih);
 450:     bmih.biWidth = *ix = imageP->GetWidth();
 451:     bmih.biHeight = *iy = imageP->GetHeight();
 452:     bmih.biPlanes = 1;
 453:     bmih.biBitCount = 32;
 454:     bmih.biCompression = BI_RGB;
 455:     bmih.biSizeImage = 0;
 456:     bmih.biXPelsPerMeter = 0;
 457:     bmih.biYPelsPerMeter = 0;
 458:     bmih.biClrUsed = 0;
 459:     bmih.biClrImportant = 0;
 460:     hBitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, 0, (void**)&pBits, NULL, 0);
 461:     if (pBits == NULL) {
 462:         ErrorMessage = "メモリ不足";
 463:         DeleteObject(hBitmap);
 464:         delete imageP;
 465:         *ix = 0;
 466:         *iy = 0;
 467:         return FALSE;
 468:     }
 469:     *hdcBMP = CreateCompatibleDC(hdc);
 470: //  HOldBitmap = (HBITMAP)SelectObject(*hdcBMP, hBitmap);
 471:     SelectObject(*hdcBMP, hBitmap);
 472:     Graphics MyGraphics(*hdcBMP);
 473:     MyGraphics.DrawImage(imageP, 0, 0, imageP->GetWidth(), imageP->GetHeight());
 474:     delete imageP;
 475: 
 476:     return TRUE;
 477: }

 479: /**
 480:  * 縦横比を保った画像サイズ計算
 481:  * @param   int* sx, sy  変換後画像サイズを格納
 482:  * @param   int  px, py  元画像の縦横サイズ
 483:  * @param   int  dx, dy  変換後領域の縦横サイズ
 484:  * @return  なし
 485: */
 486: void reSizeImage(int* sx, int* sy, int px, int py, int dx, int dy) {
 487:     double pxy = double(px) / double(py);   //元画像の横縦比
 488:     double dxy = double(dx) / double(dy);   //変換後の横縦比
 489:     int dx2, dy2;
 490: 
 491:     //横を基準に縦を縮小
 492:     if(pxy > dxy) {
 493:         dx2 = dx;
 494:         dy2 = (int)double(dx / pxy);
 495:     } else {
 496:         dy2 = dy;
 497:         dx2 = (int)double(dy * pxy);
 498:     }
 499:     *sx = dx2;
 500:     *sy = dy2;
 501: }

C++でTeX画像を作成」で GDI+ を使った画像ファイル読み込みと表示について紹介したが、今回は、読み込んだ画像をサムネイル領域のサイズに合わせて縮小する必要がある。

まず、GDI+ を使って画像データをメモリに読み込むのがユーザー関数 loadImageOnMemory である。
読み込んだ画像の縦横のピクセル数を取り出し、ユーザー関数 reSizeImage を使って、サムネイル領域のサイズに縦横比が合うように縮小する。メモリ上の画像データを縮小してサムネイル領域に表示するのは StretchBlt 関数である。
縮小したら、

解説:Exif情報を取得する

 548: /**
 549:  * 画像ファイルを開いてExif情報を取得する(実作業)
 550:  * @param   const char *fname 保存ファイル名
 551:  * @return  bool TRUE:取得成功/FALSE:失敗
 552: */
 553: bool loadExif(const char *fname) {
 554:     double f;
 555:     string ss;
 556:     char buff[SIZE_BUFF + 1];
 557:     bool ret = TRUE;
 558: 
 559:     //画像ファイルのオープン
 560:     EXIFStreamFile stream(fname);
 561:     if (! stream.IsValid()) {
 562:         ErrorMessage = "画像ファイル " + (string)fname + " の読み込みに失敗";
 563:         return FALSE;
 564:     }
 565:     //Exif情報の取得
 566:     TinyEXIF::EXIFInfo imageEXIF(stream);
 567: 
 568:     //Exif情報を配列Exifへ代入
 569:     //初期化
 570:     for (int i = 0i < SIZE_EXIFSi++) {
 571:         Exif[i].label = L"";
 572:         Exif[i].data  = L"";
 573:     }
 574:     //代入
 575:     int i = 0;
 576:     //位置情報
 577:     if (imageEXIF.GeoLocation.hasLatLon()) {
 578:         Exif[i].label = _SW("緯度(度)");
 579:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.Latitude));
 580:         Latitude = imageEXIF.GeoLocation.Latitude;
 581:         i++;
 582:         Exif[i].label = _SW("経度(度)");
 583:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.Longitude));
 584:         Longitude = imageEXIF.GeoLocation.Longitude;
 585:         i++;
 586:     } else {
 587:         ErrorMessage = "画像ファイル " + (string)fname + " に位置情報が存在しない";
 588:         ret = FALSE;
 589:     }
 590:     if (imageEXIF.GeoLocation.hasAltitude()) {
 591:         Exif[i].label = _SW("基準高度");
 592:         switch (imageEXIF.GeoLocation.AltitudeRef) {
 593:             case 0:
 594:                 Exif[i].data = _SW("海抜");
 595:                 break;
 596:             case 1:
 597:                 Exif[i].data = _SW("海面下");
 598:                 break;
 599:             default:
 600:                 Exif[i].data = _SW("不明");
 601:                 break;
 602:         }
 603:         i++;
 604:         Exif[i].label = _SW("高度(メートル)");
 605:         snprintf(buff, SIZE_BUFF, "%.1f", imageEXIF.GeoLocation.Altitude);
 606:         Exif[i].data  = _SW((string)buff);
 607:         i++;
 608:     }
 609:     if (! imageEXIF.GeoLocation.GPSMapDatum.empty()) {
 610:         Exif[i].label = _SW("測地系");
 611:         Exif[i].data  = _SW(imageEXIF.GeoLocation.GPSMapDatum);
 612:         i++;
 613:     }
 614:     //画像情報
 615:     if (imageEXIF.ImageWidth || imageEXIF.ImageHeight) {
 616:         Exif[i].label = _SW("画像の幅(ピクセル)");
 617:         Exif[i].data  = _SW(to_string(imageEXIF.ImageWidth));
 618:         i++;
 619:         Exif[i].label = _SW("画像の高さ(ピクセル)");
 620:         Exif[i].data  = _SW(to_string(imageEXIF.ImageHeight));
 621:         i++;
 622:     }
 623:     if (imageEXIF.RelatedImageWidth || imageEXIF.RelatedImageHeight) {
 624:         Exif[i].label = _SW("画像の幅(ピクセル)");
 625:         Exif[i].data  = _SW(to_string(imageEXIF.RelatedImageWidth));
 626:         i++;
 627:         Exif[i].label = _SW("画像の高さ(ピクセル)");
 628:         Exif[i].data  = _SW(to_string(imageEXIF.RelatedImageHeight));
 629:         i++;
 630:     }
 631:     if (! imageEXIF.ImageDescription.empty()) {
 632:         Exif[i].label = _SW("画像タイトル");
 633:         Exif[i].data  = _SW(imageEXIF.ImageDescription);
 634:         i++;
 635:     }
 636:     if (! imageEXIF.Make.empty() || ! imageEXIF.Model.empty()) {
 637:         Exif[i].label = _SW("カメラのメーカー名");
 638:         Exif[i].data  = _SW(imageEXIF.Make);
 639:         i++;
 640:         Exif[i].label = _SW("カメラのモデル名");
 641:         Exif[i].data  = _SW(imageEXIF.Model);
 642:         i++;
 643:     }
 644:     if (! imageEXIF.SerialNumber.empty()) {
 645:         Exif[i].label = _SW("シリアル番号");
 646:         Exif[i].data  = _SW(imageEXIF.SerialNumber);
 647:         i++;
 648:     }
 649:     if (imageEXIF.Orientation) {
 650:         Exif[i].label = _SW("画像方向");
 651:         switch (imageEXIF.Orientation) {
 652:             case 1:
 653:                 Exif[i].data  = _SW("左上");
 654:                 break;
 655:             case 3:
 656:                 Exif[i].data  = _SW("右下");
 657:                 break;
 658:             case 6:
 659:                 Exif[i].data  = _SW("右上");
 660:                 break;
 661:             case 8:
 662:                 Exif[i].data  = _SW("左下");
 663:                 break;
 664:             default:
 665:                 Exif[i].data  = _SW("不明");
 666:                 break;
 667:         }
 668:         i++;
 669:     }
 670:     if (imageEXIF.XResolution || imageEXIF.YResolution || imageEXIF.ResolutionUnit) {
 671:         ss = "";
 672:         switch (imageEXIF.ResolutionUnit) {
 673:             case 2:
 674:                 ss = "(インチ)";
 675:                 break;
 676:             case 3:
 677:                 ss = "(センチ)";
 678:                 break;
 679:             default:
 680:                 ss = "";
 681:                 break;
 682:         }
 683:         Exif[i].label = _SW("幅の解像度" + ss);
 684:         Exif[i].data  = _SW(to_string((int)imageEXIF.XResolution));
 685:         i++;
 686:         Exif[i].label = _SW("高さの解像度" + ss);
 687:         Exif[i].data  = _SW(to_string((int)imageEXIF.YResolution));
 688:         i++;
 689:     }
 690:     if (imageEXIF.BitsPerSample) {
 691:         Exif[i].label = _SW("画像のビットの深さ");
 692:         Exif[i].data  = _SW(to_string(imageEXIF.BitsPerSample));
 693:         i++;
 694:     }
 695:     if (! imageEXIF.Software.empty()) {
 696:         Exif[i].label = _SW("ソ\フトウェア");
 697:         Exif[i].data  = _SW(imageEXIF.Software);
 698:         i++;
 699:     }
 700:     if (! imageEXIF.DateTime.empty()) {
 701:         Exif[i].label = _SW("撮影日時");
 702:         Exif[i].data  = _SW(imageEXIF.DateTime);
 703:         i++;
 704:     }
 705:     if (! imageEXIF.DateTimeOriginal.empty()) {
 706:         Exif[i].label = _SW("撮影日時(オリジナル)");
 707:         Exif[i].data  = _SW(imageEXIF.DateTimeOriginal);
 708:         i++;
 709:     }
 710:     if (! imageEXIF.DateTimeDigitized.empty()) {
 711:         Exif[i].label = _SW("撮影日時(デジタイズ)");
 712:         Exif[i].data  = _SW(imageEXIF.DateTimeDigitized);
 713:         i++;
 714:     }
 715:     if (! imageEXIF.SubSecTimeOriginal.empty()) {
 716:         Exif[i].label = _SW("撮影日時(サブセック)");
 717:         Exif[i].data  = _SW(imageEXIF.SubSecTimeOriginal);
 718:         i++;
 719:     }
 720:     if (! imageEXIF.Copyright.empty()) {
 721:         Exif[i].label = _SW("著作権者");
 722:         Exif[i].data  = _SW(imageEXIF.Copyright);
 723:         i++;
 724:     }
 725:     Exif[i].label = _SW("露光制御");
 726:     switch (imageEXIF.ExposureProgram) {
 727:         case 1:
 728:             Exif[i].data  = _SW("マニュアル設定");
 729:             break;
 730:         case 2:
 731:             Exif[i].data  = _SW("通常のプログラムAE");
 732:             break;
 733:         case 3:
 734:             Exif[i].data  = _SW("絞り優先");
 735:             break;
 736:         case 4:
 737:             Exif[i].data  = _SW("シャッター速度優先");
 738:             break;
 739:         case 5:
 740:             Exif[i].data  = _SW("低速プログラム");
 741:             break;
 742:         case 6:
 743:             Exif[i].data  = _SW("高速プログラム");
 744:             break;
 745:         case 7:
 746:             Exif[i].data  = _SW("ポートレートモード");
 747:             break;
 748:         case 8:
 749:             Exif[i].data  = _SW("風景モード");
 750:             break;
 751:         default:
 752:             Exif[i].data  = _SW("不明");
 753:             break;
 754:     }
 755:     i++;
 756:     Exif[i].label = _SW("ISOスピードレート");
 757:     Exif[i].data  = _SW(to_string(imageEXIF.ISOSpeedRatings));
 758:     i++;
 759:     Exif[i].label = _SW("シャッター速度(秒)");
 760:     f = 1.0 / imageEXIF.ShutterSpeedValue;
 761:     Exif[i].data  = _SW("1/" + to_string((int)f));
 762:     i++;
 763:     Exif[i].label = _SW("露出時間");
 764:     f = 1.0 / imageEXIF.ExposureTime;
 765:     Exif[i].data  = _SW("1/" + to_string((int)f));
 766:     i++;
 767:     Exif[i].label = _SW("絞り値");
 768:     Exif[i].data  = _SW(to_string(imageEXIF.ApertureValue));
 769:     i++;
 770:     Exif[i].label = _SW("F値");
 771:     Exif[i].data  = _SW(to_string(imageEXIF.FNumber));
 772:     i++;
 773:     Exif[i].label = _SW("レンズ焦点距離");
 774:     Exif[i].data  = _SW(to_string((int)imageEXIF.FocalLength));
 775:     i++;
 776:     Exif[i].label = _SW("輝度値");
 777:     Exif[i].data  = _SW(to_string(imageEXIF.BrightnessValue));
 778:     i++;
 779:     Exif[i].label = _SW("露出補正値");
 780:     Exif[i].data  = _SW(to_string(imageEXIF.ExposureBiasValue));
 781:     i++;
 782:     Exif[i].label = _SW("被写体距離");
 783:     Exif[i].data  = _SW(to_string(imageEXIF.SubjectDistance));
 784:     i++;
 785:     Exif[i].label = _SW("フラッシュ");
 786:     switch (imageEXIF.Flash) {
 787:         case 0:
 788:             Exif[i].data  = _SW("非発光");
 789:             break;
 790:         case 1:
 791:             Exif[i].data  = _SW("発光");
 792:             break;
 793:         case 5:
 794:             Exif[i].data  = _SW("発光したが反射光検出できず");
 795:             break;
 796:         case 6:
 797:             Exif[i].data  = _SW("発光して反射光検出");
 798:             break;
 799:         default:
 800:             Exif[i].data  = _SW("不明");
 801:             break;
 802:     }
 803:     i++;
 804:     Exif[i].label = _SW("測光モード");
 805:     switch (imageEXIF.MeteringMode) {
 806:         case 0:
 807:             Exif[i].data  = _SW("平均測光");
 808:             break;
 809:         case 1:
 810:             Exif[i].data  = _SW("中央重点");
 811:             break;
 812:         case 2:
 813:             Exif[i].data  = _SW("スポット");
 814:             break;
 815:         case 4:
 816:             Exif[i].data  = _SW("多点スポット");
 817:             break;
 818:         case 5:
 819:             Exif[i].data  = _SW("マルチセグメント");
 820:             break;
 821:         case 6:
 822:             Exif[i].data  = _SW("部分測光");
 823:             break;
 824:         default:
 825:             Exif[i].data  = _SW("不明");
 826:             break;
 827:     }
 828:     i++;
 829:     Exif[i].label = _SW("光源");
 830:     switch (imageEXIF.LightSource) {
 831:         case 1:
 832:             Exif[i].data  = _SW("昼光");
 833:             break;
 834:         case 2:
 835:             Exif[i].data  = _SW("蛍光燈");
 836:             break;
 837:         case 3:
 838:             Exif[i].data  = _SW("白熱電球");
 839:             break;
 840:         case 4:
 841:             Exif[i].data  = _SW("フラッシュ");
 842:             break;
 843:         case 9:
 844:             Exif[i].data  = _SW("快晴");
 845:             break;
 846:         case 10:
 847:             Exif[i].data  = _SW("曇天");
 848:             break;
 849:         case 11:
 850:             Exif[i].data  = _SW("日陰");
 851:             break;
 852:         case 12:
 853:             Exif[i].data  = _SW("昼光色");
 854:             break;
 855:         case 13:
 856:             Exif[i].data  = _SW("昼白色");
 857:             break;
 858:         case 14:
 859:             Exif[i].data  = _SW("蛍光色");
 860:             break;
 861:         case 15:
 862:             Exif[i].data  = _SW("白色");
 863:             break;
 864:         case 17:
 865:             Exif[i].data  = _SW("標準ライトA");
 866:             break;
 867:         case 18:
 868:             Exif[i].data  = _SW("標準ライトB");
 869:             break;
 870:         case 19:
 871:             Exif[i].data  = _SW("標準ライトC");
 872:             break;
 873:         case 20:
 874:             Exif[i].data  = _SW("D55");
 875:             break;
 876:         case 21:
 877:             Exif[i].data  = _SW("D65");
 878:             break;
 879:         case 22:
 880:             Exif[i].data  = _SW("D75");
 881:             break;
 882:         case 23:
 883:             Exif[i].data  = _SW("D50");
 884:             break;
 885:         case 24:
 886:             Exif[i].data  = _SW("ISO白熱電球");
 887:             break;
 888:         default:
 889:             Exif[i].data  = _SW("不明");
 890:             break;
 891:     }
 892:     i++;
 893:     if (imageEXIF.Calibration.FocalLength !0) {
 894:         Exif[i].label = _SW("(ピクセル)");
 895:         Exif[i].data  = _SW(to_string(imageEXIF.Calibration.FocalLength));
 896:         i++;
 897:     }
 898:     if (imageEXIF.Calibration.OpticalCenterX !0) {
 899:         Exif[i].label = _SW("(ピクセル)");
 900:         Exif[i].data  = _SW(to_string(imageEXIF.Calibration.OpticalCenterX));
 901:         i++;
 902:     }
 903:     if (imageEXIF.Calibration.OpticalCenterY !0) {
 904:         Exif[i].label = _SW("(ピクセル)");
 905:         Exif[i].data  = _SW(to_string(imageEXIF.Calibration.OpticalCenterY));
 906:         i++;
 907:     }
 908:     //レンズ情報
 909:     Exif[i].label = _SW("最小絞り値");
 910:     Exif[i].data  = _SW(to_string((int)imageEXIF.LensInfo.FStopMin));
 911:     i++;
 912:     Exif[i].label = _SW("最大絞り値");
 913:     Exif[i].data  = _SW(to_string((int)imageEXIF.LensInfo.FStopMax));
 914:     i++;
 915:     Exif[i].label = _SW("広角側(ミリ)");
 916:     Exif[i].data  = _SW(to_string((int)imageEXIF.LensInfo.FocalLengthMin));
 917:     i++;
 918:     Exif[i].label = _SW("望遠側(ミリ)");
 919:     Exif[i].data  = _SW(to_string((int)imageEXIF.LensInfo.FocalLengthMax));
 920:     i++;
 921:     Exif[i].label = _SW("デジタルズーム倍率");
 922:     Exif[i].data  = _SW(to_string((int)imageEXIF.LensInfo.DigitalZoomRatio));
 923:     i++;
 924:     Exif[i].label = _SW("焦点距離(35ミリ判相当");
 925:     Exif[i].data  = _SW(to_string((int)imageEXIF.LensInfo.FocalLengthIn35mm));
 926:     i++;
 927:     ss = "";
 928:     switch (imageEXIF.LensInfo.FocalPlaneResolutionUnit) {
 929:         case 2:
 930:             ss = "(インチ)";
 931:             break;
 932:         case 3:
 933:             ss = "(センチ)";
 934:             break;
 935:         default:
 936:             ss = "";
 937:             break;
 938:     }
 939:     Exif[i].label = _SW("焦点面の幅の解像度" + ss);
 940:     Exif[i].data  = _SW(to_string(imageEXIF.LensInfo.FocalPlaneXResolution));
 941:     i++;
 942:     Exif[i].label = _SW("焦点面の高さの解像度" + ss);
 943:     Exif[i].data  = _SW(to_string(imageEXIF.LensInfo.FocalPlaneYResolution));
 944:     i++;
 945:     if (! imageEXIF.LensInfo.Make.empty() || ! imageEXIF.LensInfo.Model.empty()) {
 946:         Exif[i].label = _SW("レンズのメーカー名");
 947:         Exif[i].data  = _SW(imageEXIF.LensInfo.Make);
 948:         i++;
 949:         Exif[i].label = _SW("レンズのモデル名");
 950:         Exif[i].data  = _SW(imageEXIF.LensInfo.Model);
 951:         i++;
 952:     }
 953:     //位置情報
 954:     if (imageEXIF.GeoLocation.hasRelativeAltitude()) {
 955:         Exif[i].label = _SW("相対高度(メートル)");
 956:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.RelativeAltitude));
 957:         i++;
 958:     }
 959:     if (imageEXIF.GeoLocation.hasOrientation()) {
 960:         Exif[i].label = _SW("ロール角");
 961:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.RollDegree));
 962:         i++;
 963:         Exif[i].label = _SW("ピッチ角");
 964:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.PitchDegree));
 965:         i++;
 966:         Exif[i].label = _SW("ヨー角");
 967:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.YawDegree));
 968:         i++;
 969:     }
 970:     if (imageEXIF.GeoLocation.hasSpeed()) {
 971:         Exif[i].label = _SW("飛行速度(X方向;メートル/秒)");
 972:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.SpeedX));
 973:         i++;
 974:         Exif[i].label = _SW("飛行速度(Y方向;メートル/秒)");
 975:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.SpeedY));
 976:         i++;
 977:         Exif[i].label = _SW("飛行速度(Z方向;メートル/秒)");
 978:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.SpeedZ));
 979:         i++;
 980:     }
 981:     if (imageEXIF.GeoLocation.AccuracyXY > 0 || imageEXIF.GeoLocation.AccuracyZ > 0) {
 982:         Exif[i].label = _SW("GPS精度(XY方向;メートル)");
 983:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.AccuracyXY));
 984:         i++;
 985:         Exif[i].label = _SW("GPS精度(Z方向;メートル)");
 986:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.AccuracyZ));
 987:         i++;
 988:     }
 989:     Exif[i].label = _SW("GPS精度低下率");
 990:     Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.GPSDOP));
 991:     i++;
 992:     Exif[i].label = _SW("GPS差分補正");
 993:     switch (imageEXIF.GeoLocation.GPSDifferential) {
 994:         case 0:
 995:             ss = "差分補正なし";
 996:             break;
 997:         case 1:
 998:             ss = "差分補正適用";
 999:             break;
1000:         default:
1001:             ss = "不明";
1002:             break;
1003:     }
1004:     i++;
1005:     if (! imageEXIF.GeoLocation.GPSTimeStamp.empty()) {
1006:         Exif[i].label = _SW("GPS時刻(UTC)");
1007:         Exif[i].data  = _SW(imageEXIF.GeoLocation.GPSTimeStamp);
1008:         i++;
1009:     }
1010:     if (! imageEXIF.GeoLocation.GPSDateStamp.empty()) {
1011:         Exif[i].label = _SW("GPS日付");
1012:         Exif[i].data  = _SW(imageEXIF.GeoLocation.GPSDateStamp);
1013:         i++;
1014:     }
1015: 
1016:     return ret;
1017: }

Exif情報の取得は、サムネイル領域への表示とは別に行う。
tinyEXIF のクラス EXIFStreamFile を使って画像ファイルを読み込むことで、Exif情報を取得できる。これを、冒頭で定義したクラス配列 Exif へ逐次代入するのがユーザー関数 loadExif である。
逐次代入するのに合わせ、タグ名(データ項目名)を日本語にしている。exif-subifdで使われるtagデジタルスチルカメラ用画像ファイルフォーマット規格 Exif 2.3などを参考にした。
その他の関数、マップ描画、ヘルプファイルやインストーラー作成方法については、これまでの連載で説明してきたとおりである。

参考サイト

(この項おわり)
header