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