Menu

Map (svg, tooltip)Edit

A map component using d3-geo for the projection. Adapted from d3-selection, the SVG map component uses Layer Cake's built-in raise function to ensure the hovered-over SVG element is always on top of its siblings. The tooltip uses Svelte's createEventDispatcher.

<script>
  import { LayerCake, Svg, Html } from 'layercake';
  import { feature } from 'topojson-client';
  import { geoAlbersUsa } from 'd3-geo';
  import { scaleQuantize } from 'd3-scale';
  import { format } from 'd3-format';

  import MapSvg from './components/Map.svg.svelte';
  import Tooltip from './components/Tooltip.svelte';

  // This example loads json data as json using @rollup/plugin-json
  import usStates from './data/us-states.topojson.json';
  import stateData from './data/us-states-data.json';

  const colorKey = 'myValue';
  /* --------------------------------------------
   * Create lookups to more easily join our data
   */
  const joinKey = 'name';
  const dataLookup = new Map();

  const geojson = feature(usStates, usStates.objects.collection);
  const projection = geoAlbersUsa();

  stateData.forEach(d => {
    dataLookup.set(d[joinKey], d);
  });

  geojson.features.forEach(d => {
    // This will overwrite any existing keys on d.properties
    // so watch out for any name collision
    Object.assign(d.properties, dataLookup.get(d.properties[joinKey]));
  });

  let evt;
  let hideTooltip = true;

  // Create a flat array of objects that LayerCake can use to measure
  // extents for the color scale
  const flatData = geojson.features.map(d => d.properties);
  const colors = ['#ffdecc', '#ffc09c', '#ffa06b', '#ff7a33'];

  const addCommas = format(',');
</script>

<style>
  /*
    The wrapper div needs to have an explicit width and height in CSS.
    It can also be a flexbox child or CSS grid element.
    The point being it needs dimensions since the <LayerCake> element will
    expand to fill it.
  */
  .chart-container {
    width: 100%;
    height: 100%;
  }
</style>

<div class="chart-container">
  <LayerCake
    data={geojson}
    z={colorKey}
    zScale={scaleQuantize()}
    zRange={colors}
    {flatData}
  >
    <Svg>
      <MapSvg
        {projection}
        on:mousemove={event => evt = hideTooltip= event}
        on:mouseout={() => hideTooltip = true}
      />
    </Svg>

    <Html
      pointerEvents={false}
    >
      {#if hideTooltip !== true}
        <Tooltip
          {evt}
          let:detail
        >
          {#each Object.entries(detail.props) as [key, value]}
            <div class="row"><span>{key}:</span> {typeof value === 'number' ? addCommas(value) : value}</div>
          {/each}
        </Tooltip>
      {/if}
    </Html>
  </LayerCake>
</div>