PHPで撮影場所をマッピング(Windowsアプリ版)

(1/1)
PHP で Exif 情報を表示する(Windows アプリ版)」では、デジカメで撮影した JPEG画像に含まれる Exif 情報を表示するプログラムをつくった。
今回は、Exif 情報の中にある位置情報(GPS 情報)を Google マップ地理院地図OpenStreetMap のいずれかにマッピングするようにプログラムを拡張する。

なお、後述するように、2018 年(平成 30 年)7 月から Google Maps API キーが必須となったため、本プログラムでは無償の 地理院地図OpenStreetMap を選択した状態でコンパイルした実行型プログラムを同梱している。

実行例

PHPでWindowsアプリ開発:撮影場所をマッピング
Googleマップ
PHPでWindowsアプリ開発:撮影場所をマッピング
OSM(OpenStreetMap)
PHPでWindowsアプリ開発:撮影場所をマッピング
地理院地図
地理院地図・ OSM 利用時は、地図の右上のアイコンで複数の地図を選択することができる。

サンプル・プログラム

圧縮ファイルの内容
photomapwin.exe実行型プログラム
photomapwin.bcpプロジェクトファイル
photomapwin/photomapwin.phpwサンプル・プログラム本体
photomapwin/photomapwin.rcWinAsm用ソース
photomapwin/application.icoアイコン・ファイル
photomapwin/*.rcWinBinber用DLL
photomapwin/ext/WinBinber用拡張ファイル
photomapwin/include/WinBinber用拡張includeファイル
photomapwin/icon/アイコン・ファイル

準備:地図描画サービスの選択

0023: //地図描画サービスの選択
0024: //    0:Google
0025: //    2:地理院地図・OSM
0026: define('MAPSERVICE', 2);
0027: 
0028: //GoogleMaps APIキー;各自でキーを取得のこと
0029: //https://cloud.google.com/maps-platform/
0030: define('GOOGLE_API_KEY', '**************************');

利用する地図描画サービスを、あらかじめ定数 MAPSERVICE に設定する。
地理院地図、OSM(OpenStreetMap)は無償利用できる。

2018 年(平成 30 年)7 月 16 日から、Google Maps API を呼び出すのにキーが必要になった。キーは無料で入手できるが、Google アカウント を持っていること前提になる。
Google Maps Platform」にアクセスし、Google アカウントを使ってログインする。次に、右上の「スタートガイド」か左下の「使ってみる」をクリックすると、キー入手方法を知ることができる。
キーを入手したら、定数 GOOGLE_API_KEY に代入する。

解説:HTMLコントロール

0566: //HTMLControlの設定
0567: wb_create_control($windowHTMLControl, 'JSmap', 10, 280, 750, 400, IDC_GMAP_HTML);

マップを表示するために、HTML コントロールを使用する。

0229:     //マップ表示
0230:     $res = getGPS($exif$latitude$longitude);
0231:     if ($res) {
0232:         switch (MAPSERVICE) {
0233:             case 0:
0234:                 $html = drawGMap($latitude$longitude, 10);
0235:                 break;
0236:             default:
0237:                 $html = drawLeaflet($latitude$longitude, 10);
0238:                 break;
0239:         }
0240:         $outfp = fopen($Tempfname, 'w');
0241:         fwrite($outfp$html);
0242:         fclose($outfp);
0243:         wb_set_location(wb_get_control($windowIDC_GMAP_HTML), $Tempfname);
0244:     } else {
0245:         wb_set_location(wb_get_control($windowIDC_GMAP_HTML), 'cmd:blank');
0246:         put_error($windowERROR_NOTGPS);
0247:     }
0248: 
0249:     //Exif情報表示
0250:     wb_create_items(wb_get_control($windowIDC_EXIF_LISTVIEW), $exif);

HTML へ URL を渡すには、は WinBinder関数 wb_set_location を利用する。

解説:Googleマップ

0276: /**
0277:  * Googleマップを描く
0278:  * @param double $latitude  中心座標:緯度(世界測地系)
0279:  * @param double $longitude 中心座標:経度(世界測地系)
0280:  * @param int    $zoom      拡大率
0281:  * @return string Googleマップのコード
0282: */
0283: function drawGMap($latitude$longitude$zoom) {
0284:     $encode = 'UTF-8';
0285:     $title  = TITLE;
0286:     $id     = 'gmap_spot';
0287:     $key    = GOOGLE_API_KEY;
0288:     $type   = 'ROADMAP';
0289: 
0290: $code =<<< EOD
0291: <!DOCTYPE html>
0292: <html lang="ja">
0293: <head>
0294: <meta charset="{$encode}">
0295: <title>{$title}</title>
0296: <meta name="author" content="studio pahoo" />
0297: <meta name="copyright" content="studio pahoo" />
0298: <meta name="ROBOTS" content="NOINDEX,NOFOLLOW" />
0299: <meta http-equiv="pragma" content="no-cache">
0300: <meta http-equiv="cache-control" content="no-cache">
0301: <meta http-equiv="X-UA-Compatible" content="IE=edge">
0302: </head>
0303: <body>
0304: <script src="https://maps.googleapis.com/maps/api/js?key={$key}&amp;callback=initMap&amp;region=JP" async defer></script>
0305: <script>
0306: function initMap() {
0307:     var map = new google.maps.Map(document.getElementById('{$id}'), {
0308:         center: new google.maps.LatLng({$latitude}, {$longitude}),
0309:         zoom: {$zoom},
0310:         mapTypeId: google.maps.MapTypeId.{$type},
0311:         mapTypeControl: true,
0312:         scaleControl: true
0313:     });
0314:     var marker = new google.maps.Marker({
0315:         position: new google.maps.LatLng({$latitude}, {$longitude}),
0316:         map: map,
0317:         title: '',
0318:         zIndex: 999
0319:     });
0320: }
0321: </script>
0322: <div id="{$id}" style="width:720px; height:370px;"></div>
0323: </body>
0324: </html>
0325: 
0326: EOD;
0327: 
0328:     return $code;
0329: }

Google マップを描画には、Google Maps JavaScript API を利用する。「PHP で最寄りのネットができるホテルを検索する」で作ったユーザー関数 drawGMap をアレンジしている。

解説:地理院地図・OSM

0331: /**
0332:  * Leafletによるマップ描画
0333:  * @param double $latitude  中心座標:緯度(世界測地系)
0334:  * @param double $longitude 中心座標:経度(世界測地系)
0335:  * @param int    $zoom      拡大率
0336:  * @return string Leafletマップのコード
0337: */
0338: function drawLeaflet($latitude$longitude$zoom) {
0339:     $encode = 'SJIS';
0340:     $title  = TITLE;
0341:     $id     = 'lmap';
0342:     $type   = 'OSM';
0343: 
0344: $code =<<< EOD
0345: <!DOCTYPE html>
0346: <html lang="ja">
0347: <head>
0348: <meta charset="{$encode}">
0349: <title>{$title}</title>
0350: <meta name="author" content="studio pahoo" />
0351: <meta name="copyright" content="studio pahoo" />
0352: <meta name="ROBOTS" content="NOINDEX,NOFOLLOW" />
0353: <meta http-equiv="pragma" content="no-cache">
0354: <meta http-equiv="cache-control" content="no-cache">
0355: <meta http-equiv="X-UA-Compatible" content="IE=edge">
0356: <link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" />
0357: <script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"></script>
0358: <script>
0359: window.onload = function() {
0360:     var map = L.map('{$id}',{zoomControl:false});
0361:     map.setView([{$latitude}, {$longitude}], {$zoom});
0362:     L.control.scale({
0363:         maxWidth: 200,
0364:         position: 'bottomright',
0365:         imperial: false
0366:     }).addTo(map);
0367:     L.control.zoom({position:'topleft'}).addTo(map);
0368: 
0369:     //地理院地図:標準地図
0370:     var GSISTD = new L.tileLayer(
0371:         'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
0372:         {
0373:             attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
0374:             minZoom: 0,
0375:             maxZoom: 18,
0376:             name: 'GSISTD'
0377:         });
0378:     //地理院地図:淡色地図
0379:     var GSIPALE = new L.tileLayer(
0380:         'https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png',
0381:         {
0382:             attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
0383:             minZoom: 2,
0384:             maxZoom: 18,
0385:             name: 'GSIPALE'
0386:         });
0387:     //地理院地図:白地図
0388:     var GSIBLANK = new L.tileLayer(
0389:         'https://cyberjapandata.gsi.go.jp/xyz/blank/{z}/{x}/{y}.png',
0390:         {
0391:             attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
0392:             minZoom: 5,
0393:             maxZoom: 14,
0394:             name: 'GSIBLANK'
0395:         });
0396:     //地理院地図:写真
0397:     var GSIPHOTO = new L.tileLayer(
0398:         'https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg',
0399:         {
0400:             attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
0401:             minZoom: 2,
0402:             maxZoom: 18,
0403:             name: 'GSIPHOTO'
0404:         });
0405:     //OpenStreetMap
0406:     var OSM = new L.tileLayer(
0407:         'https://tile.openstreetmap.jp/{z}/{x}/{y}.png',
0408:         {
0409:             attribution: "(c) <a href='https://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors",
0410:             minZoom: 0,
0411:             maxZoom: 18,
0412:             name: 'OSM'
0413:         });
0414: 
0415:     //baseMapsオブジェクトにタイル設定
0416:     var baseMaps = {
0417:         "地理院地図" : GSISTD,
0418:         "淡色地図" : GSIPALE,
0419:         "白地図" : GSIBLANK,
0420:         "写真地図" : GSIPHOTO,
0421:         "オープンストリートマップ" : OSM
0422:     };
0423: 
0424:     //layersコントロールにbaseMapsオブジェクトを設定して地図に追加
0425:     L.control.layers(baseMaps).addTo(map);
0426:     {$type}.addTo(map);
0427: 
0428:     //マーカー
0429:     var icon = new L.marker([{$latitude}, {$longitude}]).addTo(map);
0430: }
0431: </script>
0432: <div id="{$id}" style="width:720px; height:370px;"></div>
0433: </body>
0434: </html>
0435: 
0436: EOD;
0437: 
0438:     return $code;
0439: }

地理院地図・ OSM には、JavaScript ライブラリ Leaflet を利用する。「PHP で住所・ランドマークから最寄り駅を求める」で作ったユーザー関数 drawLeaflet をアレンジしている。

解説:マップ描画

0569: //テンポラリ・ファイルの設定
0570: $path = getenv('TEMP');
0571: //環境変数TEMPあり
0572: if (file_exists($path)) {
0573:     $Tempfname = tempnam($path, 'gmp');
0574: //環境変数TEMPなし
0575: else {
0576:     $path = TEMPPATH;
0577:     @mkdir($path);
0578:     if (! file_exists($path))    exit(1);
0579:     $Tempfname = realpath(tempnam($path, 'gmg'));
0580: }

drawGMap または drawLeaflet が出力する HTML テキストは、ローカルディスクに一時的にセーブし、それを WinBinder関数 wb_set_location で参照する。
一時ファイルの保存先は、環境変数 TEMP が定義されていれば、組み込み関数  getenv  で取得し、無ければユーザー定数 TEMPPATH で定義したフォルダを作成し、そこに一時ファイルとして作成する。
プログラム終了時に組み込み関数  unlink  を使って削除することをお忘れなく。

コンパイル

コマンドラインから "bamcompile photomapwin.bcp" を実行する。コンパイルが完了すると、"photomapwin.exe" が生成される。"photomapwin.exe" は、DLL 不要で、単独で動作する EXE プログラムである。

参考サイト

(この項おわり)
header