地图上的半径圆是一个谎言。它告诉你从仓库距离 10 公里的每个点在相同的时间内是等可达的。实际上,沿着高速公路行驶 10 公里需要 8 分钟,穿过市中心 10 公里需要 35 分钟。圆在地理上是对称的;旅行不是。
等时线地图讲述真实。等时线是一个多边形,包含从原点在给定行驶时间内可以到达的每个点,遵循实际道路网络和速度限制。在道路速度快的地方,多边形向外膨胀。在道路速度慢或缺失的地方,它向内收缩。结果是对现实世界可达性的不规则、准确的表示。
这种分析多年来一直被大型零售商和物流公司使用,IKEA 使用行驶时间集水区分析来确定店铺规模,Deliveroo 使用配送等时线来设定区域边界,房产平台显示上市地址的通勤时间集水区。MapAtlas 路由 API 为任何拥有 API 密钥的开发者提供相同的功能。
本教程解释了等时线是什么,涵盖主要用途,并通过完整的 JavaScript 实现,在 MapAtlas 矢量地图上呈现多时间等时线(5、10 和 15 分钟)。总代码:约 60 行。
How the Isochrone API Works
The MapAtlas isochrone endpoint accepts a starting point (longitude, latitude), one or more time limits (in seconds), and a travel mode. It returns a GeoJSON FeatureCollection with one polygon per time limit.
Endpoint:
POST https://api.mapatlas.eu/v1/isochrone
Request body:
{
"locations": [[4.9041, 52.3676]],
"range": [900, 600, 300],
"range_type": "time",
"profile": "driving-car"
}
locations, array of[longitude, latitude]origin pointsrange, array of time limits in seconds (900 = 15 min, 600 = 10 min, 300 = 5 min). List largest first so polygons nest correctly.range_type,"time"for travel-time analysis,"distance"for distance-based isochronesprofile, travel mode:"driving-car","cycling-regular","foot-walking","driving-hgv"(heavy goods vehicle)
Response:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[ [4.87, 52.34], [4.92, 52.41], ... ]]
},
"properties": {
"value": 900,
"center": [4.9041, 52.3676],
"group_index": 0
}
},
{
"type": "Feature",
"geometry": { "type": "Polygon", "coordinates": [...] },
"properties": { "value": 600, ... }
},
{
"type": "Feature",
"geometry": { "type": "Polygon", "coordinates": [...] },
"properties": { "value": 300, ... }
}
]
}
Each feature's value property is the time limit in seconds. Use this to assign colors when rendering multiple nested polygons.
真实用途
配送区域映射。 电子商务和食品配送平台基于行驶时间而非距离定义区域。来自黑暗厨房的 30 分钟配送区域根据其是否在密集城市中心或郊区工业园区而覆盖完全不同的区域。等时线准确反映这一点。
店铺集水区分析。 零售选址始于「这个位置 20 分钟车程内住着多少人」。等时线给出多边形;人口普查数据给出其中的人口。这种组合推动主要零售商的选址决策。
通勤时间房产搜索。 房地产平台让用户搜索工作场所 X 分钟内的房产。这比基于半径的搜索具有显著更好的用户体验,因为它符合人们实际评估位置的方式。
医院和医疗保健覆盖。 医疗保健规划者使用等时线来识别距应急服务超过阈值时间的人口。来自多家医院的等时线可以结合起来显示覆盖缺口。
学校集水区。 地方当局和家长使用行驶时间等时线(通常是步行,不是开车)来理解学校位置的实际集水区。
活动营销。 向会议参加者展示从场地步行 10 分钟内可以到达的餐厅、酒店和景点。
房地产地图教程 和 路线优化教程 都使用等时线分析作为各自工作流程的组成部分。
Complete JavaScript Implementation
This implementation renders three nested isochrones (5, 10, 15 minutes) on a MapAtlas map. Click anywhere on the map to recalculate isochrones from that point.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Isochrone Map</title>
<link rel="stylesheet" href="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css" />
<style>
body, html { margin: 0; padding: 0; height: 100%; }
#map { width: 100%; height: 100vh; }
#controls {
position: absolute; top: 12px; left: 12px; z-index: 10;
background: white; padding: 12px 16px; border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.15); font-family: sans-serif; font-size: 14px;
}
#controls label { display: block; margin-bottom: 6px; }
#loading { color: #888; margin-top: 6px; display: none; }
</style>
</head>
<body>
<div id="controls">
<strong>Travel mode:</strong>
<label><input type="radio" name="mode" value="driving-car" checked> Driving</label>
<label><input type="radio" name="mode" value="foot-walking"> Walking</label>
<label><input type="radio" name="mode" value="cycling-regular"> Cycling</label>
<div id="loading">Calculating...</div>
<div style="margin-top:8px; color:#555;">Click the map to set origin.</div>
</div>
<div id="map"></div>
<script src="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.js"></script>
<script>
const API_KEY = 'YOUR_API_KEY';
const ISOCHRONE_URL = 'https://api.mapatlas.eu/v1/isochrone';
// Color palette for 5 / 10 / 15 minute zones
const ZONE_COLORS = {
900: { fill: '#e74c3c', opacity: 0.15, border: '#c0392b' }, // 15 min, red
600: { fill: '#f39c12', opacity: 0.20, border: '#d68910' }, // 10 min, orange
300: { fill: '#27ae60', opacity: 0.25, border: '#1e8449' }, // 5 min , green
};
// ── Initialize map ─────────────────────────────────────────────────────────────
const map = new mapmetricsgl.Map({
container: 'map',
style: `https://tiles.mapatlas.eu/styles/basic/style.json?key=${API_KEY}`,
center: [4.9041, 52.3676],
zoom: 11,
});
// ── Fetch isochrone from API ───────────────────────────────────────────────────
async function fetchIsochrone(lngLat, profile) {
const body = {
locations: [[lngLat.lng, lngLat.lat]],
range: [900, 600, 300], // 15, 10, 5 minutes in seconds
range_type: 'time',
profile,
};
const resp = await fetch(ISOCHRONE_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Api-Key': API_KEY },
body: JSON.stringify(body),
});
if (!resp.ok) throw new Error(`Isochrone API error: ${resp.status}`);
return resp.json();
}
// ── Render isochrones on map ───────────────────────────────────────────────────
function renderIsochrones(geojson) {
// Remove previous layers and source if they exist
['iso-fill-900', 'iso-fill-600', 'iso-fill-300',
'iso-line-900', 'iso-line-600', 'iso-line-300'].forEach(id => {
if (map.getLayer(id)) map.removeLayer(id);
});
if (map.getSource('isochrones')) map.removeSource('isochrones');
// Add GeoJSON source with all three polygons
map.addSource('isochrones', { type: 'geojson', data: geojson });
// Render each zone, largest first (so smaller zones appear on top)
[900, 600, 300].forEach(seconds => {
const colors = ZONE_COLORS[seconds];
// Fill layer
map.addLayer({
id: `iso-fill-${seconds}`,
type: 'fill',
source: 'isochrones',
filter: ['==', ['get', 'value'], seconds],
paint: {
'fill-color': colors.fill,
'fill-opacity': colors.opacity,
},
});
// Border layer
map.addLayer({
id: `iso-line-${seconds}`,
type: 'line',
source: 'isochrones',
filter: ['==', ['get', 'value'], seconds],
paint: {
'line-color': colors.border,
'line-width': 2,
'line-dasharray': seconds === 900 ? [1] : [4, 2],
},
});
});
}
// ── Add origin marker ─────────────────────────────────────────────────────────
let originMarker = null;
function setOriginMarker(lngLat) {
if (originMarker) originMarker.remove();
originMarker = new mapmetricsgl.Marker({ color: '#2c3e50' })
.setLngLat(lngLat)
.setPopup(new mapmetricsgl.Popup({ offset: 25 }).setHTML('<strong>Origin</strong>'))
.addTo(map);
}
// ── Click handler ─────────────────────────────────────────────────────────────
map.on('click', async (e) => {
const profile = document.querySelector('input[name="mode"]:checked').value;
const loading = document.getElementById('loading');
setOriginMarker(e.lngLat);
loading.style.display = 'block';
try {
const geojson = await fetchIsochrone(e.lngLat, profile);
renderIsochrones(geojson);
} catch (err) {
console.error('Isochrone error:', err);
alert('Could not calculate isochrone. Check the console for details.');
} finally {
loading.style.display = 'none';
}
});
// ── Load initial isochrone for Amsterdam Centraal ─────────────────────────────
map.on('load', async () => {
const defaultOrigin = { lng: 4.9001, lat: 52.3791 };
setOriginMarker(defaultOrigin);
try {
const geojson = await fetchIsochrone(defaultOrigin, 'driving-car');
renderIsochrones(geojson);
} catch (err) {
console.error('Initial isochrone error:', err);
}
});
</script>
</body>
</html>
Save this as index.html, replace YOUR_API_KEY, open it in a browser, and you'll see three colored zones around Amsterdam Centraal. Click anywhere on the map to recalculate from that point. Switch the radio buttons to compare driving, walking, and cycling isochrones for the same origin.
Walking vs. Driving Comparison
The travel mode parameter dramatically changes the isochrone shape. Compare 15-minute isochrones from the same origin:
Driving: Stretches 15–20 km along motorways, contracts to 3–5 km in congested urban areas. The polygon reflects the road network's hierarchy, fast motorways create tentacle-like extensions.
Walking: Near-circular, typically 1–1.5 km radius at normal walking pace (5 km/h). Urban obstacles (rivers, railways, limited crossings) create dents in the otherwise regular shape.
Cycling: Intermediate range, typically 3–5 km radius. More directional than walking but follows bike-path networks rather than roads, which in well-connected cities like Amsterdam or Copenhagen can be surprisingly expansive.
The API returns accurate mode-specific isochrones because it uses actual road network data with mode-appropriate speeds, not a simple average velocity applied to straight-line distance.
Multiple Origins
To show isochrones from multiple origins simultaneously, for example, coverage from two warehouse locations, merge the results into a single GeoJSON FeatureCollection and add properties to distinguish origins:
async function fetchMultiOriginIsochrones(origins, profile) {
const requests = origins.map(origin =>
fetchIsochrone({ lng: origin[0], lat: origin[1] }, profile)
);
const results = await Promise.all(requests);
// Merge all features, tagging each with its origin index
const merged = {
type: 'FeatureCollection',
features: results.flatMap((geojson, i) =>
geojson.features.map(f => ({
...f,
properties: { ...f.properties, origin_index: i },
}))
),
};
return merged;
}
// Example: Two Amsterdam warehouses
const origins = [
[4.8720, 52.3531], // Warehouse A, West Amsterdam
[4.9441, 52.3599], // Warehouse B, East Amsterdam
];
map.on('load', async () => {
const geojson = await fetchMultiOriginIsochrones(origins, 'driving-car');
// Use origin_index in filter expressions to color each origin differently
renderIsochrones(geojson);
});
The union of overlapping isochrones shows your combined service area. The non-overlapping portions reveal which zones are served by only one origin, useful for identifying gaps or over-capacity areas.
Integration With Real Estate and Logistics Applications
For real estate applications, isochrones solve the commute-time property search problem elegantly. A user enters their workplace address, selects a maximum commute time, and the map shows the catchment polygon. Any property listing whose coordinates fall inside the polygon qualifies. This is far more useful than "within 10 km", it reflects how commuters actually think.
The Real Estate Property Map with Clustering tutorial shows how to render property listings as map markers. Adding isochrone-based filtering on top of that implementation is a natural extension: calculate the isochrone, then use a point-in-polygon test to filter which markers to show.
For logistics, isochrones define delivery zones. The Route Optimization API tutorial covers how to sequence multiple stops within a zone after the zone itself is defined. These two APIs are designed to work together: isochrones define where you serve, the routing API optimizes how you serve it.
The Travel Time Analysis capabilities page has additional documentation on the isochrone endpoint, including distance-based isochrones (useful when travel time data is unavailable), multi-modal analysis, and response format details.
For businesses considering delivery operations, the Logistics & Delivery solutions page covers how mapping APIs fit into delivery workflow architecture. And for property platforms, the Real Estate & Property solutions page explains typical integration patterns.
Performance Notes
Isochrone calculations are computationally intensive, the API processes the full road network graph to determine reachability. Response times are typically 200–800ms depending on the time limit (larger time limits = larger graph traversal) and travel mode (driving traverses more roads than walking).
For production applications:
- Cache isochrone results for common queries (fixed origin + time limit). A delivery zone from a fixed warehouse location doesn't change frequently, cache it for hours or days.
- Limit simultaneous requests. If your UI allows clicking rapidly, debounce click handlers by 500ms to avoid triggering multiple simultaneous API requests.
- Use shorter time limits for interactive maps. A 5-minute walking isochrone returns in under 200ms. A 60-minute driving isochrone may take 600–800ms. Match the time limit to the use case.
Summary
Isochrone maps show what's actually reachable in a given time from a given point, accounting for road networks, travel speeds, and mode of transport. A radius circle cannot.
The MapAtlas Routing API returns isochrones as GeoJSON polygons that you render as fill layers on a vector map. The implementation in this tutorial:
- Makes a POST request to the
/isochroneendpoint with a list of time limits - Receives GeoJSON feature collection with one polygon per time limit
- Renders each polygon as a
fillandlinelayer with distinct colors - Supports driving, walking, and cycling modes
- Handles map click events to recalculate from any origin
The complete code runs in a single HTML file, no build step, no dependencies beyond the MapAtlas SDK.
Sign up for a free MapAtlas API key to get started. The Routing and Isochrone APIs are included in all plans, no credit card required to start building.

