Help us understand the problem. What is going on with this article?

kintoneに登録された写真のEXIF情報から地図表示 ~ 最近彼女ができたから ~

More than 3 years have passed since last update.

妄想

彼女が出来たのでいっぱい写真撮るんです。こんな感じの写真。
IMG_1762.JPG

でも後で見返すとどこで撮った写真かわからないんですよね。
iPhoneだと住所は表示されるけど、地図で見たい!
彼女との想い出はできるだけスマートに保存しておきたいじゃないですか?

現実

仕事で現場の写真をよく撮るんです。
社内でkintoneを使い始めたこともあって、撮った写真をkintoneに添付するんですよ。
スマホで撮った写真を添付するの楽だし、社内のグループメンバーにもその場で共有できるし。

でも、枚数が多いから後で見るとどこで撮った写真か分からなくなるんですよね・・・
メモするのも面倒だし・・・

やったこと

ということで、kintoneに添付した写真のEXIF情報から地図表示するサンプルコードを作りました。
Googleマップは商業利用の場合、問い合わせが必要なので今回はOpenStreetMapを利用します。

前提

  • kintoneに添付されるファイルは1個かつ画像
  • 画像は縮小版ではないこと(縮小版だとEXIF情報がカットされます)
  • kintoneのフォーム設定は以下の通り
フィールド名 フィールドタイプ フィールドコード
写真 添付ファイル pic
スペース map
(function() {
    'use strict';

    // kintoneに添付されたファイルをダウンロード
    function getFile(url) {
        var df = new $.Deferred();
        var xhr = new XMLHttpRequest();

        xhr.open('GET', url, true);
        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        xhr.responseType = 'blob';

        xhr.onload = function(e) {
            if (this.status === 200) {
                df.resolve(this.response);
            }
        };

        xhr.send();
        return df.promise();
    }

    // 度分秒から百分率に変換
    function toPercentage(ref, geo) {
        if (ref === 'N' || ref === 'E') {
            return geo[0] + geo[1] / 60 + geo[2] / 3600;
        } else if (ref === 'S' || ref === 'W') {
            return -(geo[0] + geo[1] / 60 + geo[2] / 3600);
        }
    }

    kintone.events.on('app.record.detail.show', function(event) {
        var record = event.record;

        var space = kintone.app.record.getSpaceElement('map');
        var fileKey = record.pic.value[0].fileKey;
        var fileUrl = '/k/v1/file.json?fileKey=' + fileKey;

        $(space).append('<div id="canvas" style="width:400px; height:400px"></div>');

        var promise = getFile(fileUrl);
        promise.done(function(imageData) {
            loadImage.parseMetaData(imageData, function(data) {
                // 画像からロケーション情報を取得
                try {
                    var gpsLatitude = data.exif.get('GPSLatitude');
                    var gpsLatitudeRef = data.exif.get('GPSLatitudeRef');
                    var gpsLongitude = data.exif.get('GPSLongitude');
                    var gpsLongitudeRef = data.exif.get('GPSLongitudeRef');

                    var latitude = toPercentage(gpsLatitudeRef, gpsLatitude);
                    var longitude = toPercentage(gpsLongitudeRef, gpsLongitude);
                } catch(e) {
                    console.log('EXIF情報が含まれていません', e);
                    return false;
                }


                var map = new OpenLayers.Map('canvas');

                // 地図の表示
                var mapnik = new OpenLayers.Layer.OSM(
                    // デフォルトではhttp接続になるのでhttps接続に変更
                    'OpenStreetMap',
                    [
                        '//a.tile.openstreetmap.org/${z}/${x}/${y}.png',
                        '//b.tile.openstreetmap.org/${z}/${x}/${y}.png',
                        '//c.tile.openstreetmap.org/${z}/${x}/${y}.png'
                    ],
                    null
                );
                map.addLayer(mapnik);

                var lonLat = new OpenLayers.LonLat(longitude, latitude)
                    .transform(
                        new OpenLayers.Projection('EPSG:4326'),
                        new OpenLayers.Projection('EPSG:900913')
                    );
                map.setCenter(lonLat, 15);


                // マーカーの表示
                var markers = new OpenLayers.Layer.Markers('Markers');
                map.addLayer(markers);
                var marker = new OpenLayers.Marker(
                    new OpenLayers.LonLat(longitude, latitude).transform(
                        new OpenLayers.Projection('EPSG:4326'),
                        new OpenLayers.Projection('EPSG:900913')
                    )
                );
                markers.addMarker(marker);
            });
        });
    });

    kintone.events.on('app.record.index.show', function(event) {
        // 地図を表示済みの場合は一旦削除
        if ($('div#canvas').length > 0) {
            $('div#canvas').remove();
        }

        var space = kintone.app.getHeaderSpaceElement();
        $(space).append('<div id="canvas" style="width:80%; height:400px"></div>');

        var map = new OpenLayers.Map('canvas');

        var mapnik = new OpenLayers.Layer.OSM(
            'OpenStreetMap',
            [
                '//a.tile.openstreetmap.org/${z}/${x}/${y}.png',
                '//b.tile.openstreetmap.org/${z}/${x}/${y}.png',
                '//c.tile.openstreetmap.org/${z}/${x}/${y}.png'
            ],
            null
        );
        map.addLayer(mapnik);

        var markers = new OpenLayers.Layer.Markers('Markers');
        map.addLayer(markers);

        for (var i = 0; i < event.records.length; i++) {
            var record = event.records[i];

            var fileKey = record.pic.value[0].fileKey;
            var fileUrl = '/k/v1/file.json?fileKey=' + fileKey;

            var promise = getFile(fileUrl);

            promise.done(function(imageData) {
                loadImage.parseMetaData(imageData, function(data) {
                    try {
                        var gpsLatitude = data.exif.get('GPSLatitude');
                        var gpsLatitudeRef = data.exif.get('GPSLatitudeRef');
                        var gpsLongitude = data.exif.get('GPSLongitude');
                        var gpsLongitudeRef = data.exif.get('GPSLongitudeRef');

                        var latitude = toPercentage(gpsLatitudeRef, gpsLatitude);
                        var longitude = toPercentage(gpsLongitudeRef, gpsLongitude);
                    } catch(e) {
                        console.log('EXIF情報が含まれていません', e);
                        return false;
                    }

                    var lonLat = new OpenLayers.LonLat(longitude, latitude).transform(
                        new OpenLayers.Projection('EPSG:4326'),
                        new OpenLayers.Projection('EPSG:900913')
                    );
                    map.setCenter(lonLat, 15);

                    var marker = new OpenLayers.Marker(
                        new OpenLayers.LonLat(longitude, latitude).transform(
                            new OpenLayers.Projection('EPSG:4326'),
                            new OpenLayers.Projection('EPSG:900913')
                        )
                    );
                    markers.addMarker(marker);
                });
            });
        }
    });

})();

出来上がりのイメージ

詳細画面

こんな感じで地図が表示されます。
IMG_2196.PNG
写真の場所は鳥取砂丘の砂の美術館ですね。楽しかったな~

一覧画面

こんな感じで地図が表示されます。
IMG_2197.PNG
プライバシー尊重のためにモザイクいれました・w・
※写真を撮った距離が離れている場合は拡大率やどこを中心にするかを計算する必要があります。
 今回のサンプルではそこまで考慮していません。

参考

梅田ロフトの場合

鳥取が田舎で地図のイメージがあまり掴めないのでこちらも・・・
IMG_2198.PNG

kintoneでの写真/ビデオ添付手順

  • 添付ファイルフィールドの参照欄をクリック
  • ↓の画像のメニューが表示されるので添付方式を選択

IMG_2193.PNG

  • 写真を選択
  • ↓のメニューが表示されるので縮小のあり/なしを選択

IMG_2194.PNG
※縮小版だとEXIF情報がカットされて地図表示できません

kintone開発者ライセンス

kintone面白そうだな~と思った方は5ユーザー、1年間無償の開発者ライセンスを使ってみてください。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away