0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

deck.glで地理座標を可視化する — 地理データを地図上に描くための最初のステップ

Posted at

はじめに

deck.glは、Uber社が開発したWebGLベースのオープンソース可視化フレームワークです。本記事では、deck.glを単体で利用し、地理座標の扱いと背景地図の重ね方の基本を試します。MapLibreやMapboxとの連携は別記事で扱おうと思います。

本記事のゴール

  • DeckGLの役割とレイヤ構成の理解
  • 緯度経度による地理的表現の理解
  • 背景地図を重ねるための基本方針の把握
  • 点群可視化(Scatterplot)

deck.glの基本構成を理解する

deck.glの基本的な考え方は「レイヤー(Layer)」を積み重ねて、地理情報やデータをWebGL上に描画するというものです。地図アプリケーションのような見た目をしていますが、deck.gl自体は地図タイルを提供するライブラリではなく、地理座標をもとに可視化を行うフレームワークです。

DeckGLの主要構成要素

  • DeckGLクラス
    全体の描画を管理する中核コンポーネント。ビュー状態(中心座標・ズームレベル・ピッチなど)やレイヤの配列を指定します。

  • Layer(レイヤ)
    実際に描画される単位。点、線、ポリゴン、タイル画像など、さまざまな種類のレイヤが用意されています。代表的なものに ScatterplotLayerGeoJsonLayerBitmapLayer などがあります。

  • ViewState(ビュー状態)
    地図の中心位置、ズーム、回転、傾きを指定するパラメータです。
    longitude(経度)、latitude(緯度)、zoom などを設定します。

地理座標を扱う仕組み

deck.glは内部的に地球座標系(WGS84)をサポートしており、longitudelatitude を指定すれば、その値に基づいて正しい位置関係でデータを描画します。
ただし、この時点では背景地図は描かれません。地理空間的な位置は保持されているが、地図のビジュアルが存在しない状態になります。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>deck.gl 基本構成(背景なし)</title>
    <script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
    <style>
      body {
        margin: 0;
        overflow: hidden;
      }
      #container {
        width: 100vw;
        height: 100vh;
      }
    </style>
  </head>
  <body>
    <div id="container"></div>

    <script>
      const { DeckGL, ScatterplotLayer } = deck;

      // ランダム座標(東京周辺)
      const points = Array.from({ length: 200 }, () => ({
        position: [
          139.7 + Math.random() * 0.4, // 経度
          35.5 + Math.random() * 0.4, // 緯度
        ],
        color: [Math.random() * 255, 140, 200],
        radius: 500,
      }));

      // DeckGLの初期化
      const deckgl = new DeckGL({
        container: "container",
        initialViewState: {
          longitude: 139.76,
          latitude: 35.68,
          zoom: 9,
          pitch: 0,
          bearing: 0,
        },
        controller: true,
        layers: [
          new ScatterplotLayer({
            id: "scatter-layer",
            data: points,
            getPosition: (d) => d.position,
            getFillColor: (d) => d.color,
            getRadius: (d) => d.radius,
            radiusScale: 1,
            radiusMinPixels: 2,
            radiusMaxPixels: 10,
            pickable: true,
            onHover: ({ object, x, y }) => {
              const el = document.getElementById("tooltip");
              if (object) {
                el.style.display = "block";
                el.style.left = `${x}px`;
                el.style.top = `${y}px`;
                el.innerHTML = `lon: ${object.position[0].toFixed(3)}<br>lat: ${object.position[1].toFixed(3)}`;
              } else {
                el.style.display = "none";
              }
            },
          }),
        ],
      });

      // 初期レンダリングを強制実行
      setTimeout(() => {
        deckgl.setProps({
          viewState: {
            longitude: 139.76,
            latitude: 35.68,
            zoom: 9,
            pitch: 0,
            bearing: 0,
          },
        });
      }, 100);

      // ホバー用ツールチップ
      const tooltip = document.createElement("div");
      tooltip.id = "tooltip";
      Object.assign(tooltip.style, {
        position: "absolute",
        background: "rgba(0,0,0,0.7)",
        color: "#fff",
        padding: "4px 8px",
        borderRadius: "4px",
        pointerEvents: "none",
        fontSize: "12px",
        display: "none",
      });
      document.body.appendChild(tooltip);
    </script>
  </body>
</html>

d231a2790cdb3797bc9-1.png

上のサンプルでは、DeckGL単体でランダムな座標点を描画しています。背景地図がないのは、地図画像を重ねていないためです。座標系は正しく維持されていますが、「背景地図レイヤ」を追加する必要があります。

DeckGLでは initialViewState が内部状態に反映されるタイミングが遅延することがあり、初期レンダリングが実行されない場合があります。
なので、初期化後に setProps でviewStateを明示的に設定しています。

背景地図を追加する — TileLayerとBitmapLayer

DeckGLは、外部の地図タイルを貼り付けるための TileLayerBitmapLayer を提供しています。
これらを利用することで、地理座標に基づいた背景地図を簡単に重ねることができます。

  • TileLayer
    タイル画像(例:https://.../{z}/{x}/{y}.png)を読み込むためのレイヤ

  • BitmapLayer
    読み込んだタイル画像を地理座標の範囲(bounds)に合わせて貼り付けるレイヤ

以下の例では、国土地理院が提供する標準地図タイルを使用しています。
DeckGL単体で背景地図を表示する構成として、最もシンプルな方法です。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8" />
  <title>deck.gl 基本構成サンプル</title>
  <script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
  <style>
    body { margin: 0; overflow: hidden; }
    #container { width: 100vw; height: 100vh; }
    .attribution {
      position: absolute; right: 8px; bottom: 8px;
      background: rgba(255,255,255,0.8);
      font: 12px/1.4 sans-serif; padding: 4px 6px; border-radius: 4px;
    }
  </style>
</head>
<body>
  <div id="container"></div>
  <div class="attribution">地理院タイル(標準地図) © 国土地理院</div>

  <script>
    const {DeckGL, TileLayer, BitmapLayer, ScatterplotLayer} = deck;

    // ランダム点(東京周辺)
    const points = Array.from({ length: 200 }, () => ({
      position: [139.7 + Math.random() * 0.4, 35.5 + Math.random() * 0.4],
      color: [Math.random() * 255, 140, 200],
      radius: 500
    }));

    // 国土地理院タイル(標準地図)
    const gsiUrl = 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png';

    const gsiTileLayer = new TileLayer({
      id: 'gsi-std',
      data: gsiUrl,
      minZoom: 3,
      maxZoom: 18,
      tileSize: 256,
      renderSubLayers: (props) => {
        const {west, south, east, north} = props.tile.bbox;
        return new BitmapLayer({
          id: `${props.id}-bitmap`,
          data: null,                // dataを渡すとエラーになるためnull
          image: props.data,         // フェッチ済みのタイル画像
          bounds: [west, south, east, north]
        });
      }
    });

    const pointsLayer = new ScatterplotLayer({
      id: 'scatter',
      data: points,
      getPosition: d => d.position,
      getFillColor: d => d.color,
      getRadius: d => d.radius,
      radiusScale: 1,
      radiusMinPixels: 2,
      radiusMaxPixels: 10,
      pickable: true,
      onHover: ({object, x, y}) => {
        const el = document.getElementById('tooltip');
        if (object) {
          el.style.display = 'block';
          el.style.left = `${x}px`;
          el.style.top = `${y}px`;
          el.innerHTML =
            `lon: ${object.position[0].toFixed(3)}<br>lat: ${object.position[1].toFixed(3)}`;
        } else {
          el.style.display = 'none';
        }
      }
    });

    const deckgl = new DeckGL({
      container: 'container',
      initialViewState: {
        longitude: 139.76,
        latitude: 35.68,
        zoom: 10,
        pitch: 0,
        bearing: 0
      },
      controller: true,
      layers: [gsiTileLayer, pointsLayer]
    });

    // ホバー用ツールチップ
    const tooltip = document.createElement('div');
    tooltip.id = 'tooltip';
    Object.assign(tooltip.style, {
      position: 'absolute',
      background: 'rgba(0,0,0,0.7)',
      color: '#fff',
      padding: '4px 8px',
      borderRadius: '4px',
      pointerEvents: 'none',
      fontSize: '12px',
      display: 'none'
    });
    document.body.appendChild(tooltip);
  </script>
</body>
</html>

d231a2790cdb3797bc9-2.png

国土地理院タイルを利用する場合は、右下などにクレジット表記「地理院タイル © 国土地理院」を追加してください。
また、renderSubLayersprops をそのまま渡すとエラーが発生するため、BitmapLayer には必要なプロパティのみを明示的に指定します。

DeckGL単体で地理座標を扱いながら、TileLayerを利用して外部タイルを重ねることで、地図上にデータを自然に表現できます。

背景タイルを切り替える

DeckGLは地図タイルを内部に持たないため、TileLayerdata プロパティに指定するURLを変更するだけで、さまざまな地図タイルを利用できます。
国土地理院の標準地図だけでなく、OpenStreetMapや他の公開タイルサービスも簡単に差し替えが可能です。

例えば、次のようにURLを置き換えることで、背景地図をOpenStreetMapに変更できます。

const gsiUrl = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';

利用するタイルの提供元に応じて、ページ内にクレジット表記を追加してください。
(例:© OpenStreetMap contributors© 国土地理院 など)

まとめ

deck.glは、WebGLをベースにした高性能な描画フレームワークであり、単体でも地理座標を扱うことができます。
本記事では、DeckGLの基本構成と、背景地図をTileLayer+BitmapLayerで重ねる方法を通じて、「地理空間上でデータを表現する最小構成」を確認しました。

ポイントを整理すると次の通りです。

  • DeckGLは地理座標の描画を担うフレームワークで、背景地図は自由に組み合わせられる
  • TileLayerとBitmapLayerを使うことで、外部の地図タイルを背景として重ねられる
  • ScatterplotLayerなどのレイヤを追加することで、地理データの可視化が容易にできる

この構成を理解しておけば、外部データ(GeoJSONやCSV)を読み込み、複数レイヤを重ねるような拡張もスムーズに行えます。

次回の記事では、MapLibreとの連携により、
DeckGL上でより高度なスタイル制御やカスタムタイルの利用を実現する方法を紹介します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?