Svelte Maplibre

Transition Layers with Zoom

This example uses the ZoomRange component and zoomTransition function to fade smoothly between states and counties as the map zooms.

Current Zoom: 4.0

<script lang="ts">
  import MapLibre from 'svelte-maplibre/MapLibre.svelte';
  import GeoJSON from 'svelte-maplibre/GeoJSON.svelte';
  import { mapClasses } from '../styles.js';
  import code from './+page.svelte?raw';
  import CodeSample from '$site/CodeSample.svelte';
  import states from '$site/states.json';
  import counties from '$site/counties.json';
  import { isTextLayer } from 'svelte-maplibre/filters.js';
  import FillLayer from 'svelte-maplibre/FillLayer.svelte';
  import SymbolLayer from 'svelte-maplibre/SymbolLayer.svelte';
  import LineLayer from 'svelte-maplibre/LineLayer.svelte';
  import { geoCentroid } from 'd3-geo';
  import type { FeatureCollection, Feature, Point } from 'geojson';
  import ZoomRange from 'svelte-maplibre/ZoomRange.svelte';
  import { zoomTransition } from 'svelte-maplibre/expressions.js';

  function calculateCenters(g: FeatureCollection): FeatureCollection {
    let centers: Feature<Point>[] = g.features.map((f) => {
      return {
        ...f,
        geometry: {
          type: 'Point',
          coordinates: geoCentroid(f),
        },
      };
    });

    return {
      type: 'FeatureCollection',
      features: centers,
    };
  }

  const stateCenters = calculateCenters(states as unknown as FeatureCollection);
  const countyCenters = calculateCenters(counties as unknown as FeatureCollection);

  let zoomThreshold = $state(5);
  let currentZoom = $state(4);
</script>

<p>
  This example uses the ZoomRange component and zoomTransition function to fade smoothly between
  states and counties as the map zooms.
</p>

<div class="w-full self-start">
  <label class="flex w-full flex-wrap gap-x-2">
    <span>Transition at zoom level: {zoomThreshold}</span>
    <input class="w-32" type="range" bind:value={zoomThreshold} min={0} max={10} step={0.1} />
  </label>
  <p>Current Zoom: {currentZoom.toFixed(1)}</p>
</div>

<MapLibre
  style="https://basemaps.cartocdn.com/gl/positron-gl-style/style.json"
  class={mapClasses}
  standardControls
  center={[-98.137, 40.137]}
  zoom={4}
  onzoomend={({ target: map }) => (currentZoom = map.getZoom())}
  filterLayers={(l) => !isTextLayer(l, 'carto')}
>
  <ZoomRange maxzoom={zoomThreshold + 0.5}>
    {@const fadeStates = zoomTransition(zoomThreshold - 1, 0.8, zoomThreshold + 0.5, 0)}
    {@const fadeStatesText = zoomTransition(zoomThreshold - 1, 1, zoomThreshold, 0.2)}
    <GeoJSON id="states" data={states as unknown as FeatureCollection} promoteId="GEOID">
      <FillLayer
        paint={{
          'fill-color': 'green',
          'fill-opacity': fadeStates,
        }}
      />

      <LineLayer
        paint={{
          'line-color': 'white',
          'line-width': 1,
          'line-opacity': fadeStates,
        }}
      />
    </GeoJSON>

    <GeoJSON id="state-centers" data={stateCenters} promoteId="GEOID">
      <SymbolLayer
        filter={['!=', ['get', 'STUSPS'], 'DC']}
        paint={{
          'text-color': '#333',
          'text-opacity': fadeStatesText,
          'text-halo-color': '#eee',
          'text-halo-width': 0.5,
          'text-halo-blur': 0.5,
        }}
        layout={{
          'text-allow-overlap': true,
          'text-field': ['get', 'STUSPS'],
          'text-size': zoomTransition(3, 16, 5, 24),
        }}
      />
    </GeoJSON>
  </ZoomRange>

  <ZoomRange minzoom={zoomThreshold - 0.5}>
    {@const fadeCounties = zoomTransition(zoomThreshold - 0.5, 0, zoomThreshold + 0.5, 0.8)}
    {@const fadeCountiesText = zoomTransition(zoomThreshold, 0.2, zoomThreshold + 0.5, 1)}
    <GeoJSON id="counties" data={counties as unknown as FeatureCollection} promoteId="GEOID">
      <FillLayer
        paint={{
          'fill-color': 'orange',
          'fill-opacity': fadeCounties,
        }}
      />

      <LineLayer
        paint={{
          'line-color': 'white',
          'line-width': 1,
          'line-opacity': fadeCounties,
        }}
      />
    </GeoJSON>

    <GeoJSON id="county-centers" data={countyCenters} promoteId="GEOID">
      <SymbolLayer
        paint={{
          'text-color': 'black',
          'text-opacity': fadeCountiesText,
        }}
        layout={{
          'text-field': ['get', 'NAME'],
          'text-size': zoomTransition(5, 12, 10, 24),
        }}
      />
    </GeoJSON>
  </ZoomRange>
</MapLibre>

Back to Examples

Github