Draw a Circle in Maptoolkit Maps JS
Maps JS does not have a native circle geometry type for geographic radii, so circles are drawn by generating a GeoJSON polygon with many sides that approximates a circle at the correct radius in meters. Turf.js provides a circle helper that handles the geodesic math. Use this technique to draw coverage areas, proximity zones, search radii, or any geographic region defined by a center point and distance.
const API_KEY = 'YOUR_API_KEY';
function createGeodesicCircle(center, radiusKm, steps = 64) {
const coords = [];
for (let i = 0; i <= steps; i++) {
const angle = (i / steps) * 2 * Math.PI;
const dx = radiusKm / 111.32;
const dy = radiusKm / (111.32 * Math.cos(center[1] * Math.PI / 180));
coords.push([center[0] + dy * Math.sin(angle), center[1] + dx * Math.cos(angle)]);
}
return { type: 'Feature', geometry: { type: 'Polygon', coordinates: [coords] } };
}
const map = new maptoolkit.Map({
container: 'map',
apiKey: API_KEY,
style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
center: [11.39085, 47.27574],
zoom: 11,
attributionControl: { compact: false }
});
map.addControl(new maptoolkit.NavigationControl(), 'top-right');
map.on('load', () => {
const circle = createGeodesicCircle([11.39085, 47.27574], 5);
map.addSource('circle', { type: 'geojson', data: circle });
map.addLayer({
id: 'circle-fill',
type: 'fill',
source: 'circle',
paint: { 'fill-color': '#3887be', 'fill-opacity': 0.3 }
});
map.addLayer({
id: 'circle-outline',
type: 'line',
source: 'circle',
paint: { 'line-color': '#3887be', 'line-width': 2 }
});
});<!DOCTYPE html>
<html lang="en">
<head>
<title>Draw a Circle - Maptoolkit Maps JS</title>
<meta property="og:description" content="Draw a geographic circle using a GeoJSON polygon approximation." />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/@maptoolkit/maps@11.0.0-beta.3/dist/maptoolkit.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@maptoolkit/maps@11.0.0-beta.3/dist/maptoolkit.css" />
<style>
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
#map { width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
const API_KEY = 'YOUR_API_KEY';
function createGeodesicCircle(center, radiusKm, steps = 64) {
const coords = [];
for (let i = 0; i <= steps; i++) {
const angle = (i / steps) * 2 * Math.PI;
const dx = radiusKm / 111.32;
const dy = radiusKm / (111.32 * Math.cos(center[1] * Math.PI / 180));
coords.push([center[0] + dy * Math.sin(angle), center[1] + dx * Math.cos(angle)]);
}
return { type: 'Feature', geometry: { type: 'Polygon', coordinates: [coords] } };
}
const map = new maptoolkit.Map({
container: 'map',
apiKey: API_KEY,
style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
center: [11.39085, 47.27574],
zoom: 11,
attributionControl: { compact: false }
});
map.addControl(new maptoolkit.NavigationControl(), 'top-right');
map.on('load', () => {
const circle = createGeodesicCircle([11.39085, 47.27574], 5);
map.addSource('circle', { type: 'geojson', data: circle });
map.addLayer({
id: 'circle-fill',
type: 'fill',
source: 'circle',
paint: { 'fill-color': '#3887be', 'fill-opacity': 0.3 }
});
map.addLayer({
id: 'circle-outline',
type: 'line',
source: 'circle',
paint: { 'line-color': '#3887be', 'line-width': 2 }
});
});
</script>
</body>
</html>Use the prompt below with any LLM to get the same result. Make sure the Maptoolkit MCP server is connected first — check out AI Integration & MCP to get started.
How it works
There is no geographic circle geometry in the style spec, so a circle with a radius in
kilometers has to be built as a polygon that approximates one. A circle layer is not an
alternative: its radius is in screen pixels, so it stays the same size as you zoom rather
than covering a fixed area of ground.
The generator walks steps angles around the centre and converts the radius from kilometers
into degrees. The two axes are not the same: a degree of latitude is about 111.32 km
everywhere, while a degree of longitude shrinks toward the poles, which is why the longitude
offset is divided by the cosine of the latitude. Skip that and circles come out as ellipses
that get worse the further from the equator you go.
steps = 64 is the smoothness. Fewer is visibly polygonal at high zoom, many more costs
geometry for no visible gain.
For large radii, or exactness, turf.circle does the same on a proper geodesic.
Next steps
A radius is a proxy, and usually a poor one. If the circle means “within reach”, the Isochrone API returns the area actually reachable, following roads, which is a different shape and a truer answer.
If the radius is genuinely a distance, the next step is making it adjustable and testing your own points against it, which is what turns the circle into a query rather than a decoration.