svelte-maplibre

repository·master·Indexed 19 days ago

https://github.com/dimfeld/svelte-maplibre

Svelte bindings for the MapLibre GL JS mapping library, providing a wrapper to integrate MapLibre into Svelte applications. It includes a MapLibre component, specialized layer components (such as FillLayer, SymbolLayer, and HeatmapLayer), source components (GeoJSON, VectorTileSource, etc.), and map controls. The library also provides utilities for managing map context, generating viewport hashes, and creating MapLibre expressions and filters.

Tokens
6K
Snippets
27
Records
33
Agent score
68%

What's inside svelte-maplibre

  1. How context-based state management works

    master

    The library uses Svelte's context API combined with a Box<T> wrapper to provide reactive state to deeply nested components.

    • Box<T>: A wrapper around a $state() value. When you retrieve a Box from a context (like getLayer()), you access or update its value via the .value property.
    • Context Hierarchy: Components like layers and sources use specialized context functions (e.g., setLayer, setSource, setCluster) to communicate with their parents. This allows for a declarative structure where a layer component automatically knows which source it should be associated with by looking up the context tree.
    // Conceptual usage of the Box pattern
    import { getLayer } from 'svelte-maplibre';
    
    const layerBox = getLayer();
    if (layerBox) {
      // Accessing the reactive value
      console.log(layerBox.value);
      
      // Updating the reactive value
      layerBox.value = 'new-layer-id';
    }
  2. Configure ESLint for Svelte projects

    master

    To use ESLint with Svelte in this project, call createConfig(true). This enables the following:

    • eslint-plugin-svelte recommended rules (flat/recommended).
    • Svelte-Prettier compatibility (flat/prettier).
    • Parsing of **/*.svelte files using the TypeScript parser.
    • Disables svelte/valid-compile to prevent duplicate warnings in editors.

    Note: The configuration automatically includes globals.browser and globals.node.

    import { createConfig } from './eslint.config.js';
    
    export default createConfig(true);
  3. Use the MapLibre component in Svelte

    master

    To render a map, import the MapLibre component from svelte-maplibre. You can configure the map's center, zoom level, and style URL via props. Note that you must provide a height for the map container via CSS (using :global if the class is applied to the component).

    <script>
      import { MapLibre } from 'svelte-maplibre';
    </script>
    
    <MapLibre 
      center={[50,20]}
      zoom={7}
      class="map"
      standardControls
      style="https://basemaps.cartocdn.com/gl/positron-gl-style/style.json" />
    
    <style>
      :global(.map) {
        height: 500px;
      }
    </style>
  4. Remove a source from the map with removeSource()

    master

    The removeSource function safely removes a source from the map. It uses Svelte's tick() to wait for any associated layers to have a chance to remove themselves before attempting to remove the source. To prevent accidental removal of a newly added source that might have reused the same ID, it verifies that the source currently on the map is the exact same object (sourceObj) that was intended for removal.

    Parameters:

    • map: The MapLibre Map instance.
    • sourceId: The ID of the source to remove.
    • sourceObj: The original source object that was added, used for identity verification before removal.
    import { removeSource } from './path-to-source';
    
    // Assuming 'originalSource' is the object returned or stored from the addition
    removeSource(mapInstance, 'my-source-id', originalSource);
  5. Use boundsEqual to compare map bounds

    master

    The boundsEqual utility compares a provided LngLatBoundsLike parameter against the current map bounds, accounting for floating-point precision.

    export function boundsEqual(
      param: LngLatBoundsLike,
      mapBounds: MapLibre.LngLatBounds | undefined
    ): { equal: boolean; bounds: MapLibre.LngLatBounds }
  6. Generate a viewport hash with getViewportHash()

    master

    Use getViewportHash(map) to generate a string representation of the current MapLibre GL map state (zoom, center, bearing, and pitch). This hash can be used to store or share a specific map view via a URL fragment. The resulting string starts with a # and follows the format #zoom/lat/lng[/bearing][/pitch].

    import type { Map } from 'maplibre-gl';
    import { getViewportHash } from 'svelte-maplibre';
    
    // Assuming 'map' is an instance of MapLibre GL Map
    const hash = getViewportHash(map);
    console.log(hash); // e.g., "#12/52.52/13.40/0/45"
  7. Manage Zoom Limits for layers

    master

    You can restrict the zoom range for specific components or groups of components using setZoomLimits(min, max). This creates a ZoomRange context that layers can inherit. If a layer has its own zoom settings, they will override the parent's settings, but will still be constrained by the parent's bounds.

    import { setZoomLimits, getZoomLimits } from 'svelte-maplibre';
    
    // Set limits for a specific section of your map
    setZoomLimits(5, 15);
    
    // Retrieve current limits
    const limits = getZoomLimits();
    // limits.minzoom and limits.maxzoom
  8. Add a source to the map with addSource()

    master

    The addSource function adds a MapLibre source to a map instance. It includes logic to handle race conditions where a source with the same ID might be in the process of being removed, ensuring the new source is added only after the old one is gone.

    Parameters:

    • map: The MapLibre Map instance.
    • sourceId: A unique string ID for the source.
    • source: A SourceSpecification object defining the source data.
    • okToAdd: A callback function (sourceId: string) => boolean used to verify if the source should still be added (e.g., checking if the component is still mounted or the ID hasn't changed).
    • cb: A callback function () => void executed once the source has been successfully added.
    import type { Map, SourceSpecification } from 'maplibre-gl';
    import { addSource } from './path-to-source';
    
    addSource(
      mapInstance,
      'my-source-id',
      { type: 'geojson', data: myGeojsonData },
      (id) => id === 'my-source-id', // okToAdd check
      () => console.log('Source added!') // cb
    );
  9. Use zoomTransition to interpolate values by zoom level

    master

    The zoomTransition function creates a MapLibre interpolate expression that changes a value based on the map's current zoom level using a ['linear'] interpolation method. This is commonly used for scaling icon sizes, opacity, or line widths as the user zooms in or out.

    import { zoomTransition } from 'svelte-maplibre/expressions';
    
    // Example: Scale icon size from 10 to 30 between zoom levels 5 and 15
    const iconSize = zoomTransition(5, 10, 15, 30);
    
    const layer = {
      type: 'symbol',
      layout: {
        'icon-size': iconSize
      }
    };
  10. Identify text layers with isTextLayer

    master

    The isTextLayer utility checks if a given maplibregl.LayerSpecification is a symbol layer that contains a text-field layout property. You can optionally restrict the check to a specific source name.

    import { isTextLayer } from 'svelte-maplibre/filters';
    
    const layer = {
      type: 'symbol',
      source: 'my-source',
      layout: { 'text-field': '{name}' }
    };
    
    const isText = isTextLayer(layer, 'my-source'); // true
    const isWrongSource = isTextLayer(layer, 'other-source'); // false
  11. Use convertBoundsToUserFormat to normalize bounds

    master

    The convertBoundsToUserFormat function converts MapLibre.LngLatBounds into a user-friendly LngLatBoundsLike format (such as a flat array of 4 numbers or nested arrays), depending on the provided param structure.

    export function convertBoundsToUserFormat(
      bounds: MapLibre.LngLatBounds,
      param: LngLatBoundsLike | undefined
    ): LngLatBoundsLike