OpenStreetMap & kintoneで「いまどこ」チェックイン

地図とIT…便利な連携ができそうです。
自分の居場所を知られたくないケースはよくありますが、知ってほしいケースもあります。

例えば

 - 待ち合わせで今いる場所を正確に伝えたい（今ここにいるよーという声）
 - 災害時に報告して安否を通達したい（大丈夫ですよーという声）
 - ラウンダー業務の証拠づくり（現場で仕事していますよーという声）

今回は[OpenStreetMap](https://openstreetmap.jp/) 、[OpenLayers](https://openlayers.org/)、[OpenStreetMap Nominatim](http://nominatim.openstreetmap.org/) を使って、いま・どこで・どうなっているかを記録して通達する仕組みを作ってみました。記録したデータは[kintone](https://kintone.cybozu.com/jp/)に貯めておきます。

# できあがり
スマートフォンのkintoneアプリから保存、閲覧したkintone画面
**だれが、いつ、どこにいたかが早わかり**
![20161209-9-36_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/100494/4e344b00-0880-15b6-f6b4-778e9aed261d.png)

# 素材
## [OpenStreetMap](https://openstreetmap.jp/)
道路地図などの地理情報データを誰でも無償で利用できるサービスで、APIも豊富です。Google Mapも無料で利用しているのになぜ？と思われがちですが、APIを使う際にはライセンス（有償）が必要なことがあるようです。

## [OpenLayers](https://openlayers.org/)
ブラウザに様々な地図を、簡単に重ね合わせて表示できるオープンソースのJavaScriptライブラリです。

## [OpenStreetMap Nominatim](http://nominatim.openstreetmap.org/)
geocoding ができる WebAPIで、緯度・経度から場所を取得できます。

## [kintone](https://kintone.cybozu.com/jp/)
kintoneは、業務システムをエンドユーザーが素早く作れる＆使えるクラウドサービスで、[cybozu developer network](https://cybozudev.zendesk.com/hc/ja)に載っているようにREST API、JavaScript API もあるので、いろいろカスタマイズできちゃいます。

### 使ったAPI、function、method
- ol.Map
- ol.layer.Tile
- ol.View
- ol.source.OSM
- ol.proj.transform
- (OpenLayers).addOverlay
- kintone.app.getId
- kintone.api.url
- kintone.api
- kintone.[mobile.]app.record.getFieldElement
- kintone.app.record.setFieldShown
- kintone.mobile.app.getHeaderSpaceElement
- kintone.app.record.getSpaceElement
- kintone.events.on
- kintone.proxy
- kintone.Promise

# 準備

 - [kintone開発者ライセンスを取得](https://cybozudev.zendesk.com/hc/ja/articles/200929540#step3) ※developer networkへのメンバー必要
 - [Githubでプログラムなどのダウンロード](https://github.com/ushiront/kintone-osmPoint)
 - [スマホ用kintoneアプリ](https://kintone.cybozu.com/jp/feature/mobile.html)のインストール
    - kintoneのURLを指定するとブラウザからも利用可能

# 作り方
 - kintoneにログイン後にアプリテンプレートの読み込み
    - [Githubでプログラム一式のダウンロード](https://github.com/ushiront/kintone-osmPoint)にある「kintone-osmPoint.zip」をkintoneに読み込む ※ [ファイルからアプリテンプレートを読み込む方法](https://help.cybozu.com/ja/k/admin/import_template.html)
 - テンプレートからアプリを作成
 - スマホからログインして使ってみる
    - レコード追加する

# kintoneからの挙動
- レコードを追加したら、位置情報を取得して保存
- 緯度経度情報を元にOpenStreetMapで地図を表示
- 緯度経度情報を元にNominatimを使い逆ジオゴ－ディング
- 中心部にピント、登録者名＋日時を表示
- 緯度経度はブラウザから編集できないように非表示

# プログラム
https://github.com/ushiront/kintone-osmPoint
![20161209-17-12_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/100494/f6553613-3264-7076-f388-30b47524d6cc.png)


## Point.1

```js:sample-osmpoint.js
var appId = kintone.app.getId() ? kintone.app.getId() : 
Number(location.pathname.replace(/\/k\/m\//, "").split("/")[0]);

```
> スマートフォン用のJavaScriptではアプリIDを取得できないのでブラウザのアドレスから取得。

## Point.2

```js:sample-osmpoint.js
var hideGroupFields = function(event) {
    if (event.type.match(/^mobile./)) {
        kintone.mobile.app.record.setFieldShown("Lat", false);
        kintone.mobile.app.record.setFieldShown("Lng", false);
    } else {
        if (kintone.app.record.getFieldElement("Lat") === "undefined") { return; }
        if (kintone.app.record.getFieldElement("Lng") === "undefined") { return; }
        if (kintone.app.record.getFieldElement("Title") === "undefined") { return; }
        if (kintone.app.record.getFieldElement("Address") === "undefined") { return; }

        kintone.app.record.setFieldShown("Lat", false);
        kintone.app.record.setFieldShown("Lng", false);
     }
     event.record["Title"]["disabled"] = true;
     event.record["Address"]["disabled"] = true;
};

```
> フィールドの表示、非表示、非アクティブ化。
> スマートフォン用のJavaScriptでは要素が取れないのでPC用のみでエラーチェック。

## Point.3

```js:sample-osmpoint.js
// OpenStreetMap and OpenLayers
var map = new ol.Map({
    target: 'map',
    layers: [
        new ol.layer.Tile({
            source: new ol.source.OSM()
        })
    ],
    view: new ol.View({
        center: ol.proj.fromLonLat([lng, lat]),
        zoom: osmPosition.defZoom
    })
});

// Append Overlay
var pos = ol.proj.transform([lng, lat], 'EPSG:4326', 'EPSG:3857');
// Marker pin
var imgElement = document.createElement('img');
var imgSrc = 'data:image/png;base64,base64 imagedata';
imgElement.setAttribute('src', imgSrc);

var marker = new ol.Overlay({
    position: pos,
    element: imgElement,
    stopEvent: false,
    positioning: 'center-center'
});
map.addOverlay(marker);

```
> 中心部の緯度経度、縮尺を指定して地図を取得
> pin画像をレイヤ追加

## Point.4

```js:sample-osmpoint.js
// kintone promise
return new kintone.Promise(function(resolve, reject) {
    // geolocation API
    navigator.geolocation.getCurrentPosition(function(position) {

        event.record["Lat"]["value"] = position.coords.latitude.toFixed(7);
        event.record["Lng"]["value"] = position.coords.longitude.toFixed(7);

        var url = "https://nominatim.openstreetmap.org/reverse?format=json";
        url += "&lat=" + event.record["Lat"]["value"];
        url += "&lon=" + event.record["Lng"]["value"];

        // Get address by Re-Geocording
        kintone.proxy(url, 'GET', {}, {}).then(function(args) {
            var resp = JSON.parse(args[0]);
            var respValue = {
                state: resp.address.state ? resp.address.state : "",
                city: resp.address.city ? resp.address.city : "",
                traffic_signals: resp.address.traffic_signals ? resp.address.traffic_signals : "",
                country: resp.address.country ? resp.address.country : ""
            };
            event.record["Address"]["value"] =
            respValue.state + respValue.city + respValue.traffic_signals + "(" + respValue.country + ")";

            resolve(event);
        }, function(error) {
            console.log(error);
            reject(event);
        });

    }, function(error) {
        // error event
        reject(event);
    });
});
```
> レコード保存時に位置情報を取得し、住所を取得
> ※位置情報は詳細特定をされないように桁指定
> kintone.Promise, kintone.proxyを利用して外部サービスを実行

# 今後やってみたいこと
- スタッフの最新情報を表示
- トレース
- 地図に関するiPhoneAppを作ってみたい