D3.js로 도도부현별 지도 그리기

16174 단어 d3.jsSVG
이전에 일본지도를 그렸습니다. 하지만 이번에는 시구정촌(행정구역)으로 나뉘어진 도도부현의 지도를 그려보겠습니다.

시구정촌 레벨의 지도 데이터는 국토 지리원의 지구지도 이나 국토 교통성의 국토 수치 정보 에서 다운로드할 수 있습니다.
국토지리원의 데이터는, 정령 지정 도시(삿포로시라든지)의 구가 알려지지 않고, 시구정촌명도 로마자 표기였으므로, 국토 교통성의 국토 수치 정보를 사용하기로 했습니다.

행정구역 데이터의 전국을 선택하여 다운로드합니다. (도도부현별의 파일도 있습니다만, 귀찮아서 전국 데이터로부터 각 도도부현 데이터를 추출합니다.)N03-160101_GML.zip 라는 파일이 다운로드되므로 (엄청 시간이 걸렸지만), 압축을 풀고 N03-16_160101.shp 라는 파일을 사용합니다.

extract_pref.sh
#! /bin/sh

for i in `seq 1 47`
do
  if [ $i -lt 10 ]; then
    i=0$i
  fi
  ogr2ogr -f GeoJSON -where "N03_007 like '$i%'" pref/ward.json N03-16_160101.shp
  topojson -s 0.000000001 -p N03_003 -p N03_004 -p N03_007 -o pref/pref$i.json pref/ward.json
  rm pref/ward.json
done

이런 식으로 도도부현별 TopoJSON 파일을 47개 만듭니다.
덧붙여서 N03_003과 N03_004에 이름이, N03_007에 시구정촌 코드가 들어가 있습니다.
그대로는 너무 세세한 (파일이 커져 표시에 시간이 걸린다) 때문에, -s 옵션으로 조금 거칠게 하고 있습니다.

pref.html
<!DOCTYPE html>
    <html lang="ja">
      <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <style type="text/css">
        .ward {
            fill: #fff;
            stroke: #aaa;
        }
        svg {
            background: #eff;
            padding: 40px;
        }
        </style>
      </head>
      <body>
        <script src="http://d3js.org/d3.v4.min.js"></script>
        <script src="http://d3js.org/topojson.v2.min.js"></script>
        <script type="text/javascript">
        var pref_code = location.search.match(/code=([0-9]+?)(&|$)/);
        if (pref_code) {
            pref_code = pref_code[1];
        } else {
            pref_code = '01';
        }

        var width = 960,
        height = 640,
        padding = 40;

        var svg = d3.select("body").append("svg")
            .attr("width", width)
            .attr("height", height);

        var projection = d3.geoMercator()
            .translate([width / 2, height / 2]);

        var path = d3.geoPath()
            .projection(projection);

        d3.json("pref/pref" + pref_code + ".json", function(error, pref) {
            var topo = topojson.feature(pref, pref.objects.ward);
            projection
                .center(d3.geoCentroid(topo))
                .fitSize([width, height], topo);
            svg.selectAll(".ward")
                .data(topo.features)
                .enter()
                .append("path")
                .attr("class", function(d) {
                    return "ward ward-" + d.properties.N03_007;
                })
                .attr("d", path)
                .on("mouseover", function(d) {
                    d3.selectAll(".ward-" + d.properties.N03_007).style("fill", "#f99");
                    var label = d.properties.N03_003 ? d.properties.N03_003 : '';
                    label += d.properties.N03_004 ? d.properties.N03_004 : '';
                    svg.append("text")
                        .attr("x", d3.event.offsetX - padding - 20)
                        .attr("y", d3.event.offsetY - padding - 15)
                        .attr("class", "ward-label")
                        .text(label);
                })
                .on("mouseout", function(d) {
                    d3.selectAll(".ward-" + d.properties.N03_007).style("fill", "#fff");
                    svg.select(".ward-label").remove();
                });
        });
        </script>
      </body>
</html>

d3.js 버전 4라면 projection.fitSize() 에서 좋은 느낌의 scale로 해주는 것이 기쁘다! (scale을 어떻게 계산하면 좋을지 모르고 좌절했다…)
pref.html?code=01 와 같이 액세스하면 홋카이도의 지도가 표시되고, 마우스 오버로 시구정촌명이 나옵니다.


padding이 js로 잡히지 않아서 두 번 쓰고 있는 것이 이마이치입니다만…

좋은 웹페이지 즐겨찾기