Menu

MapPoints.canvas.svelte component

Generates canvas dots onto a map using 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}.
r number 3.5
no
The point's radius.
fill string 'yellow'
no
The point's fill color.
stroke string '#000'
no
The point's stroke color.
strokeWidth number 1
no
The point's stroke width.
features (Array<Object> | undefined) None
no
A list of GeoJSON features to plot. If unset, the plotted features will default to those in $data.features, assuming this field is a list of GeoJSON features.
<!--
  @component
  Generates canvas dots onto a map using [d3-geo](https://github.com/d3/d3-geo).
 -->
<script>
  import { getContext } from 'svelte';
  import { scaleCanvas } from 'layercake';

  const { data, width, height } = 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 {number} [r=3.5] - The point's radius.
   * @property {string} [fill='yellow'] - The point's fill color.
   * @property {string} [stroke='#000'] - The point's stroke color.
   * @property {number} [strokeWidth=1] - The point's stroke width.
   * @property {Array<Object>|undefined} [features] - A list of GeoJSON features to plot. If unset, the plotted features will default to those in `$data.features`, assuming this field is a list of GeoJSON features.
   */

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

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

  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;

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

    // To scale the circle by size, set width and height to `$rGet(d.properties)`
    featuresToDraw.forEach(
      /** @param {any} d */ d => {
        context.beginPath();
        const coordinates = projectionFn(d.geometry.coordinates);
        context.arc(coordinates[0], coordinates[1], r, 0, 2 * Math.PI, false);
        context.fillStyle = fill;
        context.fill();
        context.lineWidth = strokeWidth;
        context.strokeStyle = stroke;
        context.stroke();
      }
    );
  });
</script>