Draw a circle
Replicates the MapLibre example Draw a circle. The fill and outline layers and their shared source are YAML; the source starts empty. The polygon is computed at runtime from a centre and a radius — what turf.circle does upstream, in a few lines — and handed to the layers with updateLayerData(). A slider in a corner slot recomputes it.
The YAML half
Section titled “The YAML half”# yaml-language-server: $schema=https://docs.maplibre-yaml.org/schema/latest/map.schema.json# Gallery twin of https://maplibre.org/maplibre-gl-js/docs/examples/draw-a-circle/# The YAML half: an OpenStreetMap raster basemap and the two layers that# style the circle. The circle polygon is computed at runtime from a centre# and a radius (what turf.circle does upstream) and handed to the layer —# see draw-a-circle.js. A precomputed polygon would make this pure YAML.type: mapid: draw-a-circleconfig: center: [2.3454, 48.8452] zoom: 13 maxZoom: 18 mapStyle: "https://demotiles.maplibre.org/style.json"sources: osm: type: raster tiles: ["https://tile.openstreetmap.org/{z}/{x}/{y}.png"] tileSize: 256 maxzoom: 19 attribution: "© OpenStreetMap Contributors" location-radius: type: geojson data: { type: FeatureCollection, features: [] }layers: - id: osm type: raster source: osm - id: location-radius type: fill source: location-radius paint: fill-color: "#8CCFFF" fill-opacity: 0.5 - id: location-radius-outline type: line source: location-radius paint: line-color: "#0094ff" line-width: 3The page half
Section titled “The page half”This markup goes inside <ml-map>; children with a slot ride its corner system.
<div slot="top-left" class="gallery-panel"> <label>Radius <strong data-radius-readout>1.0 km</strong></label> <input type="range" data-radius min="0.2" max="3" step="0.1" value="1" /></div>The JavaScript half
Section titled “The JavaScript half”// The JavaScript half: compute geometry in the page, hand it to the layer.// circle() is what turf.circle does — 64 points at a fixed great-circle// distance from the centre — and updateLayerData() fills the (initially// empty) source both YAML layers share. The slider recomputes it live.const mapEl = document.querySelector("ml-map");const slider = document.querySelector("[data-radius]");const readout = document.querySelector("[data-radius-readout]");const center = [2.3454, 48.8452];
function circle([lng, lat], radiusKm, steps = 64) { const R = 6371.0088; // mean Earth radius, km const d = radiusKm / R; const φ1 = (lat * Math.PI) / 180; const λ1 = (lng * Math.PI) / 180; const ring = []; for (let i = 0; i <= steps; i++) { const θ = (-2 * Math.PI * (i % steps)) / steps; // counter-clockwise, closed const φ2 = Math.asin(Math.sin(φ1) * Math.cos(d) + Math.cos(φ1) * Math.sin(d) * Math.cos(θ)); const λ2 = λ1 + Math.atan2(Math.sin(θ) * Math.sin(d) * Math.cos(φ1), Math.cos(d) - Math.sin(φ1) * Math.sin(φ2)); ring.push([(λ2 * 180) / Math.PI, (φ2 * 180) / Math.PI]); } return { type: "Feature", properties: { radiusKm }, geometry: { type: "Polygon", coordinates: [ring] } };}
function draw() { const km = Number(slider.value); readout.textContent = `${km.toFixed(1)} km`; mapEl.updateLayerData("location-radius", circle(center, km));}
mapEl.mapReady().then(() => { draw(); slider.addEventListener("input", draw);});What to notice
Section titled “What to notice”- A circle that never changes needs no JS: precompute the polygon and paste it into
data:— the page is then pure YAML. - The two layers share one named source, so one
updateLayerDatacall redraws both.