Complex

A showcase of various reactive features.

lat: 37.062, lng: 137.354, z: 6.0, pitch: 0.0, bearing: 0.0
marker: 35.681, 139.767
<script lang="ts">
  import maplibregl from 'maplibre-gl';
  import {
    MapLibre,
    RasterTileSource,
    RasterDEMTileSource,
    VectorTileSource,
    GeoJSONSource,
    BackgroundLayer,
    RasterLayer,
    FillLayer,
    LineLayer,
    CircleLayer,
    FillExtrusionLayer,
    NavigationControl,
    FullScreenControl,
    ScaleControl,
    LogoControl,
    GeolocateControl,
    AttributionControl,
    HeatmapLayer,
    TerrainControl,
    Hash,
    HillshadeLayer,
    Terrain,
    Sky,
    Projection,
    Light,
    Marker,
    Popup,
    GlobeControl
  } from 'svelte-maplibre-gl';

  let map: maplibregl.Map | undefined = $state.raw();
  let hash = $state(true);
  let sky = $state(true);
  let globe = $state(true);
  let showCities = $state(true);
  let hillshade = $state(true);
  let extrude = $state(false);
  let heatmap = $state(false);
  let pointColor = $state('#ffff00');
  let lineColor = $state('#ff00dd');
  let lineWidth = $state(1.5);
  let circleRadius = $state(3);
  let center: [number, number] = $state([137.3543, 37.062]);
  let zoom = $state(6.0);
  let pitch = $state(0);
  let bearing = $state(0);
  let controlPosition: maplibregl.ControlPosition | undefined = $state('bottom-right');
  let markerLnglat = $state({ lng: 139.767052, lat: 35.681167 });
  let popupOpen = $state(false);
</script>

<div class="flex items-center gap-x-2 p-1 text-sm">
  <label>
    <input class="rounded border p-1 leading-none" type="checkbox" bind:checked={showCities} />
    Cities
  </label>
  <label>
    <input class="rounded border p-1 leading-none" type="checkbox" bind:checked={hillshade} />
    Hillshade
  </label>
  <label>
    <input class="rounded border p-1 leading-none" type="checkbox" bind:checked={sky} />
    Sky
  </label>
  <label>
    <input class="rounded border p-1 leading-none" type="checkbox" bind:checked={extrude} />
    Extrude
  </label>
  <label>
    <input class="rounded border p-1 leading-none" type="checkbox" bind:checked={heatmap} />
    Heatmap
  </label>
  <label>
    <input class="rounded border p-1 leading-none" type="checkbox" bind:checked={globe} />
    Globe
  </label>
  <label>
    pC:
    <input type="color" bind:value={pointColor} class="inline-block w-7 rounded border leading-none" />
  </label>
  <label>
    pR:
    <input
      type="number"
      min="0"
      max="10"
      step="0.5"
      bind:value={circleRadius}
      class="w-12 rounded border p-1 leading-none"
    />
  </label>
  <label>
    lC:
    <input type="color" bind:value={lineColor} class="w-7 rounded border leading-none" />
  </label>
  <label>
    lW:
    <input
      type="number"
      min="0"
      max="10"
      step="0.5"
      bind:value={lineWidth}
      class="w-12 rounded border p-1 leading-none"
    />
  </label>
  <select bind:value={controlPosition}>
    <option value="top-left">top-left</option>
    <option value="top-right">top-right</option>
    <option value="bottom-left">botom-left</option>
    <option value="bottom-right">bottom-right</option>
    <option value={undefined}>auto</option>
  </select>
  <button
    class="rounded border p-1 leading-none"
    onclick={() => {
      map?.flyTo({ center: markerLnglat, zoom: 15 });
    }}>Fly to</button
  >
</div>
<div class="flex items-center gap-x-4 text-sm">
  <pre
    class="my-1 grow">{`lat: ${center[1].toFixed(3)}, lng: ${center[0].toFixed(3)}, z: ${zoom.toFixed(1)}, pitch: ${pitch.toFixed(1)}, bearing: ${bearing.toFixed(1)}`}</pre>
  <pre class="my-1 grow">marker: {`${markerLnglat.lat.toFixed(3)}, ${markerLnglat.lng.toFixed(3)}`}</pre>
  <label>
    z:
    <input type="number" min="0" max="24" step="0.5" bind:value={zoom} class="w-12 rounded border p-1 leading-none" />
  </label>
  <label>
    <input type="checkbox" bind:checked={popupOpen} /> Popup Open
  </label>
  <label>
    <input type="checkbox" bind:checked={hash} /> Hash
  </label>
</div>

<MapLibre
  class="h-[55vh] min-h-[300px]"
  bind:map
  bind:zoom
  bind:center
  bind:pitch
  bind:bearing
  maxPitch={85}
  attributionControl={false}
>
  <!--
  maxBounds={[
    { lng: 120, lat: 20 },
    { lng: 150, lat: 50 }
  ]}
  -->
  {#if hash}
    <Hash />
  {/if}
  {#if sky}
    <Sky
      sky-color="#001560"
      horizon-color="#0090c0"
      fog-color="#ffffff"
      sky-horizon-blend={0.9}
      horizon-fog-blend={0.8}
      fog-ground-blend={0.7}
      atmosphere-blend={['interpolate', ['linear'], ['zoom'], 2, 0.8, 4, 0.3, 7, 0]}
    />
  {/if}
  <Light anchor="map" />
  <Projection type={globe ? 'globe' : undefined} />
  <AttributionControl position={controlPosition} compact />
  <LogoControl position={controlPosition} />
  <ScaleControl position={controlPosition} />
  <FullScreenControl position={controlPosition} />
  <GeolocateControl position={controlPosition} />
  <NavigationControl position={controlPosition} visualizePitch />
  <TerrainControl position={controlPosition} source="terrain" />
  <GlobeControl position={controlPosition} />
  <RasterDEMTileSource
    id="terrain"
    tiles={['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png']}
    minzoom={0}
    maxzoom={15}
    encoding="terrarium"
    attribution="<a href='https://github.com/tilezen/joerd/blob/master/docs/attribution.md'>Mapzen (Terrain)</a>"
  >
    {#if !globe}
      <Terrain />
    {/if}
  </RasterDEMTileSource>
  <RasterTileSource
    tiles={['https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg']}
    minzoom={2}
    maxzoom={18}
    attribution="国土地理院, TSIC, GEO Grid/AIST, USGS, GEBCO, NASA"
  >
    <RasterLayer />
  </RasterTileSource>
  <BackgroundLayer id="dummy1" layout={{ visibility: 'none' }} />
  {#if hillshade}
    <RasterDEMTileSource
      tiles={['https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png']}
      minzoom={0}
      maxzoom={15}
      encoding="terrarium"
      attribution="<a href='https://github.com/tilezen/joerd/blob/master/docs/attribution.md'>Mapzen (Terrain)</a>"
    >
      <HillshadeLayer beforeId="dummy1" />
    </RasterDEMTileSource>
  {/if}
  <BackgroundLayer id="dummy2" layout={{ visibility: 'none' }} />
  <BackgroundLayer id="dummy3" layout={{ visibility: 'none' }} />
  <VectorTileSource
    tiles={['https://jma-assets.mierune.dev/tiles/mete/{z}/{x}/{y}.pbf']}
    minzoom={0}
    maxzoom={13}
    attribution={'<a href="https://www.data.jma.go.jp/developer/gis.html">気象庁</a>'}
  >
    {#if showCities}
      <LineLayer
        sourceLayer="city"
        beforeId="dummy3"
        layout={{ 'line-join': 'round', 'line-cap': 'round' }}
        paint={{ 'line-color': lineColor, 'line-width': lineWidth }}
      />
      {#if extrude}
        <FillExtrusionLayer
          sourceLayer="city"
          beforeId="dummy2"
          paint={{
            'fill-extrusion-color': '#555533',
            'fill-extrusion-height': ['/', ['to-number', ['get', 'code']], 100],
            'fill-extrusion-opacity': 0.7
          }}
        />
      {:else}
        <FillLayer
          id="cityfill"
          sourceLayer="city"
          beforeId="dummy2"
          paint={{ 'fill-color': '#aaaa33', 'fill-opacity': 0.3 }}
        />
      {/if}
    {/if}
  </VectorTileSource>
  <GeoJSONSource data="https://jma-assets.mierune.dev/codes/amedas_ame.geojson">
    {#if heatmap}
      <HeatmapLayer
        paint={{
          'heatmap-weight': 1,
          'heatmap-intensity': ['interpolate', ['exponential', 2], ['zoom'], 0, 0.9, 18, 10],
          'heatmap-color': [
            'interpolate',
            ['linear'],
            ['heatmap-density'],
            0,
            'rgba(33,102,172,0)',
            0.2,
            'rgb(103,169,207)',
            0.4,
            'rgb(209,229,240)',
            0.6,
            'rgb(253,219,199)',
            0.7,
            'rgb(239,138,98)',
            0.9,
            'rgb(178,24,43)',
            1,
            'rgb(100,0,200)'
          ],
          'heatmap-radius': ['interpolate', ['linear'], ['zoom'], 0, 8, 18, 20],
          'heatmap-opacity': ['interpolate', ['linear'], ['zoom'], 2, 1, 18, 0]
        }}
      />
    {:else}
      <CircleLayer beforeId="dummy3" paint={{ 'circle-radius': circleRadius, 'circle-color': pointColor }} />
    {/if}
  </GeoJSONSource>
  <Marker bind:lnglat={markerLnglat} draggable color="#99dd55">
    {#snippet content()}
      <span class="text-3xl">🐸</span>
    {/snippet}
    <Popup class="text-black" bind:open={popupOpen}>
      <span class="text-lg">
        {`${markerLnglat.lat.toFixed(3)}, ${markerLnglat.lng.toFixed(3)}`}
      </span>
    </Popup>
  </Marker>
</MapLibre>