This is a limitation of the underlying backend service. The latest version of the route API is based on the Bing Maps routing service. It has more data but lower limits on the number of waypoints. The only way to get around this limitation is to split your waypoints up into multiple requests, then merge the results. A batch route request could be used to minimize the number of HTTP connections created and reduce the risk of hitting QPS limits.Here is a modified version of your code that makes multiple route requests:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Azure Maps Route API</title>
<script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"></script>
<link rel="stylesheet" href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css">
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
margin: 20px;
}
#map {
width: 100%;
height: 600px;
margin-top: 20px;
border: 2px solid #ccc;
border-radius: 10px;
}
button {
padding: 10px 20px;
font-size: 16px;
background-color: #0078D4;
color: white;
border: none;
cursor: pointer;
border-radius: 5px;
}
button:hover {
background-color: #005A9E;
}
</style>
</head>
<body>
<h2>Azure Maps Route API - New York State</h2>
<button onclick="getRoute()">Get Route</button>
<div id="map"></div>
<script>
const subscriptionKey = "<YOUR_AZURE_MAPS_KEY>"; // Replace with your Azure Maps subscription key
let map = new atlas.Map("map", {
center: [-75.5, 42.9], // Centered in New York State
zoom: 7,
view: "Auto",
authOptions: {
authType: "subscriptionKey",
subscriptionKey: subscriptionKey
}
});
function getSubRoute(waypoints){
console.log(`Sub-route request created with ${waypoints.length} waypoints.`);
return new Promise(async (resolve, reject) => {
const url = "https://atlas.microsoft.com/route/directions?api-version=2025-01-01";
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Subscription-Key": subscriptionKey
},
body: JSON.stringify({
"type": "FeatureCollection",
"features": waypoints.map((coords, index) => ({
"type": "Feature",
"geometry": { "coordinates": coords, "type": "Point" },
"properties": { "pointIndex": index, "pointType": "waypoint" }
})),
"routeOutputOptions": ["routePath","itinerary"]
})
});
if (!response.ok) {
reject(Error(`HTTP error! Status: ${response.status} - ${await response.text()}`));
} else {
const data = await response.json();
resolve(data);
}
} catch (error) {
reject(Error("Error fetching route:", error.message));
}
});
}
async function getRoute() {
const url = "https://atlas.microsoft.com/route/directions?api-version=2025-01-01";
// 26 waypoints within New York State
const waypoints = [
[-74.006, 40.7128], [-73.9352, 40.7306], [-73.9808, 40.7648], [-73.9982, 40.7265],
[-74.0355, 40.7282], [-74.0589, 40.7458], [-74.0781, 40.7608], [-74.1211, 40.8362],
[-74.1686, 40.8471], [-74.1915, 40.8598], [-74.2003, 40.8798], [-74.2131, 40.8952],
[-74.2331, 40.9108], [-74.2525, 40.9202], [-74.2728, 40.9328], [-74.3002, 40.9501],
[-74.3282, 40.9608], [-74.3525, 40.9705], [-74.3811, 40.9801], [-74.4005, 40.9902],
[-74.4258, 41.0001], [-74.4505, 41.0108], [-74.4752, 41.0205], [-74.4982, 41.0301],
[-74.5202, 41.0408], [-74.3122, 40.967]
];
try {
let data = null;
if(waypoints.length <= 25){
data = await getSubRoute(waypoints);
} else {
var routePromises = [];
//Need to split into subsets then merge results.
const batchSize = 24;
let startIdx = 0;
let endIdx = 0;
while(endIdx < waypoints.length - 1){
endIdx = Math.min(endIdx + batchSize, waypoints.length - 1);
routePromises.push(getSubRoute(waypoints.slice(startIdx, endIdx + 1)));
console.log(`Sub-route from waypoint ${startIdx} to ${endIdx}`);
startIdx = endIdx;
}
//Requests the routes in parallel (alternatively use the batch route service).
var results = await Promise.all(routePromises);
//Merge the responses.
data = {
"type": "FeatureCollection",
features: []
};
var routePath = null;
var legOffset = 0;
var timeOffset = 0;
for(var i=0;i<results.length;i++){
//Route data contains a lot of index info that will need to offset based on which route response it belongs to.
const idxOffset = i * batchSize;
//Extract and merge the points.
for(var j=0;j<results[i].features.length;j++){
const f = results[i].features[i];
if(f.geometry.type === 'Point'){
//Offset the leg indices.
f.properties.routePathPoint.legIndex += legOffset;
f.properties.steps.forEach(s => {
s.legIndex += legOffset;
});
data.features.push(f);
}
}
//Get the route path.
const path = results[i].features.filter(f => f.properties.type == 'RoutePath')[0];
//Merge the route path.
if(routePath == null){
routePath = path;
} else {
//Each leg is it's own line in the MultiLineString.
//Loop through the line coordinates of the path and append to the route path.
path.geometry.coordinates.forEach(l => {
routePath.geometry.coordinates.push(l);
});
//Merge the bounding box of the route path.
routePath.geometry.bbox[0] = Math.min(routePath.geometry.bbox[0], path.geometry.bbox[0]);
routePath.geometry.bbox[1] = Math.min(routePath.geometry.bbox[1], path.geometry.bbox[1]);
routePath.geometry.bbox[2] = Math.max(routePath.geometry.bbox[2], path.geometry.bbox[2]);
routePath.geometry.bbox[3] = Math.max(routePath.geometry.bbox[3], path.geometry.bbox[3]);
//Update metadata properties.
routePath.properties.distanceInMeters += path.properties.distanceInMeters;
routePath.properties.durationInSeconds += path.properties.durationInSeconds;
if(routePath.properties.durationTrafficInSeconds){
routePath.properties.durationTrafficInSeconds += path.properties.durationTrafficInSeconds;
}
//Off set the leg indices and add the route path legs.
path.properties.legs.forEach(l => {
l.legIndex += legOffset;
//Offset arrivalAt/departureAt time of leg.
var d = new Date(routePath.properties.departureAt);
d.setSeconds(d.getSeconds() + timeOffset);
routePath.properties.departureAt = d.toISOString();
d.setSeconds(d.getSeconds() + (l.durationTrafficInSeconds || l.durationInSeconds));
routePath.properties.arrivalAt = d.toISOString();
//Offset the time offset for this leg.
timeOffset += l.durationTrafficInSeconds;
routePath.properties.legs.push(l);
});
}
legOffset += path.properties.legs.length;
timeOffset = routePath.properties.durationInSeconds;
}
//Update the route paths arrivalAt time based on the known departureAt and the durationTrafficInSeconds.
var d = new Date(routePath.properties.departureAt);
d.setSeconds(d.getSeconds() + (routePath.properties.durationTrafficInSeconds ||routePath.properties.durationInSeconds));
routePath.properties.arrivalAt = d.toISOString();
//Add the route path the merged data.
data.features.push(routePath);
}
if (data && data.features && data.features.length > 0) {
const dataSource = new atlas.source.DataSource();
map.sources.add(dataSource);
// Add route line
dataSource.add(new atlas.data.LineString(waypoints));
map.layers.add(new atlas.layer.LineLayer(dataSource, null, {
strokeColor: "red",
strokeWidth: 3
}));
// Add waypoints as markers with labels A, B, C...
waypoints.forEach((coord, index) => {
dataSource.add(new atlas.data.Feature(new atlas.data.Point(coord), {
label: String.fromCharCode(65 + index) // Converts 0 → 'A', 1 → 'B', etc.
}));
});
// Add custom marker icon
map.imageSprite.add("marker_icon", "<svg xmlns='http://www.w3.org/2000/svg' width='26' height='28' viewBox='-4 0 36 36'><g fill='none' fill-rule='evenodd'><path d='M14 0c7.732 0 14 5.641 14 12.6C28 23.963 14 36 14 36S0 24.064 0 12.6C0 5.641 6.268 0 14 0Z' fill='#fff' stroke='#000' stroke-width='1'/><circle fill='dodgerblue' fill-rule='nonzero' cx='14' cy='14' r='10' /></g></svg>");
// Add SymbolLayer for displaying markers with labels
map.layers.add(new atlas.layer.SymbolLayer(dataSource, null, {
iconOptions: {
image: "marker_icon",
anchor: "bottom",
allowOverlap: true,
ignorePlacement: true
},
textOptions: {
textField: ["get", "label"], // Get waypoint label from properties
color: "black",
offset: [0, -0.7], // Adjust label position
font: ["StandardFont-Bold"],
allowOverlap: true
}
}));
// Adjust map view to fit the route
map.setCamera({ bounds: atlas.data.BoundingBox.fromPositions(waypoints), padding: 50 });
} else {
console.error("No route data returned.");
alert("No route data returned.");
}
} catch (error) {
console.error("Error fetching route:" + error.message);
alert("Route API Error: " + error.message);
}
}
</script>
</body>
</html>