HERE Mapsとオープンデータでコロプレスマップを描いてみた

:::note info
この記事は [【HERE WeGo!】ジオファン集まれ！地理空間情報、地図に関する記事を募集しています by HERE Advent Calendar 2022](https://qiita.com/advent-calendar/2022/here) 21日目の記事です。
:::

# はじめに

HERE社が提供するAPIを使って、こんな感じの色分けWEB地図を作ってみました。人口が多い場所は濃く、人口が少ない場所は薄く表示しています。

[実際の動作ページ](https://hajime-miyauchi.github.io/here-api-test/)
※HERE APIの無料枠の利用上限を超えた場合は表示されません。その場合は翌日とかにアクセスしてみてください。

![スクリーンショット 2022-12-18 14.02.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/d06cd106-c262-3c17-ff3f-29c8160498ff.png)

このように、人口などの数値に応じて色分けを行う地図のことを「コロプレスマップ」と言います。(この例では人口の多寡を濃淡で表現しています)

無料で使える[e-stat](https://www.e-stat.go.jp/)というサイトでオープンデータとして公開されている国勢調査のデータを使い、表示にはHERE社が提供する[Maps API for JavaScript](https://developer.here.com/develop/javascript-api)を使っています。

HEREの優れたAPIのおかげで、書いたコードはHTML、CSS、JavaScriptを合わせても100行に満たない程度です。
たったそれだけのコードで上記のようなコロプレスマップをブラウザ上に描くことができます。

# 目次

* データを用意する
* コードを書く

# データを用意する

まずはデータを用意しましょう。ステップは２つです。

* データをダウンロードする
* データをWEBで扱いやすいGeoJSONというデータ形式に変換する

## データをダウンロードする

今回使用したのは、「令和２年国勢調査町丁・字等別境界データ」の中でも、Shapefile形式で測地系・座標系が世界測地系緯度経度の、愛知県全域版のデータになります。
地域は愛知県に限らずどこでも構いませんが、Shapefile形式で世界測地系緯度経度のものを選択する前提で以降の手順を進めます。

まずは[e-stat](https://www.e-stat.go.jp/)にアクセスしましょう。トップページの「地図」というアイコンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/2fe47d10-e6b7-83c8-5809-1a0a568e70c2.png)

次に「境界データダウンロード」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/e4a9e523-68dd-e6d3-310b-c6af79de151d.png)

次に「小地域」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/7741cdf8-75fa-ebb2-07d3-944ab2bbb423.png)

今回は人口データで色分けを行うので「国勢調査」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/82273011-2abe-f81e-1584-876e3ba05f02.png)

次に「2020年」の「小地域(町丁・字等別)(JGD2000)」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/c8e32ca3-b0b5-f190-b1be-643729dd1a16.png)

ここでいくつかの形式が選択できますが、「世界測地系緯度経度・Shapefile」を選択しましょう。
位置情報データの取り扱いになれていない人はいくつも形式が選べて混乱するかもしれませんね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/58d8f952-1814-c840-e531-9a234fe3fd44.png)

次に、都道府県を選択します。私は愛知県を選択しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/199ed4a4-cb6e-506a-0d49-53674f898ce4.png)

さらに細かいエリアの選択ができますが、私は愛知県全域のデータをダウンロードしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/a00717f8-e885-9f6b-1578-0c10c5bb8364.png)

私と同じ愛知県全域を選択した人は「A002005212020DDSWC23.zip」というファイル名のデータがダウンロードできたはずです。
**ZIPファイルを解凍しておきましょう。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/edb96053-4c64-e800-4ca1-7e43e855f4ec.png)


## データをGeoJSON形式に変換する

さて、ここまででShapefile形式のデータがダウンロードできました。
このShapefile形式のデータはWEBでは取り扱いに向いていないので、よりWEBと親和性の高い[GeoJSON](https://geojson.org/)というデータ形式に変換する必要があります。

余談ですが、**Shapefileは複数ファイルからなるファイル形式**で、同じフォルダ内に4つまたはそれ以上のファイルが存在しており、基本的にはいずれも必要です。
「.shp」ファイルだけ解凍しても開くことができませんので注意してください。

Shapefile形式からGeoJSON形式への変換方法はいくつかありますが、今回はQGISというオープンソースのデスクトップGISを使う方法を紹介します。

まずは[QGIS](https://www.qgis.org/)のサイトからQGISをダウンロードしてインストールしておきましょう。

本記事執筆時点のMac版のQIGS最新版は3.28ですが、私は安定版(LTR=Long Term Release)版である3.22を使用しています。
Windows版や、最新版をお使いの方もおそらく少し画面が異なる程度ではないかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/5a70811e-545d-be86-5248-fbef523ac368.png)

インストールが終わったら、さきほどダウンロードして解凍したデータのうち「shp」という拡張子のファイル(愛知県の場合は「r2ka23.shp」というファイル名)をQGISで開いてみてください。

開き方はいくつかありますが、どれでも良いです。

* QGISを立ち上げて、shpファイルをQGISにドラッグアンドドロップする。
* .shpファイルを右クリックしてQGISで開く。
* QGISのメニューから「レイヤ」-「レイヤを追加」-「ベクタレイヤを追加」で.shpファイルを選択する。

いずれかの方法で読み込みが成功すると、このような画面になると思います。(色は勝手に選択されるので異なると思います)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/5604c2ab-6aad-dd54-6e78-18048430d53e.png)

Shapefileの読み込みが完了したら、いよいよ変換です。
読み込んだShapefileはレイヤとして表示されていると思いますので、そのレイヤ(１枚しかないと思います)を右クリックして「エクスポート」から「地物の保存...」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/2a123326-ef3f-7d44-3420-3226f53f85ab.png)

はい、ではデータ形式を「GeoJSON」にして、ファイル名を選び、「OK」ボタンをクリックしましょう。これでGeoJSONデータへの変換は完了です。
変換されたファイルのファイル名は私の場合「**aichi.geojson**」としました。
違うファイル名にした場合は、後で出てくるコードの変更が必要になるので覚えておいてくださいね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/27271397-de5f-fd95-c5d2-8e4846893041.png)

基本的には細かい項目はデフォルトのままで良いはずですが、もし、座標参照系が「EPSG4612」になっていないようであれば変更してください。

テキストエディタなどでGeoJSONデータを開いてみると、このようにJSON形式のデータになっていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/f961d1bc-ac4a-0d44-5a64-59cdecd85e51.png)

色分けするための人口データも「JINKO」という項目が入っていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/0eec7544-f64f-a0de-e642-8538e6e3b43a.png)

また、境界データを描くためのポリゴンデータも緯度経度の連続として格納されています。数字の羅列だとどんな形(ポリゴン)なのか全くわかりませんね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/b6370c12-cf04-9787-7ca6-c12a68ba6b3d.png)


# コードを書く

さて、ようやくデータの準備ができたので表示するためのコードを書きましょう。

同じディレクトリに下記のファイルを用意します。

* aichi.geojson
* index.html
* demo.js
* template.css

HTMLとCSSとJavaScriptがひとつづつ、後は先ほど準備したGeoJSONという4つです。
aichi.geojsonはさきほど準備したものなのでHTMLとCSSとJavaScriptを以下に示します。

まずはindex.htmlから。どシンプルです。特に解説すべきところはありません。いくつかHEREのAPIに必要なCSS、JSを読み込んでいるくらいです。

ここで重要なのは、「map」というidを指定したdivに地図を描画するということと、「demo.js」という地図を表示するためのjs(後述)を読み込んでいる点くらいでしょうか。

```index.html
<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes">
    <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
    <title>HEREでコロプレスマップを描く</title>
    <link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css" />
    <link rel="stylesheet" type="text/css" href="template.css" />
    <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-core.js"></script>
    <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-service.js"></script>
    <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-ui.js"></script>
    <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"></script>
    <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-data.js"></script>
  </head>
  <body id="markers-on-the-map">
    <div id="map"></div>
    <script type="text/javascript" src='demo.js'></script>
  </body>
</html>
```

次にCSSファイルです。これまたシンプルです。
しいて解説すると、前述した通りmapのdivに地図を描画するので、高さと幅を100%にして**画面全体に地図を描画される**ようにしています。

```template.css
body {
    margin: 0;
    padding: 0;
}

#map {
    height: 100vh;
    width: 100vw;
}
```

最後に最も重要なJavaScriptです。
といっても、実質20行程度のコードです。
これだけでコロプレスマップが描画されます。

ここで重要なのは、```{YOUR_API_KEY```という箇所です。
この部分はあなたのアカウントのHEREアカウントのAPIキーに書き換える必要があります。

このAPIキーの発行方法や、そもそもアカウントの登録方法については、@asahina の[「【HERE】Maps API for JavaScriptを試してみた！」という記事](https://qiita.com/asahina820/items/5e28cb30a3d0a89438e4) で大変丁寧にまとめられています。
ぜひそちらを参照してください。

```demo.js
// {YOUR_API_KEY}の部分はあなたのHEREアカウントのAPIキーに書き換える必要があります。
var apikey = '{YOUR_API_KEY}';

// GeoJSONデータを読み込んで色分けして表示する
function showGeoJSONData (map) {
  var reader = new H.data.geojson.Reader('aichi.geojson', {
    style: function (mapObject) {
      if (mapObject instanceof H.map.Polygon) {
        // ここの2000の値はエリアによってうまく調整してください。
        var opacity = 1.0 / 2000 * parseInt(mapObject.data.JINKO, 0);
        
        mapObject.setStyle({
          fillColor: 'rgba(255, 0, 0, ' + opacity + ')',
          strokeColor: 'rgba(255, 0, 0, 0.5)',
          lineWidth: 1
        });
      }
    }, disableLegacyMode: true // <- これ結構重要
  });

  reader.parse();

  map.addLayer(reader.getLayer());
}

var platform = new H.service.Platform({
  apikey: window.apikey
});
var defaultLayers = platform.createDefaultLayers();

var map = new H.Map(document.getElementById('map'),
  defaultLayers.vector.normal.map, {
  // 愛知県以外の場所にしたい場合はこの緯度経度書き換えてくださいな
  center: {lat:35.18028, lng:136.90667},
  zoom: 13,
  pixelRatio: window.devicePixelRatio || 1
});
window.addEventListener('resize', () => map.getViewPort().resize());

var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));

var ui = H.ui.UI.createDefault(map, defaultLayers);

showGeoJSONData(map);
```

上記、4つのファイルを**WEBサーバにアップロードしたら**コロプレスマップが表示されます。
**ローカル環境ではindex.htmlをブラウザで表示してもコロプレスマップは表示されません。**
GeoJSON形式を読み込むためのXMLHttpRequestが動作しないことが要因です。
WEBサーバが必要となります。

私はVisual Studio CodeでWEBサーバが立ち上がる[Live Preview](https://marketplace.visualstudio.com/items?itemName=ms-vscode.live-server)という拡張機能を使いました。とても便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22360/cbef5a97-88f4-d2df-604d-72127a899564.png)

ただ、コードを書き換えるたびに地図描画が走る、つまりAPIを叩いているため、APIの利用上限にひっかかからないかヒヤヒヤしますが。

# 最後に

今回は人口の多寡によって濃淡を分けましたが、本来は人口密度によって濃淡を分けるべきです。(広いエリアは人口が多いのがあたりまえなので)
人口密度の場合は以下のコードで解説したような計算式で透明度を算出することもできますので参考にしてください。

https://github.com/hajime-miyauchi/webgis-hands-on/blob/master/postgis/05.md

また、GoeJSONを読み込むコードはHERE社のGitHubのサンプルコードを参考にさせてもらいました。

https://github.com/heremaps/maps-api-for-javascript-examples/tree/master/display-geojson-on-map

HEREのAPIはGeoJSONの読み込みも簡単且つ柔軟で、自身で書くコードは最小限で済みました。
他のアドベントカレンダーの記事も興味深いものが多いので、ぜひ他の記事も読んでみてくださいな。

https://qiita.com/advent-calendar/2022/here

ではでは、皆様良いお年を。