JavaScript
React

SPA経県マップをつくろう (1) ~地方ごとに色わけできるところまで

はじめに

経県マップとは...
は、いろんなサイトで紹介されておりますから、検索してみてくださいまし(^ ^;
いつもこちら経県値&経県マップで更新していたんですが、flash なようでスマホからみれなかったり(アプリはあったりしますが)、仕事場でみれなかったり、firefox で打ち切られたりして段々みれなくなってきてるんですよね。
昨年丁度九州いったので、残り1つってところだったのでいい機会と思いSPAで作成していこうと思いました。

のんびりつくってくところを残しておこうと思います。
ちなみにちゃいの経県値

ちゃい経県値.PNG

こんな感じですね。
今回は、地図を地方単位に色塗りをするところまでを作ります。

環境

OS: Windows7,10 & CentOS 7.2(VM)
node: v8.9.4

VirtualBoxにOS入れたのを思い出したのでそこに一から環境を作っていきました。Windowsも併用でごちゃごちゃですみません。

アプリ作成

前にみたD3.jsで日本地図を描き、都道府県別に色を塗るD3.jsでヒートマップっぽいものを作ってみたこちらがほぼやりたいことをしているので参考にしていきます。

日本地図データを作成

ツールのインストールも参考してますが、そこは Windowsでやったので gdal は http://www.gisinternals.com/release.php こちらから頂いてきました。
まずは topojson は新しいのでやってくのでバージョン指定しないでインストールします。

$ npm i -g topojson

都道府県地図データをダウンロードしてから、軽いtopojsonにします。
適当なディレクトリでこんな感じで topojson 作成します。

# zipげっと!
$ wget http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_1_states_provinces.zip
# zip解凍
$ unzip ne_10m_admin_1_states_provinces.zip
# 日本抜粋(ne_pref_japan_geo.json作成)
$ ogr2ogr -f GeoJSON -where "adm0_a3 = 'JPN'" ne_pref_japan_geo.json ne_10m_admin_1_states_provinces.shp
# 上のリンクから何故か静岡は null になってるので補完してから topojson へ
$ sed -e 's/"name_local": null,/"name_local": "静岡県",/' < ne_pref_japan_geo.json | geo2topo -o japan_geo2topo.json

topojsonできました。まずは今回は地方単位に色分けしできてから県の色分けにいきたいと思いますので、地方が入っていそうな region を grep してみました。

jq . < japan_geo2topo.json | grep -w region | sort | uniq
"region": "Chubu",
"region": "Chugoku",
"region": "Hokkaido",
"region": "Kanto",
"region": "Kinki",
"region": "Kyushu",
"region": "Okinawa",
"region": "Shikoku",
"region": "Tohoku",
"region": null,

むむ?ここでもどこか null になってますね。どこの県かを探してみます。

$ cat japan_geo2topo.json | jq .objects.'"-"'.geometries[].properties" | select(.region == null) | .name_local,.region"
"佐賀県"
null
"長崎県"
null

むむぅ(- -;昨年いった長崎と佐賀なの?何地方なんでしょうね。
まー色分けしてみればわかるからいいかぁって感じで進みます。

アプリの作成

今回は create-react-app を使って1からアプリを作成しましょう。

# create-react-appインストール
$ npm i -g create-react-app
# 経県値project作成
$ create-react-app keikenti
$ ls -1 keikenti
README.md
node_modules
package-lock.json
package.json
public
src

これでアプリの骨組みできました!
先程作成した japan_geo2topo.json は src 直下へいれます。

つくりながらやってくので、まずは

  • ライブラリ追加
  • Mapコンポーネントを作成し地図を色分けして表示する
  • index.css に地方ごとの色を定義する
  • App.js で Mapコンポーネントを使うようにしてみる。

こんな感じで色ぬりぬりしていきましょう。

ライブラリ追加

d3を使って絵を書くので d3 を入れます。topojson-clientも使うのでいれておきます。
d3は一年ぶりにつかうので思い出しながらやってます(笑)

$ npm i --save d3
$ npm i --save topojson-client

色定義追加

地方名を小文字にしたものをクラス名にして定義しておきます。
順序は適当です。

index.css
.region {
  fill: #fff;
  stroke: #aaa;
}

.hokkaido {
  fill: #0000ff;
}

.chubu {
  fill: #ff0000;
}

.chugoku {
  fill: #00ff00;
}

.kanto {
  fill: #00ffff;
}

.kinki {
  fill: #FFD700;
}

.kyushu {
  fill: #4169E1;
}

.okinawa {
  fill: #FFC0CB;
}

.shikoku {
  fill: #FFA07A;
}

.tohoku {
  fill: #FF4500;
}

地図クラス作成

先人のちえをいただいてまずは描画できるものをつくります。
React と d3 を合わせて使うには DOM がちゃんと出来てからではならないので、componentDidMount, componentDidUpdate で描画するようにしておきます(毎回一から描画は無駄なのであとで修正予定とします)。探せば react と d3 をうまく繋いでくれるのもあるのかもです。

Map.js
import React, { Component } from 'react';
import * as topojson from 'topojson-client';
import * as d3 from 'd3';

// geo2topo output
import mapJson from './japan_geo2topo.json';

class Map extends Component {

  componentDidMount() {
    this.drawMap()
  }

  componentDidUpdate() {
    this.drawMap()
  }

  drawMap() {

    var w = 1300;
    var h = 960;
    // 地図の投影図法を設定する.
    var projection = d3.geoMercator()
        .center([136, 35.5])
        .scale(2000)
        .translate([w / 2, h / 2]);

    // GeoJSONからpath要素を作る.
    var path = d3.geoPath()
        .projection(projection);

    const jpn = mapJson;
    const geoJp = topojson.feature(jpn, jpn.objects['-']);
    console.log(geoJp);
    const svg = d3.select(this.node);
    svg.attr('height', h)
      .attr('width', w)
      .selectAll("path")
      .data(geoJp.features)
      .enter()
      .append("path")
      .attr("class", d => d.properties.region ? `region ${d.properties.region.toLowerCase()}` : 'region')
      .attr("d", path);
  }

  render() {
    return (
      <svg ref={node => this.node = node}>
      </svg>
    );
  }
}

export default Map;

AppでMap使う

App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Map from './Map'; // 追加

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
        <Map /> // 追加
      </div>
    );
  }
}

export default App;

アプリ起動

おきまりの npm start ですね。

react_region_color.png

いい感じに色が塗られたのが確認できるかと思います。
赤多いけどなんか色まちがえましたかね?
とりあえず佐賀と長崎も違ういろになっていたのでなんとかできたかと思います(^ ^)
次回県毎に色分けしてみます。