Menu

Map.canvas.svelte component

Generates a canvas map using the geoPath function from d3-geo.

Param Default Required Description
projection Function None
yes
A D3 projection function. Pass this in as an uncalled function, e.g. projection={geoAlbersUsa}.
stroke string '#ccc'
no
The shape's stroke color.
strokeWidth number 1
no
The shape's stroke width.
fill (string | undefined) None
no
The shape's fill color. By default, the fill will be determined by the z-scale, unless this prop is set.
features (Array<GeoJSON> | undefined) None
no
A list of GeoJSON features. Use this if you want to draw a subset of the features in $data while keeping the zoom on the whole GeoJSON feature set. By default, it plots everything in $data.features if left unset.

Used in these examples:

SSR Examples:

<!--
  @component
  Generates a canvas map using the `geoPath` function from [d3-geo](https://github.com/d3/d3-geo).
 -->
<script>
  import { getContext } from 'svelte';
  import { scaleCanvas } from 'layercake';
  import { geoPath } from 'd3-geo';

  const { data, width, height, zGet } = getContext('LayerCake');

  const { ctx } = getContext('canvas');

  /**
   * @typedef {Object} Props
   * @property {Function} projection - A D3 projection function. Pass this in as an uncalled function, e.g. `projection={geoAlbersUsa}`.
   * @property {string} [stroke='#ccc'] - The shape's stroke color.
   * @property {number} [strokeWidth=1] - The shape's stroke width.
   * @property {string|undefined} [fill] - The shape's fill color. By default, the fill will be determined by the z-scale, unless this prop is set.
   * @property {Array<GeoJSON>|undefined} [features] - A list of GeoJSON features. Use this if you want to draw a subset of the features in `$data` while keeping the zoom on the whole GeoJSON feature set. By default, it plots everything in `$data.features` if left unset.
   */

  /** @type {Props} */
  let { projection, stroke = '#ccc', strokeWidth = 1, fill, features } = $props();

  let projectionFn = $derived(projection().fitSize([$width, $height], $data));

  let geoPathFn = $derived(geoPath(projectionFn));

  let featuresToDraw = $derived(features || $data.features);

  $effect(() => {
    if (!$width || !$height || !$ctx) return;

    // Assign to a local variable: setting properties on `$ctx` directly
    // would re-notify the store and re-trigger this effect
    const context = $ctx;
    const zGetFn = $zGet;

    scaleCanvas(context, $width, $height);
    context.clearRect(0, 0, $width, $height);

    featuresToDraw.forEach(
      /** @param {any} feature */ feature => {
        context.beginPath();
        // Set the context here since setting it in `geoPath` is a circular reference
        geoPathFn.context(context);
        geoPathFn(feature);

        context.fillStyle = fill || zGetFn(feature.properties);
        context.fill();

        context.lineWidth = strokeWidth;
        context.strokeStyle = stroke;
        context.stroke();
      }
    );
  });
</script>