技術日誌

野球関係のサービスを個人開発しています。

D3.jsで日本地図に地点をプロットしてツールチップを表示してみる

作ったもの

キャップ野球チームの全国分布図を作成。

  • チームカラー
  • (あれば)チーム旗

f:id:ckoshien:20200127000421j:plain https://cap-baseball.com/map

動作イメージ

地図データの準備

地図データはshapeファイルで提供されているものをGeoJSON→topojson化する。

npm i topojson -g
$ 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
# ogr2ogrが含まれているgdalをインストールする
$ brew install gdal
# GeoJSONから日本のデータのみ抽出してtopoJson化する
$ ogr2ogr -f GeoJSON -where "adm0_a3 = 'JPN'" ne_pref_japan_geo.json ne_10m_admin_1_states_provinces.shp

CRAでd3を扱う

準備

# d3とtopojson-clientをインストールする
$ npm i --save d3 topojson-client

reactでzoom機能を扱う

コンストラクタでzoom機能を定義する

this.zoom = d3
      .zoom()
      .scaleExtent([-5, Infinity])
      .translateExtent([
        [-100, -100],
        [200, 300]
      ])
      .extent([
        [0, 0],
        [0, 0]
      ])
      .on("zoom", this.zoomed.bind(this));

zoomしたときに再描画を発火させるのがミソ。

zoomed() {
    this.setState({
      zoomTransform: d3.event.transform
    });
  }

点をプロットする

プロットする点のデータ構造

{
        name:"学習院大",
        y:35.71,
        x:139.7,
        teamId: 73,
        color:"lightblue"
    },

データから円を書く

svg
      .append("g")
      .attr("id", "circle-g")
      .selectAll("circle")
      .data(teamInfo)
      .enter()
      .append("circle");

円を塗りつぶす

データで定義した色で塗りつぶします。 マウスオーバーしたときにツールチップに表示する情報や、 表示の切り替えなどもここで記述しています。

    svg
      .selectAll("#circle-g circle")
      .attr("fill", (d)=>{
        return (d.color !== undefined ? d.color :'gray')
      })
      //.attr("opacity", 0.5)
      .attr("transform", function(d) {
        var coord = projection([d.x, d.y]);
        return "translate(" + coord.join(",") + ")";
      })
      .on("mouseover", function(d) {
          //console.log(d.name);
          tooltip
          .style("display", "block")
          .html(
              `<div>
              <h1>${d.name}</h1>
              <div
                style={{
                    display: flex,
                    margin: 10px
                }}
              >
              <img src="/images/${d.teamId}.jpg" width="100" />
              
              </div>
              </div>`
              )
        
      })
      .attr("r", 5 / (d3.event !== null ? d3.event.transform.k : 1));

参考