📌

Next.jsとdeck.glで国土交通省の行政区データを魅力的に可視化する方法

2023/08/11に公開

仕事がら位置情報を扱うことが多く、kepler.glを使って地図に可視化することが多いのですが、今回はNext.jsとdeck.glを使って国土地理院の行政区データを可視化してみました。Next.jsもdeck.glも普段から頻繁に触っているわけではないので間違いがあればご指摘のほどよろしくお願いいたします。

前提

主要なライブラリのバージョンです。

  • Next.js: 13.4.13
  • deck.gl: 8.9.23
  • react-map-gl: 7.1.3

作成したもの

openstreetmap上にgeojsonデータの東京都の行政区を表示してみました。下記のようになります。品川区にもうひとうのPOLYGONレイヤーをのせています。
行政区のデータは国土交通省からダウンロードしました。

作成手順

Next.jsのプロジェクトを作成します。ここはたくさん情報があるので割愛します。下記のようなコマンドでプロジェクトを作成してください。

npx create-next-app@latest deckgl-practice

deck.glインストール

プロジェクトのルートディレクトリで下記のコマンドを実行してください。react-map-glはベースとなる地図を表示するために使用します。
インストールについての詳細はドキュメントに記載があります。
deck.gl Get Started
react-map-gl Get Started

yarn add deck.gl react-map-gl mapbox-gl maplibre-gl @types/mapbox-gl

geojsonダウンロード

国土交通省のデータをダウンロードしてください。東京はN03-20230101_13_GML.zipになります。
解凍するとshapeファイルなどもありますが、N03-23_13_230101.geojsonをプロジェクトのpublicの中に配置してください。
ファイル名がわかりづらいのでtokyo.geojsonにリネームしておきます。

マップコンポーネントの作成

app/page.tsxを下記のように編集します。デフォルトの状態から全て削除しGeoJsonMapコンポーネントを追加しています。
この時点ではGeoJsonMap.tsxを作成していないのでエラーが出ます。

app/page.tsx
import GeoJsonMap from './GeoJsonMap'

const Home = async () => (
    <div style={{ height: '100vh' }}>
        <GeoJsonMap />
    </div>
)

export default Home

続いてapp/GeoJsonMap.tsxを作成します。deck.glでgeojsonファイルを可視化する際はGeoJsonLayerを使用します。設定できる項目はドキュメントに記載があります。

app/GeoJsonMap.tsx
'use client'

import { useState, useEffect } from 'react'
import Map from 'react-map-gl/maplibre'
import 'maplibre-gl/dist/maplibre-gl.css'
import DeckGL from '@deck.gl/react/typed'
import { GeoJsonLayer } from '@deck.gl/layers/typed'

const INITIAL_VIEW_STATE = {
    latitude: 35.68184623091793,
    longitude: 139.76729646137065,
    zoom: 12,
}

function renderTooltip(hoverInfo: any) {
    const { object, x, y } = hoverInfo
    if (!object) {
        return null
    }
    return (
        <div className="tooltip" style={{ position: "absolute", left: x, top: y }}>
            <big>test</big>
        </div>
    )
}

const GeoJsonMap = () => {
    const [viewState, setViewState] = useState(INITIAL_VIEW_STATE);
    const [data, setData] = useState([])
    const [hoverInfo, setHoverInfo] = useState({})
    useEffect(() => {
        fetch('/tokyo.geojson', { method: 'GET' })
            .then((res) => res.json())
            .then((data) => {
                console.log(data)
                setData(data)
            })
    }, [])

    const layer = new GeoJsonLayer({
        id: 'geojson-layer',
        data,
        filled: true,
        stroked: true,
        getLineWidth: 40,
        getLineColor: [255, 0, 0],
        getFillColor: [255, 255, 0, 50],
    })
    const polygon = {
        type: 'Feature',
        geometry: {
            type: 'Polygon',
            coordinates: [
                [
                    [139.7380683850485, 35.609983775090214],
                    [139.7323157802146, 35.6137715585613],
                    [139.73816346942544, 35.6200712501424],
                    [139.7465308946397, 35.620032603240034],
                    [139.7483850399991, 35.61748186642562],
                    [139.74905063064136, 35.612921255435],
                    [139.74781453373384, 35.60836038441386],
                    [139.7431553992402, 35.60863095181429],
                    [139.7380683850485, 35.609983775090214],
                ],
            ],
        },
    }

    const layer2 = new GeoJsonLayer({
        id: 'geojson-layer2',
        data: polygon,
        stroked: true,
        filled: true,
        lineWidthMinPixels: 2,
        getLineColor: [255, 0, 0, 255],
        getFillColor: [0, 255, 0, 200],
        pickable: true,
        onHover: setHoverInfo,
    })

    return (
        <DeckGL
            initialViewState={viewState}
            controller={true}
            layers={[layer, layer2]}
        >
            <Map
                mapStyle="https://tile.openstreetmap.jp/styles/osm-bright-ja/style.json"
                maplibreLogo
                styleDiffing
            />
            {renderTooltip(hoverInfo)}
        </DeckGL>
    )
}

export default GeoJsonMap

上から順に説明していきます。
'use client'でClient Componentsにしています。deck.glではSSRはできないみたいです。use clientはNext.js 13でリリースされた機能で、Client Componentsを使用するために必要です。Next.js 13ではuse clientで指定しないとデフォルトSSRになります。
必要なライブラリをimportします。INITIAL_VIEW_STATEでは地図の初期表示の位置をしていしています。今回は東京駅です。
const GeoJsonMapにdeck.glに関する内容を記載しています。useEffect内でgeojsonデータを取得しています。
GeoJsonLayerで今回可視化したいgeojsonについて設定しています。GeoJsonLayerに可視化したいデータをセットするだけになります。

const layer = new GeoJsonLayer({
        id: 'geojson-layer',
        data,
        filled: true,
        stroked: true,
        getLineWidth: 40,
        getLineColor: [255, 0, 0],
        getFillColor: [255, 255, 0, 50],
    })

<DeckGL>のlayersにさきほどのGeoJsonLayerを配列型式でいれることで可視化できます。layers={[layer, layer2]}の順番によってレイヤーの前後を変更できます。
<Map>のmapStyleに使用するベースマップを指定します。今回はopenstreetmapを利用していますが、MapboxやGoogle Mapsなども利用できます。
{renderTooltip(hoverInfo)}でツールチップを表示しています。

return (
        <DeckGL
            initialViewState={viewState}
            controller={true}
            layers={[layer, layer2]}
        >
            <Map
                mapStyle="https://tile.openstreetmap.jp/styles/osm-bright-ja/style.json"
                maplibreLogo
                styleDiffing
            />
            {renderTooltip(hoverInfo)}
        </DeckGL>
    )

まとめ

今回はdeck.glを利用してgeojsonを可視化する方法を紹介しました。deck.glは他にも様々な可視化ができるので、私もいろいろ試す予定です。

Discussion