View source
// CDN build: `ApexMaps` is a global (see index.html). With a bundler:
//   import ApexMaps from 'apexmaps'

// Nothing in the engine assumes the earth. `registerMap` puts any GeoJSON into
// the registry under an id, with the same provenance fields the built-in packs
// carry, and the `identity` projection draws flat coordinates as-is. Here that
// is a schematic six-zone floor plan, hand-written in this file.
const zone = (id, name, x, y, w, h) => ({
  type: 'Feature',
  properties: { id, name },
  geometry: {
    // Clockwise winding: d3-geo reads that as "the inside is here".
    type: 'Polygon',
    coordinates: [[[x, y], [x, y + h], [x + w, y + h], [x + w, y], [x, y]]],
  },
})

ApexMaps.registerMap(
  'demo/floor',
  {
    type: 'FeatureCollection',
    features: [
      zone('A', 'Assembly', 0, 0, 40, 30),
      zone('B', 'Paint', 40, 0, 25, 30),
      zone('C', 'Store', 65, 0, 20, 30),
      zone('D', 'QA', 0, 30, 30, 20),
      zone('E', 'Packing', 30, 30, 30, 20),
      zone('F', 'Loading', 60, 30, 25, 20),
    ],
  },
  { source: 'hand-drawn', license: 'n/a', vintage: '2026', keyField: 'id', levelName: 'Zones' },
)

const floor = new ApexMaps(document.getElementById('floor'), {
  chart: { height: 260 },
  // Identity: the coordinates are a flat plan, not lon/lat, so nothing is projected.
  geo: { map: 'demo/floor', projection: 'identity', keyField: 'id' },
  series: [
    {
      name: 'Utilisation',
      joinBy: ['id', 'key'],
      data: [
        { key: 'A', value: 81 },
        { key: 'B', value: 54 },
        { key: 'C', value: 37 },
        { key: 'D', value: 66 },
        { key: 'E', value: 48 },
        { key: 'F', value: 23 },
      ],
      scale: { palette: 'oranges', classes: 3 },
    },
  ],
  dataLabels: { enabled: true, minFeatureArea: 0 },
})

// Register a custom sequential palette, then use it by id on real geometry.
ApexMaps.registerPalette('demo/heat', {
  kind: 'sequential',
  stops: ['#f7f4ff', '#d9c7ff', '#b18aff', '#7d4bd6', '#4b1d96'],
  colorblindSafe: true,
})

const custom = new ApexMaps(document.getElementById('custom-palette'), {
  chart: { height: 260 },
  geo: { map: 'eu/nuts0@20m' },
  series: [
    {
      name: 'Adoption',
      joinBy: ['nuts_id', 'key'],
      data: [
        { key: 'DE', value: 72 },
        { key: 'FR', value: 64 },
        { key: 'IT', value: 51 },
        { key: 'ES', value: 45 },
        { key: 'PL', value: 33 },
        { key: 'NL', value: 28 },
        { key: 'SE', value: 19 },
      ],
      scale: { palette: 'demo/heat' },
    },
  ],
})

// `setGeoSource` takes a base URL or a loader function, so a self-hosted copy,
// an air-gapped install and a bundler import are all the same feature.
ApexMaps.setGeoSource((file) => fetch(`https://cdn.jsdelivr.net/npm/apexmaps-geo/${file}`).then((r) => r.json()))

Promise.all([floor.render(), custom.render()]).then(() => {
  const meta = ApexMaps.mapMeta('demo/floor')
  console.log(`demo/floor: ${meta.levelName}, ${floor.geo.features.length} features, source ${meta.source}`)
  console.log(`palettes registered: ${ApexMaps.listPalettes().length} (includes "demo/heat")`)
})
Extending - ApexMaps Demo | ApexCharts.js