Arcs and Routes

Premium feature

Arc & route series is a Premium feature

Available on the Premium and OEM plans. These series work without a key for evaluation, with a watermark on the map; call ApexMaps.setLicense(key) to remove it.

A choropleth or bubble map says something about a place. The arc and line series exist to say something about the connection between two places: routes, cables, trade, migration.

series: [
  {
    type: 'arc',
    name: 'Weekly flights',
    data: [{ from: [103.99, 1.36], to: [-0.45, 51.47], value: 900 }],
    endpoints: { show: true },
  },
]

Great circles by default, and why

geodesic defaults to true: each arc follows the real great circle between its two endpoints, resampled densely enough to stay smooth at any zoom. It matters because the shortest path between two places on a sphere is a great circle, and a straight line drawn between the same two points on a projected map is neither the route a plane actually flies nor the right length. Set geodesic: false to draw the plain straight line between the two points instead.

Antimeridian cutting

Each arc is emitted internally as a lon/lat LineString, which lets d3-geo cut it at the antimeridian rather than draw it as one continuous path. A trans-Pacific route between Singapore and Los Angeles leaves the right edge of the map and re-enters on the left, instead of streaking backwards across the whole width of it.

Endpoints

endpoints: { show: true, radius: 2.5, color: '#00E396' }

endpoints.show draws a dot at each end of the arc. It defaults to off, since not every arc needs its endpoints called out separately from the line itself.

Each endpoint may be given as a [lon, lat] pair or, with joinBy set on the series, as a geometry key that resolves against the current map:

{
  type: 'arc',
  name: 'Weekly flights',
  joinBy: 'iso_a3',
  data: [{ from: [103.99, 1.36], to: 'GBR', value: 900 }],
}

Curvature, and its dev-mode warning

curvature bulges an arc perpendicular to its own chord, from 0 up to about 1, for the decorative fanned look you see in flight-route graphics. It's off by default and mutually exclusive with geodesic accuracy: a curved arc is no longer the true path between its endpoints, only a stylized reference to it, and setting curvature prints a dev-mode warning saying so.

{
  type: 'arc',
  name: 'Weekly flights',
  data,
  geodesic: false,
  curvature: 0.35,
}

Arcs live in world space and scale with the camera, the same as the geometry underneath them, which is why a decorative curvature value is a style choice rather than something the projection could get wrong for you.

Routes through given vertices: the line series

An arc derives its own path from two endpoints. The line series is for the opposite case: you already have the path, a GPS trace, a shipping lane, a transit line, and you want it drawn through the vertices you give it, in order.

series: [
  {
    type: 'line',
    name: 'Shipping lane',
    data: [
      {
        name: 'Rotterdam to Singapore',
        path: [
          [4.48, 51.92],
          [32.57, 30.06],
          [43.3, 12.8],
          [103.85, 1.29],
        ],
      },
    ],
    endpoints: { show: true },
  },
]

coordinates is accepted as a synonym for path, since route data often arrives shaped like GeoJSON already. width (a SizeOptions scale) and colorScale work the same way they do on arc, and a route's own color overrides the series color for just that row.

Flow: beads that show direction

An arc says two places are related. It does not say which way anything moves, and on a hub map, where every route leaves the same airport, the reader cannot infer it either. flow: true on an arc or line series sends beads travelling along each route from from toward to.

series: [
  {
    type: 'arc',
    name: 'Great-circle distance',
    data: ROUTES,
    geodesic: true,
    width: { range: [0.6, 3.4] },
    flow: true, // beads, from -> to
  },
]

Pass an object to tune it:

flow: { style: 'dash', speed: 60, spacing: 30 }
flow: { color: '#e8f4ff', size: 5, stagger: false }
flow: { scale: 'screen' } // fixed size and spacing at any zoom
OptionDefaultWhat it does
style'dots''dots' beads the route (reads as traffic); 'dash' marches a dashed highlight along it
scale'zoom''zoom' anchors beads to the ground so they spread and grow with the geography; 'screen' holds size and spacing fixed at any zoom
speed90Travel speed in screen px/sec at the opening zoom
spacing56Screen px between beads at the opening zoom. Lower it for a map of short routes
sizeroute widthBead diameter (or dash weight), so heavy corridors carry the fat beads
colorroute colorBead color
opacity1Bead opacity
staggertrueOffsets each route's phase so a corridor of parallel routes reads as traffic, not one synchronized pulse

It costs nothing per frame. The mechanism is one dashed companion path per route, animated on its dash offset rather than a dot moved per frame. A repeating pattern only has to advance one period to loop, so the duration is spacing / speed and the route's own length never enters into it. Nothing is measured (no getTotalLength, which does not exist on a server or in jsdom), so the phase is a pure function of time and panning cannot disturb it.

Under the default scale: 'zoom' each of the three camera-following properties is bounded, because each degenerates at the far end of a zoom: bead size reaches 3x its opening size, pace 2x, and spacing 6x. Past that the flow looks the same however far the reader keeps going.

Beads stop travelling, but stay spaced along the route so it still reads as a route, under prefers-reduced-motion, with chart.animations.enabled: false, or when a single series carries more than FLOW_BUDGET (600) routes. A flow is a repaint of every dashed geodesic for as long as the page is open rather than a transition that ends, which is why it has a ceiling of its own well below the mark budget.

Flow rides on the already-licensed arc and line series and adds no new gate. One known limit: d3-geo cuts a trans-Pacific arc in two at the antimeridian, so beads keep travelling in the right direction across the seam but not in step across it.

Hoverable thin lines

Both series get an invisible, wider hit path drawn behind the visible stroke. A 1px flight line is otherwise unpointable, and widening the visible stroke to make it hoverable would misrepresent the value it's encoding through width. Hover and click both test against the wider invisible path instead.