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

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

目次

サンプル・プログラム

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

使用ライブラリ

文字列リテラルの操作に、ープンソースのライブラリ Boost C++ライブラリが必要になる。導入方法等については、「C++ 開発環境の準備」をご覧いただきたい。
また、地図表示に Web ブラウザ・コントロールを利用するため、オープンソースの cwebpage.dll および webbrowser.h が必要になる。cwebpage.dllcodeproject から、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 "C:\Windows\system32\GdiPlus.dll" "C:\pleiades\eclipse\mingw\mysys2\mingw32\bin\cwebpage.dll" に設定する。

解説:ヘッダファイル等

0011: // 初期化処理 ======================================================
0012: #include <iostream>
0013: #include <fstream>
0014: #include <stdio.h>
0015: #include <stdlib.h>
0016: #include <tchar.h>
0017: #include <time.h>
0018: #include <sstream>
0019: #include <string>
0020: #include <winsock2.h>
0021: #include <windows.h>
0022: #include <shlobj.h>
0023: #include <commctrl.h>
0024: #include <boost/format.hpp>
0025: #include <gdiplus/gdiplus.h>
0026: #include <ole2.h>
0027: #include <richedit.h>
0028: #include <boost/format.hpp>
0029: #include "webbrowser.h"
0030: #include "TinyEXIF.h"
0031: #include "resource.h"
0032: 
0033: using namespace std;
0034: using namespace Gdiplus;
0035: using namespace boost;
0036: 
0037: #define APPNAME     "フォトマップ"            //アプリケーション名
0038: #define APPVERSION "1.0"                    //バージョン
0039: #define APPYEAR     "2020"                    //作成年
0040: #define REFERENCE "https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-13-01.shtm"  // 参考サイト
0041: 
0042: //char*バッファサイズ
0043: #define SIZE_BUFF     512
0044: 
0045: //ListViewItemの最大文字長:変更不可
0046: #define MAX_LISTVIEWITEM 258
0047: 
0048: //現在のインターフェイス
0049: static HINSTANCE hInst;
0050: 
0051: //親ウィンドウ
0052: static HWND hParent;
0053: 
0054: //AppDataのフルパス
0055: TCHAR MyPath[MAX_PATH];
0056: #define MYAPPDATA "photomapwin"
0057: 
0058: //エラー・メッセージ格納用
0059: string ErrorMessage;
0060: 
0061: //ヘルプ・ファイル
0062: #define HELPFILE ".\\etc\\help.chm"
0063: 
0064: //ブラウザ・コントロール
0065: WebBrowser wBrowser;
0066: 
0067: //マップに表示するマーカー画像URL
0068: #define URL_MARKER "http://maps.google.co.jp/mapfiles/ms/icons/red-dot.png"
0069: 
0070: //Google Cloud Platform APIキー格納ファイル名:インストーラー使用時は変更不可
0071: #define FNAME_GOOGLE_API "GoogleAPIkey"
0072: string GoogleAPIkey = "";
0073: string GoogleMap1 = "";
0074: string GoogleMap2 = "";
0075: string GoogleMap3 = "";
0076: string GoogleMap4 = "";
0077: 
0078: #define ZOOM         12        //地図拡大率(デフォルト)
0079: #define MAP_WIDTH     630        //地図の幅(ピクセル)
0080: #define MAP_HEIGHT     325        //地図の高さ(ピクセル)
0081: 
0082: //撮影地点(緯度・経度)
0083: double LatitudeLongitude;

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

解説:データ構造

0107: //Exif格納クラス
0108: #define SIZE_EXIFS     100        //最大格納数
0109: class _Exif {
0110: public:
0111:     wstring label = L"";        //データ項目
0112:     wstring data  = L"";        //データ本体
0113: Exif[SIZE_EXIFS];

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

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

0303: /**
0304:  * ファイル名(フルパス)からファイル名(拡張子を除く)を取り出す
0305:  * @param wstring path ファイル名(フルパス)
0306:  * @return wstring ファイル名(拡張子を除く)
0307: */
0308: wstring wgetBasename(wstring path) {
0309:     wstring basename = L"";
0310: 
0311:     //最後のディレクトリ識別子を探す
0312:     size_t pos = path.rfind(_SW("\\"));
0313:     if (pos == string::npos) {
0314:         return basename;
0315:     }
0316:     //ファイル名+拡張子
0317:     wstring fname = path.substr(pos + 1);
0318: 
0319:     //拡張子を除く
0320:     pos = fname.rfind(_SW("."));
0321:     if (pos == string::npos) {
0322:         basename = fname;
0323:     } else {
0324:         basename = fname.substr(0, pos);
0325:     }
0326:     return basename;
0327: }

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

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

0675: /**
0676:  * 画像ファイルの表示
0677:  * @param HWND  hWnd 表示するウィンドウ・ハンドル
0678:  * @param const char *fname 画像ファイル名
0679:  * @return bool TRUE:表示成功/FALSE:失敗
0680: */
0681: bool onPaint(HWND hWndconst char *fname) {
0682:     HDC hdc;
0683:     PAINTSTRUCT ps;
0684:     static HDC hdcBMP;
0685:     static int ix,iy;
0686:     int sxsy;
0687:     RECT rect;
0688:     bool ret = TRUE;
0689: 
0690:     //画像ファイル読み込み
0691:     hdc = GetDC(hWnd);
0692:     if(loadImageOnMemory((TCHAR*)fname, &hdcBMPhdc, &ix, &iy) == FALSE) {
0693:         ErrorMessage = "画像ファイル " + (string)fname + " の読み込みに失敗";
0694:         return FALSE;
0695:     }
0696:     ReleaseDC(hWndhdc);
0697:     //縮小表示
0698:     InvalidateRect(hWnd, 0, TRUE);
0699:     hdc = BeginPaint(hWnd, &ps);
0700:     GetClientRect(hWnd, &rect);
0701:     reSizeImage(&sx, &syixiy, (rect.right-rect.left), (rect.bottom-rect.top));
0702:     SetStretchBltMode(hdcHALFTONE);
0703:     StretchBlt(hdc, 0, 0, sxsyhdcBMP, 0, 0, ixiySRCCOPY);
0704:     EndPaint(hWnd, &ps);
0705: 
0706:     return ret;
0707: }

0597: /**
0598:  * 画像ファイルをGDI+を使って読み込む
0599:  * @param TCHAR* szFile  画像ファイル名
0600:  * @param HDC* hdcBMP      オフスクリーン用デバイスコンテキストを格納
0601:  * @param HDC  hdc      デバイスコンテキスト
0602:  * @param int* ix, iy        読み込んだ画像の横・縦ピクセル数
0603:  * @return なし
0604: */
0605: bool loadImageOnMemory(TCHARszFileHDChdcBMPHDC hdcintixintiy) {
0606:     ImageimageP;
0607:     WCHAR wTitle[MAX_PATH];
0608:     BITMAPINFOHEADER bmih;
0609:     HBITMAP hBitmapHOldBitmap;
0610:     BYTE *pBits;
0611: 
0612:     //読み込むファイル名
0613:     MultiByteToWideChar(932, 0, szFile, -1, wTitlesizeof(wTitle) / sizeof(TCHAR));
0614:     imageP = Bitmap::FromFile(wTitle);
0615:     if (imageP == 0) {
0616:         *ix = 0;
0617:         *iy = 0;
0618:         *hdcBMP = 0;
0619:         return FALSE;
0620:     }
0621: 
0622:     bmih.biSize = sizeof(bmih);
0623:     bmih.biWidth = *ix = imageP->GetWidth();
0624:     bmih.biHeight = *iy = imageP->GetHeight();
0625:     bmih.biPlanes = 1;
0626:     bmih.biBitCount = 32;
0627:     bmih.biCompression = BI_RGB;
0628:     bmih.biSizeImage = 0;
0629:     bmih.biXPelsPerMeter = 0;
0630:     bmih.biYPelsPerMeter = 0;
0631:     bmih.biClrUsed = 0;
0632:     bmih.biClrImportant = 0;
0633:     hBitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, 0, (void**)&pBitsNULL, 0);
0634:     if (pBits == NULL) {
0635:         ErrorMessage = "メモリ不足";
0636:         DeleteObject(hBitmap);
0637:         delete imageP;
0638:         *ix = 0;
0639:         *iy = 0;
0640:         return FALSE;
0641:     }
0642:     *hdcBMP = CreateCompatibleDC(hdc);
0643:     HOldBitmap = (HBITMAP)SelectObject(*hdcBMPhBitmap);
0644:     Graphics MyGraphics(*hdcBMP);
0645:     MyGraphics.DrawImage(imageP, 0, 0, imageP->GetWidth(), imageP->GetHeight());
0646:     delete imageP;
0647: 
0648:     return TRUE;
0649: }

0651: /**
0652:  * 縦横比を保った画像サイズ計算
0653:  * @param int* sx, sy  変換後画像サイズを格納
0654:  * @param int  px, py  元画像の縦横サイズ
0655:  * @param int  dx, dy  変換後領域の縦横サイズ
0656:  * @return なし
0657: */
0658: void reSizeImage(intsxintsyint pxint pyint dxint dy) {
0659:     double pxy = double(px) / double(py);    //元画像の横縦比
0660:     double dxy = double(dx) / double(dy);    //変換後の横縦比
0661:     int dx2dy2;
0662: 
0663:     //横を基準に縦を縮小
0664:     if(pxy > dxy) {
0665:         dx2 = dx;
0666:         dy2 = (int)double(dx / pxy);
0667:     } else {
0668:         dy2 = dy;
0669:         dx2 = (int)double(dy * pxy);
0670:     }
0671:     *sx = dx2;
0672:     *sy = dy2;
0673: }

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

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

解説:Exif情報を取得する

0709: /**
0710:  * 画像ファイルを開いてExif情報を取得する(実作業)
0711:  * @param const char *fname 保存ファイル名
0712:  * @return bool TRUE:取得成功/FALSE:失敗
0713: */
0714: bool loadExif(const char *fname) {
0715:     double f;
0716:     string ss;
0717:     char buff[SIZE_BUFF + 1];
0718:     bool ret = TRUE;
0719: 
0720:     //画像ファイルのオープン
0721:     EXIFStreamFile stream(fname);
0722:     if (! stream.IsValid()) {
0723:         ErrorMessage = "画像ファイル " + (string)fname + " の読み込みに失敗";
0724:         return FALSE;
0725:     }
0726:     //Exif情報の取得
0727:     TinyEXIF::EXIFInfo imageEXIF(stream);
0728: 
0729:     //Exif情報を配列Exifへ代入
0730:     //初期化
0731:     for (int i = 0; i < SIZE_EXIFSi++) {
0732:         Exif[i].label = L"";
0733:         Exif[i].data  = L"";
0734:     }
0735:     //代入
0736:     int i = 0;
0737:     //位置情報
0738:     if (imageEXIF.GeoLocation.hasLatLon()) {
0739:         Exif[i].label = _SW("緯度(度)");
0740:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.Latitude));
0741:         Latitude = imageEXIF.GeoLocation.Latitude;
0742:         i++;
0743:         Exif[i].label = _SW("経度(度)");
0744:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.Longitude));
0745:         Longitude = imageEXIF.GeoLocation.Longitude;
0746:         i++;
0747:     } else {
0748:         ErrorMessage = "画像ファイル " + (string)fname + " に位置情報が存在しない";
0749:         ret = FALSE;
0750:     }
0751:     if (imageEXIF.GeoLocation.hasAltitude()) {
0752:         Exif[i].label = _SW("基準高度");
0753:         switch (imageEXIF.GeoLocation.AltitudeRef) {
0754:             case 0:
0755:                 Exif[i].data = _SW("海抜");
0756:                 break;
0757:             case 1:
0758:                 Exif[i].data = _SW("海面下");
0759:                 break;
0760:             default:
0761:                 Exif[i].data = _SW("不明");
0762:                 break;
0763:         }
0764:         i++;
0765:         Exif[i].label = _SW("高度(メートル)");
0766:         snprintf(buffSIZE_BUFF, "%.1f", imageEXIF.GeoLocation.Altitude);
0767:         Exif[i].data  = _SW((string)buff);
0768:         i++;
0769:     }
0770:     if (! imageEXIF.GeoLocation.GPSMapDatum.empty()) {
0771:         Exif[i].label = _SW("測地系");
0772:         Exif[i].data  = _SW(imageEXIF.GeoLocation.GPSMapDatum);
0773:         i++;
0774:     }
0775:     //画像情報
0776:     if (imageEXIF.ImageWidth || imageEXIF.ImageHeight) {
0777:         Exif[i].label = _SW("画像の幅(ピクセル)");
0778:         Exif[i].data  = _SW(to_string(imageEXIF.ImageWidth));
0779:         i++;
0780:         Exif[i].label = _SW("画像の高さ(ピクセル)");
0781:         Exif[i].data  = _SW(to_string(imageEXIF.ImageHeight));
0782:         i++;
0783:     }
0784:     if (imageEXIF.RelatedImageWidth || imageEXIF.RelatedImageHeight) {
0785:         Exif[i].label = _SW("画像の幅(ピクセル)");
0786:         Exif[i].data  = _SW(to_string(imageEXIF.RelatedImageWidth));
0787:         i++;
0788:         Exif[i].label = _SW("画像の高さ(ピクセル)");
0789:         Exif[i].data  = _SW(to_string(imageEXIF.RelatedImageHeight));
0790:         i++;
0791:     }
0792:     if (! imageEXIF.ImageDescription.empty()) {
0793:         Exif[i].label = _SW("画像タイトル");
0794:         Exif[i].data  = _SW(imageEXIF.ImageDescription);
0795:         i++;
0796:     }
0797:     if (! imageEXIF.Make.empty() || ! imageEXIF.Model.empty()) {
0798:         Exif[i].label = _SW("カメラのメーカー名");
0799:         Exif[i].data  = _SW(imageEXIF.Make);
0800:         i++;
0801:         Exif[i].label = _SW("カメラのモデル名");
0802:         Exif[i].data  = _SW(imageEXIF.Model);
0803:         i++;
0804:     }
0805:     if (! imageEXIF.SerialNumber.empty()) {
0806:         Exif[i].label = _SW("シリアル番号");
0807:         Exif[i].data  = _SW(imageEXIF.SerialNumber);
0808:         i++;
0809:     }
0810:     if (imageEXIF.Orientation) {
0811:         Exif[i].label = _SW("画像方向");
0812:         switch (imageEXIF.Orientation) {
0813:             case 1:
0814:                 Exif[i].data  = _SW("左上");
0815:                 break;
0816:             case 3:
0817:                 Exif[i].data  = _SW("右下");
0818:                 break;
0819:             case 6:
0820:                 Exif[i].data  = _SW("右上");
0821:                 break;
0822:             case 8:
0823:                 Exif[i].data  = _SW("左下");
0824:                 break;
0825:             default:
0826:                 Exif[i].data  = _SW("不明");
0827:                 break;
0828:         }
0829:         i++;
0830:     }
0831:     if (imageEXIF.XResolution || imageEXIF.YResolution || imageEXIF.ResolutionUnit) {
0832:         ss = "";
0833:         switch (imageEXIF.ResolutionUnit) {
0834:             case 2:
0835:                 ss = "(インチ)";
0836:                 break;
0837:             case 3:
0838:                 ss = "(センチ)";
0839:                 break;
0840:             default:
0841:                 ss = "";
0842:                 break;
0843:         }
0844:         Exif[i].label = _SW("幅の解像度" + ss);
0845:         Exif[i].data  = _SW(to_string((int)imageEXIF.XResolution));
0846:         i++;
0847:         Exif[i].label = _SW("高さの解像度" + ss);
0848:         Exif[i].data  = _SW(to_string((int)imageEXIF.YResolution));
0849:         i++;
0850:     }
0851:     if (imageEXIF.BitsPerSample) {
0852:         Exif[i].label = _SW("画像のビットの深さ");
0853:         Exif[i].data  = _SW(to_string(imageEXIF.BitsPerSample));
0854:         i++;
0855:     }
0856:     if (! imageEXIF.Software.empty()) {
0857:         Exif[i].label = _SW("ソ\フトウェア");
0858:         Exif[i].data  = _SW(imageEXIF.Software);
0859:         i++;
0860:     }
0861:     if (! imageEXIF.DateTime.empty()) {
0862:         Exif[i].label = _SW("撮影日時");
0863:         Exif[i].data  = _SW(imageEXIF.DateTime);
0864:         i++;
0865:     }
0866:     if (! imageEXIF.DateTimeOriginal.empty()) {
0867:         Exif[i].label = _SW("撮影日時(オリジナル)");
0868:         Exif[i].data  = _SW(imageEXIF.DateTimeOriginal);
0869:         i++;
0870:     }
0871:     if (! imageEXIF.DateTimeDigitized.empty()) {
0872:         Exif[i].label = _SW("撮影日時(デジタイズ)");
0873:         Exif[i].data  = _SW(imageEXIF.DateTimeDigitized);
0874:         i++;
0875:     }
0876:     if (! imageEXIF.SubSecTimeOriginal.empty()) {
0877:         Exif[i].label = _SW("撮影日時(サブセック)");
0878:         Exif[i].data  = _SW(imageEXIF.SubSecTimeOriginal);
0879:         i++;
0880:     }
0881:     if (! imageEXIF.Copyright.empty()) {
0882:         Exif[i].label = _SW("著作権者");
0883:         Exif[i].data  = _SW(imageEXIF.Copyright);
0884:         i++;
0885:     }
0886:     Exif[i].label = _SW("露光制御");
0887:     switch (imageEXIF.ExposureProgram) {
0888:         case 1:
0889:             Exif[i].data  = _SW("マニュアル設定");
0890:             break;
0891:         case 2:
0892:             Exif[i].data  = _SW("通常のプログラムAE");
0893:             break;
0894:         case 3:
0895:             Exif[i].data  = _SW("絞り優先");
0896:             break;
0897:         case 4:
0898:             Exif[i].data  = _SW("シャッター速度優先");
0899:             break;
0900:         case 5:
0901:             Exif[i].data  = _SW("低速プログラム");
0902:             break;
0903:         case 6:
0904:             Exif[i].data  = _SW("高速プログラム");
0905:             break;
0906:         case 7:
0907:             Exif[i].data  = _SW("ポートレートモード");
0908:             break;
0909:         case 8:
0910:             Exif[i].data  = _SW("風景モード");
0911:             break;
0912:         default:
0913:             Exif[i].data  = _SW("不明");
0914:             break;
0915:     }
0916:     i++;
0917:     Exif[i].label = _SW("ISOスピードレート");
0918:     Exif[i].data  = _SW(to_string(imageEXIF.ISOSpeedRatings));
0919:     i++;
0920:     Exif[i].label = _SW("シャッター速度(秒)");
0921:     f = 1.0 / imageEXIF.ShutterSpeedValue;
0922:     Exif[i].data  = _SW("1/" + to_string((int)f));
0923:     i++;
0924:     Exif[i].label = _SW("露出時間");
0925:     f = 1.0 / imageEXIF.ExposureTime;
0926:     Exif[i].data  = _SW("1/" + to_string((int)f));
0927:     i++;
0928:     Exif[i].label = _SW("絞り値");
0929:     Exif[i].data  = _SW(to_string(imageEXIF.ApertureValue));
0930:     i++;
0931:     Exif[i].label = _SW("F値");
0932:     Exif[i].data  = _SW(to_string(imageEXIF.FNumber));
0933:     i++;
0934:     Exif[i].label = _SW("レンズ焦点距離");
0935:     Exif[i].data  = _SW(to_string((int)imageEXIF.FocalLength));
0936:     i++;
0937:     Exif[i].label = _SW("輝度値");
0938:     Exif[i].data  = _SW(to_string(imageEXIF.BrightnessValue));
0939:     i++;
0940:     Exif[i].label = _SW("露出補正値");
0941:     Exif[i].data  = _SW(to_string(imageEXIF.ExposureBiasValue));
0942:     i++;
0943:     Exif[i].label = _SW("被写体距離");
0944:     Exif[i].data  = _SW(to_string(imageEXIF.SubjectDistance));
0945:     i++;
0946:     Exif[i].label = _SW("フラッシュ");
0947:     switch (imageEXIF.Flash) {
0948:         case 0:
0949:             Exif[i].data  = _SW("非発光");
0950:             break;
0951:         case 1:
0952:             Exif[i].data  = _SW("発光");
0953:             break;
0954:         case 5:
0955:             Exif[i].data  = _SW("発光したが反射光検出できず");
0956:             break;
0957:         case 6:
0958:             Exif[i].data  = _SW("発光して反射光検出");
0959:             break;
0960:         default:
0961:             Exif[i].data  = _SW("不明");
0962:             break;
0963:     }
0964:     i++;
0965:     Exif[i].label = _SW("測光モード");
0966:     switch (imageEXIF.MeteringMode) {
0967:         case 0:
0968:             Exif[i].data  = _SW("平均測光");
0969:             break;
0970:         case 1:
0971:             Exif[i].data  = _SW("中央重点");
0972:             break;
0973:         case 2:
0974:             Exif[i].data  = _SW("スポット");
0975:             break;
0976:         case 4:
0977:             Exif[i].data  = _SW("多点スポット");
0978:             break;
0979:         case 5:
0980:             Exif[i].data  = _SW("マルチセグメント");
0981:             break;
0982:         case 6:
0983:             Exif[i].data  = _SW("部分測光");
0984:             break;
0985:         default:
0986:             Exif[i].data  = _SW("不明");
0987:             break;
0988:     }
0989:     i++;
0990:     Exif[i].label = _SW("光源");
0991:     switch (imageEXIF.LightSource) {
0992:         case 1:
0993:             Exif[i].data  = _SW("昼光");
0994:             break;
0995:         case 2:
0996:             Exif[i].data  = _SW("蛍光燈");
0997:             break;
0998:         case 3:
0999:             Exif[i].data  = _SW("白熱電球");
1000:             break;
1001:         case 4:
1002:             Exif[i].data  = _SW("フラッシュ");
1003:             break;
1004:         case 9:
1005:             Exif[i].data  = _SW("快晴");
1006:             break;
1007:         case 10:
1008:             Exif[i].data  = _SW("曇天");
1009:             break;
1010:         case 11:
1011:             Exif[i].data  = _SW("日陰");
1012:             break;
1013:         case 12:
1014:             Exif[i].data  = _SW("昼光色");
1015:             break;
1016:         case 13:
1017:             Exif[i].data  = _SW("昼白色");
1018:             break;
1019:         case 14:
1020:             Exif[i].data  = _SW("蛍光色");
1021:             break;
1022:         case 15:
1023:             Exif[i].data  = _SW("白色");
1024:             break;
1025:         case 17:
1026:             Exif[i].data  = _SW("標準ライトA");
1027:             break;
1028:         case 18:
1029:             Exif[i].data  = _SW("標準ライトB");
1030:             break;
1031:         case 19:
1032:             Exif[i].data  = _SW("標準ライトC");
1033:             break;
1034:         case 20:
1035:             Exif[i].data  = _SW("D55");
1036:             break;
1037:         case 21:
1038:             Exif[i].data  = _SW("D65");
1039:             break;
1040:         case 22:
1041:             Exif[i].data  = _SW("D75");
1042:             break;
1043:         case 23:
1044:             Exif[i].data  = _SW("D50");
1045:             break;
1046:         case 24:
1047:             Exif[i].data  = _SW("ISO白熱電球");
1048:             break;
1049:         default:
1050:             Exif[i].data  = _SW("不明");
1051:             break;
1052:     }
1053:     i++;
1054:     if (imageEXIF.Calibration.FocalLength != 0) {
1055:         Exif[i].label = _SW("(ピクセル)");
1056:         Exif[i].data  = _SW(to_string(imageEXIF.Calibration.FocalLength));
1057:         i++;
1058:     }
1059:     if (imageEXIF.Calibration.OpticalCenterX != 0) {
1060:         Exif[i].label = _SW("(ピクセル)");
1061:         Exif[i].data  = _SW(to_string(imageEXIF.Calibration.OpticalCenterX));
1062:         i++;
1063:     }
1064:     if (imageEXIF.Calibration.OpticalCenterY != 0) {
1065:         Exif[i].label = _SW("(ピクセル)");
1066:         Exif[i].data  = _SW(to_string(imageEXIF.Calibration.OpticalCenterY));
1067:         i++;
1068:     }
1069:     //レンズ情報
1070:     Exif[i].label = _SW("最小絞り値");
1071:     Exif[i].data  = _SW(to_string((int)imageEXIF.LensInfo.FStopMin));
1072:     i++;
1073:     Exif[i].label = _SW("最大絞り値");
1074:     Exif[i].data  = _SW(to_string((int)imageEXIF.LensInfo.FStopMax));
1075:     i++;
1076:     Exif[i].label = _SW("広角側(ミリ)");
1077:     Exif[i].data  = _SW(to_string((int)imageEXIF.LensInfo.FocalLengthMin));
1078:     i++;
1079:     Exif[i].label = _SW("望遠側(ミリ)");
1080:     Exif[i].data  = _SW(to_string((int)imageEXIF.LensInfo.FocalLengthMax));
1081:     i++;
1082:     Exif[i].label = _SW("デジタルズーム倍率");
1083:     Exif[i].data  = _SW(to_string((int)imageEXIF.LensInfo.DigitalZoomRatio));
1084:     i++;
1085:     Exif[i].label = _SW("焦点距離(35ミリ判相当");
1086:     Exif[i].data  = _SW(to_string((int)imageEXIF.LensInfo.FocalLengthIn35mm));
1087:     i++;
1088:     ss = "";
1089:     switch (imageEXIF.LensInfo.FocalPlaneResolutionUnit) {
1090:         case 2:
1091:             ss = "(インチ)";
1092:             break;
1093:         case 3:
1094:             ss = "(センチ)";
1095:             break;
1096:         default:
1097:             ss = "";
1098:             break;
1099:     }
1100:     Exif[i].label = _SW("焦点面の幅の解像度" + ss);
1101:     Exif[i].data  = _SW(to_string(imageEXIF.LensInfo.FocalPlaneXResolution));
1102:     i++;
1103:     Exif[i].label = _SW("焦点面の高さの解像度" + ss);
1104:     Exif[i].data  = _SW(to_string(imageEXIF.LensInfo.FocalPlaneYResolution));
1105:     i++;
1106:     if (! imageEXIF.LensInfo.Make.empty() || ! imageEXIF.LensInfo.Model.empty()) {
1107:         Exif[i].label = _SW("レンズのメーカー名");
1108:         Exif[i].data  = _SW(imageEXIF.LensInfo.Make);
1109:         i++;
1110:         Exif[i].label = _SW("レンズのモデル名");
1111:         Exif[i].data  = _SW(imageEXIF.LensInfo.Model);
1112:         i++;
1113:     }
1114:     //位置情報
1115:     if (imageEXIF.GeoLocation.hasRelativeAltitude()) {
1116:         Exif[i].label = _SW("相対高度(メートル)");
1117:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.RelativeAltitude));
1118:         i++;
1119:     }
1120:     if (imageEXIF.GeoLocation.hasOrientation()) {
1121:         Exif[i].label = _SW("ロール角");
1122:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.RollDegree));
1123:         i++;
1124:         Exif[i].label = _SW("ピッチ角");
1125:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.PitchDegree));
1126:         i++;
1127:         Exif[i].label = _SW("ヨー角");
1128:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.YawDegree));
1129:         i++;
1130:     }
1131:     if (imageEXIF.GeoLocation.hasSpeed()) {
1132:         Exif[i].label = _SW("飛行速度(X方向;メートル/秒)");
1133:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.SpeedX));
1134:         i++;
1135:         Exif[i].label = _SW("飛行速度(Y方向;メートル/秒)");
1136:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.SpeedY));
1137:         i++;
1138:         Exif[i].label = _SW("飛行速度(Z方向;メートル/秒)");
1139:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.SpeedZ));
1140:         i++;
1141:     }
1142:     if (imageEXIF.GeoLocation.AccuracyXY > 0 || imageEXIF.GeoLocation.AccuracyZ > 0) {
1143:         Exif[i].label = _SW("GPS精度(XY方向;メートル)");
1144:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.AccuracyXY));
1145:         i++;
1146:         Exif[i].label = _SW("GPS精度(Z方向;メートル)");
1147:         Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.AccuracyZ));
1148:         i++;
1149:     }
1150:     Exif[i].label = _SW("GPS精度低下率");
1151:     Exif[i].data  = _SW(to_string(imageEXIF.GeoLocation.GPSDOP));
1152:     i++;
1153:     Exif[i].label = _SW("GPS差分補正");
1154:     switch (imageEXIF.GeoLocation.GPSDifferential) {
1155:         case 0:
1156:             ss = "差分補正なし";
1157:             break;
1158:         case 1:
1159:             ss = "差分補正適用";
1160:             break;
1161:         default:
1162:             ss = "不明";
1163:             break;
1164:     }
1165:     i++;
1166:     if (! imageEXIF.GeoLocation.GPSTimeStamp.empty()) {
1167:         Exif[i].label = _SW("GPS時刻(UTC)");
1168:         Exif[i].data  = _SW(imageEXIF.GeoLocation.GPSTimeStamp);
1169:         i++;
1170:     }
1171:     if (! imageEXIF.GeoLocation.GPSDateStamp.empty()) {
1172:         Exif[i].label = _SW("GPS日付");
1173:         Exif[i].data  = _SW(imageEXIF.GeoLocation.GPSDateStamp);
1174:         i++;
1175:     }
1176: 
1177:     return ret;
1178: }

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

参考サイト

(この項おわり)
header