connpassからkintoneイベントを取得して、mapboxで地図表示してみる話🗺

こんばんは！Mikeiです^^
今日は[kinotne Advent Calendar 2017](https://qiita.com/advent-calendar/2017/kintone)の23日目Postです。

今年はconnpassからkintone関連イベントを取得して、mapboxで地図表示をするというカスタマイズにチャレンジしてみました！
Google mapだと、いざ運用に乗せようとした時にライセンス問題があり、なかなか思うように使えないですが、mapboxはある程度自由に使えそうなので、要チェックです。（[料金体系](https://www.mapbox.com/pricing/)を見ると、地図のビュー数が月々５万を超えなければ無料枠で大丈夫）
APIやサンプルや地図のスタイルも豊富で触っていてとてもワクワクします^^

# 📍完成イメージ
カスタマイズ内容と完成イメージはこちらです。

- connpassからkintoneというキーワードを含むイベントを取得して、kintoneにレコード追加
<img width="643" alt="5.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/3c8f7995-ca86-32a8-7eb8-c2e0b6266769.png">

- kintoneに登録されている位置情報から、mapboxを使ってカスタマイズビューに地図表示
 - 今月開催のイベントのみ色付け
<img width="1440" alt="3.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/9429ca13-6f22-d1e6-06dd-c476b63ae6c3.png">
 - 分布図チックに改造
<img width="1440" alt="4.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/427bde1d-cce9-1189-dedc-190a62470b40.png">

# 📍連携サービス紹介
カスタマイズの説明に移る前に、今回使った連携サービスの紹介です。
## connpassとは
[connpass](https://connpass.com)は、イベント情報を管理するサービスのことです。[kintone関連のイベント](https://connpass.com/search/?q=kintone)もよくこちらのサイトにアップされています。
APIリファレンスは[こちら](https://connpass.com/about/api/)です。
<img width="1199" alt="スクリーンショット 2017-12-20 16.00.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/ec9dc1c6-5650-de22-0f49-7816bf1914fa.png">


## mapboxとは
[mapbox](https://www.mapbox.com/)は、Webの地図サービスです。APIやSDKなども揃っているので、カスタマイズするにも持ってこいのサービス。
サンプル集や地図のスタイルが豊富なのも嬉しいポイントです。
詳しいドキュメントは[こちら](https://www.mapbox.com/developers/)になります。
<img width="1382" alt="スクリーンショット 2017-12-20 16.00.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/7a4e7c7b-25f4-e046-d6f4-94f808f19319.png">

# 📍準備するもの
- kintone環境(開発者ライセンスの取得は[こちら](https://developer.cybozu.io/hc/ja/articles/200929540#step3))
- mapboxアカウント(お持ちでない方は、[こちら](https://www.mapbox.com/)からサインアップ)
- テキストエディタ
- アプリテンプレート・ソースコード・スタイルのJSONファイル      
  https://github.com/TomokoMiyake/kintone-mapbox  

  ※githubにアップしているファイル群は右上の「Clone or download」から一括ダウンロードできます。
<img width="1018" alt="6.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/0ee1fd35-00f2-3cd9-6f06-01527a07ac08.png">

# 📍カスタマイズ手順
## kintoneアプリ作成
アプリテンプレートを[githubリポジトリ](https://github.com/TomokoMiyake/kintone-mapbox)に用意しているので、[こちら](https://jp.cybozu.help/ja/k/admin/import_template.html)の手順に沿って読み込んでください。
「イベント管理(connpass & mapbox連携).zip」というファイルです。

また、今回地図が見やすいように2パターンとも一覧画面や詳細画面ではなく、カスタマイズビューに設置しています。
カスタマイズビューには以下のHTMLを記述しています。

```html:カスタマイズビュー
<div id='map' style='width:100%; height:500px'></div>
```
※一覧名はソースコード内で指定しているので、変更すると地図が表示されなくります。

## mapboxへの地図スタイル読み込み
mapboxでは下のように様々なスタイルの地図を選択してカスタマイズすることができます。（[ギャラリー](https://www.mapbox.com/gallery/)見るだけでも楽しいですね♪）
他の地図サービスと違うのは、テンプレートから選べるだけでなく、自分で地図スタイルを作ることもできる点です。
<img width="489" alt="7.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/7aeede2c-dd70-62ad-4106-650e9db7c418.png">

今回は完成イメージにもあるようにLightとDarkというテンプレートのスタイルに修正を加えたので、それを読み込んで使います。
※mapboxでは残念ながら地図上の表記を全て日本語に設定することができません。初期設定では全て英語になっているので、各国の言語で表示されるように設定し直す必要があります。今回は私の方で直したものをアップしています。（具体的には、スタイル内のテキスト設定で「name_en」となっている箇所を全て「name」に編集します。）

1. mapboxにログイン
2. スタイルの作成
 - 「Create a style」をクリック
<img width="1359" alt="8.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/c99426cb-4500-ea6d-c530-b139e6884ac1.png">
 - ポップアップから「Upload a style」→「Select a file」の順にクリック
 - githubからダウンロードした「light_style.json」と「dark_style.json」をそれぞれアップロードして、「Create」ボタンをクリック     
<img width="617" alt="9.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/699cecd3-0c5d-1cc4-ee1e-28b610edd7df.png">
 - 読み込みが完了したらスタイル名をクリックして一覧に戻る
<img width="1435" alt="10.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/a91119e1-afd1-30b2-5df7-e51d5e176a06.png">
 - 出来上がったスタイルの右上の「Menu」ボタンから「Share, develop & use」をクリック
<img width="1335" alt="11.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/bb4b75a1-eac4-245d-196b-bf5c7ded3b2b.png">
 - 少し下にスクロールして出てくる「Style URL」と「Access Token」をメモ（kintoneに適用するJavaScriptファイルに転記します）     
<img width="425" alt="12.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/7552bd27-44d1-af02-db94-c53bb185ff17.png">
 - 「dark_style.json」も同様に読み込み完了後、「Style URL」と「Access Token」をメモ

## JavaScriptファイルの編集
1. githubからダウンロードした「map(要編集).js」ファイルをテキストエディタで開く
2. ソースコード最下部の「×××」4箇所を先ほどmapboxでのスタイル作成時にメモした内容に書き換えて、ファイルを保存

```javascript:map(要編集).js
// イベント処理 ※書き換え必要箇所
kintone.events.on('app.record.index.show', (event) => {
    const records = event.records;
    const appId = kintone.app.getId();
    const domain = '×××'; // kintoneのサブドメイン名を記入
    const token = '×××'; // mapboxで作成したStyleのAccess tokenを記入(lightもdarkも同じ)
    const styleStandard = 'mapbox://styles/×××/×××'; // mapboxで作成したlight styleのURLを記入
    const styleChoropleth = 'mapbox://styles/×××/×××'; // mapboxで作成したdark styleのURLを記入

    // connpassからのイベント取得
    getEvents();

    // mapboxで地図表示
    if (event.viewName === '地図') {
        showMap(records, appId, domain, token, styleStandard);
    }

    // mapboxでコロプレス地図表示
    if (event.viewName === '分布地図') {
        showChoroplethMap(records, appId, domain, token, styleChoropleth);
    }
});
```

## kintoneへの適用 & 動作確認
1. 保存したJavaScriptファイルを作成したkintoneアプリに適用（アプリへの適用方法は[こちら](https://jp.cybozu.help/ja/k/user/js_customize)）
 - 適用ファイルの構成が以下のようになっていることを確認して、設定を保存
<img width="564" alt="13.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/7ba0cc67-5562-ba85-bcf3-a02556067a33.png">     

2. アプリの一覧画面に移動して、動作確認
 - 4つの一覧画面（地図表示用の2つはカスタマイズビュー）
<img width="1432" alt="14.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/661fffc2-17aa-1f3d-52ef-d78cdbb48beb.png">
 - 「イベント一覧（リスト）」→カレンダーアイコンをクリックして、connpassから今日以降のイベントを取得
<img width="643" alt="5.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/3c8f7995-ca86-32a8-7eb8-c2e0b6266769.png">
   ※来年以降の予定があまり入っていないので、地図表示を楽しみたい方は、githubからダウンロードした「sample.csv」をkintoneに読み込んでから次に進んでください。
 - 「地図」→今月開催のイベントは赤印・ポップアップで詳細表示・今月のイベント数表示
<img width="1054" alt="15.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/0b45a77b-e3d5-73dc-cfba-3dd8f855ff70.png">
 - 「分布地図」→どの地域でのイベントが多いか丸印で可視化
<img width="1440" alt="16.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/01a1202d-9015-ff0c-b255-0c8a77bef9b9.png">

完成しましたでしょうか？
途中寄り道したくなる機能が沢山ありましたよね（笑）

# 📍妄想
mapboxはこれがきっかけで初めて触りましたが、工夫したい欲が湧いてくるツールでした^^
今回ここまでできなかったのですが、できたら実用的だなーと思います。（冬休みの課題にいかがですか^^;）

- BIツール化(kintoneの導入社数の分布図、kintone Caféの開催マップ)
- 旅の記録
- バスなどの運行状況マップ

分布図系は、こんなイメージ↓
<img width="724" alt="17.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/7494fac4-d022-e535-6816-e12f4c677586.png">
https://blog.mapbox.com/introducing-data-driven-styling-in-mapbox-gl-js-f273121143c3

<img width="753" alt="18.png" src="https://qiita-image-store.s3.amazonaws.com/0/90265/84a38bcd-eaff-56ab-a9ef-4b304f9b84c1.png">
https://www.mapbox.com/mapbox-gl-js/example/updating-choropleth/


# 📍カスタマイズのポイント解説
カスタマイズ部分が気になる方はこちらもよければご覧ください。

## 利用したAPI
- [kintone API](https://developer.cybozu.io/hc/ja/articles/201941754)
- [connpass API](https://connpass.com/about/api/)
- [mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/api/)

## プログラム
https://github.com/TomokoMiyake/kintone-mapbox

## ポイント１：reduce関数を使う
Promiseの順番処理を行うためにreduce関数を使います。

```javascript:map(要編集).js
// kintoneへ登録用のデータ配列作成
let post = [];
return body.reduce((promise, formatedData) => {
    return promise.then(() => {
        const getRecords = {
            'app': kintone.app.getId(),
            'query': 'url =' + '"' + formatedData.event_url + '"'
        };
        return kintone.api(kintone.api.url('/k/v1/records', true), 'GET', getRecords).then((getResp) => {
            if (getResp.records.length === 0) {
                const records = {
                    'title': {
                        'value': formatedData.title
                    },
                    'start': {
                        'value': formatedData.started_at
                    },
                    'end': {
                        'value': formatedData.ended_at
                    },
                    'url': {
                        'value': formatedData.event_url
                    },
                    'address': {
                        'value': formatedData.address
                    },
                    'place': {
                        'value': formatedData.place
                    },
                    'lat': {
                        'value': formatedData.lat
                    },
                    'lon': {
                        'value': formatedData.lon
                    }
                };
                post.push(records);
                return post;
            }
        });
    });
}, Promise.resolve()).then(() => {
    ////// 省略 //////
});
```

## ポイント２：地図データのマッピング
中心の座標の指定やkitnoneアプリ内の座標データを地図データにプロットします。

```javascript:map(要編集).js
const map = new mapboxgl.Map({
    container: 'map',
    style: styleChoropleth,
    center: [137.6850225, 38.258595],
    zoom: 6
});

// 地図にピン打ちするデータ作成
const features = records.map((record) => {
    const link = 'https://' + domain + '.cybozu.com/k/' + appId + '/show#record=' + record.$id.value;

    const feature = {
        type: 'Feature',
        geometry: {
            type: 'Point',
            coordinates: [parseFloat(record.lon.value), parseFloat(record.lat.value)]
        },
        properties: {
            title: '<a href=' + link + '>' + record.title.value + '</a>',
            description: record.address.value
        }
    };
    return feature;
});
```

## ポイント３：マーカー（地図上のアイコン）のプロット
「地図」では条件付きで赤色と黒色のマーカーを分けているので、それ用に2つの要素を作成します。
それぞれのマーカーには緯度経度のデータやポップアップで表示する内容を指定します。

```javascript:map(要編集).js
// マーカーを地図に追加
geojson.features.forEach(function(marker) {
    if (isNaN(marker.geometry.coordinates[0]) || isNaN(marker.geometry.coordinates[1])) {
        return;
    }

    // HTML要素の作成
    const elThisMonth = document.createElement('div');
    const el = document.createElement('div');
    if (marker.properties.icon === 'thisMonth') {
        el.className = 'marker-this-month';
    } else if (marker.properties.icon === 'notThisMonth') {
        el.className = 'marker';
    }
    // マーカー要素を作成
    new mapboxgl.Marker(elThisMonth)
        .setLngLat(marker.geometry.coordinates)
        .setPopup(new mapboxgl.Popup({ offset: 25 })
        .setHTML('<h3><b>' + marker.properties.title + '</b></h3><p>' + marker.properties.description + '</p>'))
        .addTo(map);

    new mapboxgl.Marker(el)
        .setLngLat(marker.geometry.coordinates)
        .setPopup(new mapboxgl.Popup({ offset: 25 })
        .setHTML('<h3><b>' + marker.properties.title + '</b></h3><p>' + marker.properties.description + '</p>'))
        .addTo(map);
});
```

JavaScriptで生成したクラスにCSS側でFontAwesomeをアイコンとしてあてます。

```css:map.css
.marker:before {
    font-family: FontAwesome;
    content: '\f041';
    color: #585858;
    font-size: 2em;
    text-shadow: 0px 0px 1px #FFFFFF;
}
```

# 📍おわりに
最後まで読んでくださりありがとうございました！間違ってるところがあればご指摘いただけると嬉しいですm(__)m
では皆さん、Merry Christmas and Happy New Year!!!🎄