📘

deck.glでGoogleマップ上に任意の位置をクリックしてポリゴンとラインストリングを作成する

2023/04/25に公開

マウス操作でポリゴン作って地図上に表示できるか試してみたところ、期待通りできたのでその方法を書いてみたいと思います。

環境

言語: TypeScript
ライブラリ: deck.gl
※ここで紹介してるプログラムはTypeScriptの部分のみですが、表示にはvue3を使っています。

まずは表示

ひとまず、動作させるとどうなるかご覧ください。

地図上をクリックすると点が表示される。

2点にすると線が結ばれる。

3点にするとポリゴンが描画される。

いい感じの位置に点を置く限り綺麗な多角形になる。


点を順番に格納していって、終点に始点を入れているので
点と点を結んだ線が重ならなければ綺麗なポリゴンになりそうです。

変な位置をクリックすると崩れる。


右回りに点を打っていましたが、ポリゴンの右側をクリックしたら線が重なりこのようになりました。
不正な形になる時はエラー出すなり、描画させないなり処理が必要かと思います。
今回はその辺りは実装しませんでした。

プログラム

1.GoogleMapsOverlayの作成

まず、GoogleMapsOverlayインスタンスを作成し、地図に設定します。

const overlay = new GoogleMapsOverlay();
overlay.setMap(マップのインスタンス);

2.マーカーを追加する関数

addMarkerは指定された位置にマーカーを作成し、マップに追加します。
3つ以上あればポリゴンが形成できるので、3つ以上になったらすべてのマーカーのアイコンを削除するようにしています。

const markers: google.maps.Marker[] = [];

const addMarker = (location) => {
  const marker = new google.maps.Marker({
    position: location,
    map: map,
    icon: {
      url: 'data:image/svg+xml;utf-8,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="50" fill="black"/></svg>'),
      scaledSize: new google.maps.Size(10, 10) // アイコンのサイズを調整
    }
  });

  if (marker) {
    markers.push(marker);
    updateGeoJSONLayer();

    // マーカーが3つ以上ある場合削除
    if (markers.length >= 3) {
      markers.forEach(marker => marker.setMap(null));
    }
  }
};

3.GeoJSONのラインストリングを作成する関数

createGeoJSONLineStringは、マーカーの配列からGeoJSON形式のラインストリングを作成します。

const createGeoJSONLineString = (markers) => {
  const coordinates = markers.map(marker => {
    const position = marker.getPosition();
    return [position.lng(), position.lat()];
  });

  return {
    type: 'FeatureCollection',
    features: [
      {
        type: 'Feature',
        geometry: {
          type: 'LineString',
          coordinates
        },
        properties: {}
      }
    ]
  };
};

4.GeoJSONのポリゴンを作成する関数

createGeoJSONPolygonは、マーカーの配列からGeoJSON形式のポリゴンを作成します。最初の座標を末尾に追加することでポリゴンとしての形を生成します。

const createGeoJSONPolygon = (markers) => {
  const coordinates = markers.map(marker => {
    const position = marker.getPosition();
    return [position.lng(), position.lat()];
  });

  // 最初の座標を末尾に追加してポリゴンを閉じる
  coordinates.push(coordinates[0]);

  return {
    type: 'FeatureCollection',
    features: [
      {
        type: 'Feature',
        geometry: {
          type: 'Polygon',
          coordinates: [coordinates]
        },
        properties: {}
      }
    ]
  };
};

5.GeoJSONレイヤーを更新する関数

updateGeoJSONLayerは、マーカーの数に応じて、GeoJSONのポリゴンもしくはラインストリングを作成し、それを表示する新しいGeoJsonLayerインスタンスを作成します。その後overlayのプロパティを更新して、新しいレイヤーが表示されるようにします。

const updateGeoJSONLayer = () => {
  const geojsonData = markers.length >= 3 ? createGeoJSONPolygon(markers) : createGeoJSONLineString(markers);

  const geoJsonLayer = new GeoJsonLayer({
    id: 'geojson-layer',
    data: geojsonData,
    stroked: true,
    lineWidthMinPixels: 2,
    getLineColor: [255, 0, 0, 255],
    getFillColor: [255, 0, 0, 100]
  });

  overlay.setProps({
    layers: [geoJsonLayer]
  });
};

あとはaddMarkerを地図上をクリックしたイベントで呼べば、クリックすることである程度自由な形のポリゴンを作ることができます。

まとめ

本当はdeck.glにこの機能がないかなと期待してたんですがなかったため自作しました。
試していませんが、Googleマップだけではなく、他の地図でも座標を取得できれば同じようにポリゴンを作れるんじゃないかと思います。
今回はただ単にポリゴンを生成しただけですが、他にも色々な機能を持たせることで面白い表現ができるんじゃないかと思います。

レスキューナウテックブログ

Discussion