LoginSignup
7
1

[MapLibre GL JS]レイヤー管理するプラクティスのSvelteKit版

Last updated at Posted at 2023-12-16

これは MapLibre AdventCalendar 2023 16日目の記事です。
昨日は @qazsato さんによる MapLibre GL JS と japanmesh で地域メッシュを表示する でした。

@Kanahiro さんが8日目に書いた、[MapLibre GL JS]ReactやVueでレイヤー管理する際のプラクティスをSvelteでやる場合こう書くよという例になります

リアクティブな配列の変数を用意して、setMapstyleでstyleを書き換えていく感じになります
ポイントはcreateMapstyle関数で、こいつを上手く作ることでレイヤーの重なり順や、様々なタイル形式に対応させていくことになります(今回はラスタータイルのみ対応バージョンになっています)

Map.svelte
<script lang="ts">
  import { onMount } from "svelte";
  import maplibregl, { type StyleSpecification } from "maplibre-gl";
  import type { Map } from "maplibre-gl";
  import "maplibre-gl/dist/maplibre-gl.css";

  // 型の定義
  interface LayerData {
    id: string;
    type: string;
    name: string;
    opacity: number;
    tiles: string[];
    attribution: string;
  }

  interface Source {
    [key: string]: {
      type: string;
      tiles: string[];
      tileSize: number;
      attribution: string;
    };
  }

  interface Layer {
    id: string;
    type: string;
    source: string;
    minzoom: number;
    maxzoom: number;
    paint?: {
      "raster-opacity": number;
    };
  }

  // 地図
  let map: Map | undefined;

  // レイヤー定義
  const layerDataList = [
    {
      id: "pale",
      type: "raster",
      name: "地理院地図(淡色地図)",
      opacity: 1.0,
      tiles: [
        "https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png",
      ],
      attribution:
	    "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
    },
    {
      id: "flood-shinsui",
      type: "raster",
      name: "洪水浸水想定区域(想定最大規模)",
      opacity: 0.5,
      tiles: [
        "https://disaportaldata.gsi.go.jp/raster/01_flood_l2_shinsuishin_data/{z}/{x}/{y}.png",
      ],
      attribution:
        "<a href='https://disaportal.gsi.go.jp/index.html' target='_blank'>ハザードマップポータルサイト</a>",
    },
  ];

  // 選択されたレイヤー
  let selectedLayers: Array<LayerData> = [layerDataList[0]];
  // レイヤー選択時に地図を更新
  $: if (map) {
    const style = createMapstyle(selectedLayers);
    map.setStyle(style);
  }

  onMount(() => {
    const lng = 137.767;
    const lat = 35.681;
    const zoom = 7;

    map = new maplibregl.Map({
      container: "map",
      center: new maplibregl.LngLat(lng, lat),
      zoom: zoom,
      style: createMapstyle(selectedLayers),
    });

    map.addControl(new maplibregl.NavigationControl(), "bottom-right");
    map.addControl(new maplibregl.GeolocateControl({}), "bottom-right");
    map.addControl(new maplibregl.ScaleControl({}), "bottom-left");
  });

  // 地図スタイルの作成
  const createMapstyle = (layers: Array<LayerData>) => {
    let selectSources: Source = {};
    let selectLayers: Array<Layer> = [];

    layers.forEach((layerData) => {
      selectSources[layerData.id] = {
        type: layerData.type,
        tiles: layerData.tiles,
        tileSize: 256,
        attribution: layerData.attribution,
      };

      selectLayers.push({
        id: layerData.id,
        type: layerData.type,
        source: layerData.id,
        minzoom: 0,
        maxzoom: 23,
        paint: {
          "raster-opacity": layerData.opacity,
        },
      });
    });

    return {
      version: 8,
      sources: selectSources,
      layers: selectLayers,
    } as StyleSpecification;
  };
</script>

<div id="map" class="h-screen w-screen" />
<div class="absolute top-0 left-0 p-4">
  <ul>
    {#each layerDataList as layerData}
      <li>
        <input type="checkbox" bind:group={selectedLayers} value={layerData} />
        {layerData.name}
      </li>
    {/each}
  </ul>
</div>

このケース、他のライブラリーでも書いたことが何回かあるのですが、Svelteが一番スッキリ書けますね
Svelte最高!!

明日は@freedom-techさんの記事です!お楽しみにー

7
1
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
7
1