OnlyMapJS Latest release
GitHub Docs

← Gallery / Layers

Draw arcs and paths

The same five journeys twice: PathLayer follows the real driving route, ArcLayer curves straight between endpoints.

Open full screen ↗ View source ↗
<!DOCTYPE html>
<!--
  Two ways to draw a line between two places, on the same five journeys.

  PathLayer follows the road, because the data is the real driving route.
  ArcLayer ignores it and curves straight from origin to destination — the
  right choice when only the endpoints and the volume matter.

  Toggle one layer off in the legend to see the difference clearly.
-->
<html>
<head>
  <meta charset="utf-8" />
  <title>OnlyMapJS — Draw arcs and paths</title>
  <link rel="stylesheet" href="https://unpkg.com/@nika-js/onlymap/dist/onlymapjs.css" />
  <script type="module" src="https://unpkg.com/@nika-js/onlymap/dist/onlymap.standalone.js"></script>
  <style>
    html, body { margin: 0; height: 100%; font-family: system-ui, sans-serif; }
    om-map { display: block; height: 100vh; }
  </style>
</head>
<body>

  <om-map center="[103.86, 1.335]" zoom="11.3" pitch="45" basemap="positron">

    <!-- The routes are a plain .geojson of LineString features, exactly what a
         routing service returns (these came from OSRM with geometries=geojson).
         $geometry.coordinates reaches into the feature's own geometry, so the
         file needs no reshaping first.

         width-units="pixels" matters: deck.gl measures width in metres by
         default, so a 4 m road vanishes as soon as you zoom out. -->
    <om-layer id="routes" type="PathLayer"
              data="../../data/sg-routes.geojson"
              label="Driving route (follows roads)" color="#dc2626"
              get-path="$geometry.coordinates"
              get-color="[220, 38, 38]"
              get-width="4"
              width-units="pixels"
              width-min-pixels="2"
              cap-rounded joint-rounded
              pickable></om-layer>

    <!-- Same five journeys as straight arcs. Two positions per row instead of
         a path, and two colours so direction reads at a glance — the source
         colour fades into the target colour along the curve.
         get-height controls how far the arc bows off the ground. -->
    <om-layer id="flows" type="ArcLayer"
              data="../../data/sg-commutes.json"
              label="Commute flow (endpoints only)" color="#0ea5e9"
              get-source-position="[$from_lon, $from_lat]"
              get-target-position="[$to_lon, $to_lat]"
              get-source-color="[14, 165, 233, 200]"
              get-target-color="[249, 115, 22, 220]"
              get-width="scale($trips, sqrt, [2, 10], domain=[0, 22000])"
              get-height="0.5"
              width-units="pixels"
              pickable></om-layer>

    <om-widget type="legend" title="Layers" interactive position="top-right"></om-widget>
    <om-widget type="zoom-controls" position="bottom-right"></om-widget>
    <om-widget type="scale-bar" position="bottom-left"></om-widget>

    <om-overlay id="route-detail" anchor-from="selection" visible="false">
      <style>
        .card { background: #fff; padding: 8px 12px; border-radius: 8px;
                box-shadow: 0 2px 10px rgba(0,0,0,.2); font-size: 13px; white-space: nowrap; }
        .card em { color: #64748b; font-style: normal; }
      </style>
      <div class="card"><strong>{{name}}</strong><br /><em>{{distance_km}} km · {{duration_min}} min by road</em></div>
    </om-overlay>
    <om-behavior on="click" layer="routes" action="show-overlay" target="route-detail"></om-behavior>

    <om-overlay id="flow-detail" anchor-from="selection" visible="false">
      <style>
        .card { background: #fff; padding: 8px 12px; border-radius: 8px;
                box-shadow: 0 2px 10px rgba(0,0,0,.2); font-size: 13px; white-space: nowrap; }
        .card em { color: #64748b; font-style: normal; }
      </style>
      <div class="card"><strong>{{from}} → {{to}}</strong><br /><em>{{trips}} trips each morning</em></div>
    </om-overlay>
    <om-behavior on="click" layer="flows" action="show-overlay" target="flow-detail"></om-behavior>

    <om-widget type="attribution"
               text="Routes generated with OSRM from OpenStreetMap data © OpenStreetMap contributors, ODbL"
               position="bottom-start"></om-widget>

    <om-fallback>
      <p style="font: 15px system-ui, sans-serif; padding: 24px; max-width: 42ch">
        <strong>This map needs JavaScript.</strong><br />
        Open this file in a web browser such as Chrome, Safari, or Firefox.
      </p>
    </om-fallback>

  </om-map>

</body>
</html>

The snippet above loads the latest release. The demo above it is pinned to v0.5.2, so it keeps working when a new version ships.