Skip to content

Meeting point

Four friends in different parts of Paris want to meet somewhere fair. POST /api/v1/centroid takes their locations and returns the point on the road network where the sum of everyone’s travel time is smallest, along with how long each person takes to get there.

The meeting point is a premium endpoint: each request counts as 4x.

Terminal window
curl -X POST https://plaza.fyi/api/v1/centroid \
-H "x-api-key: pk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"locations": [
{ "type": "Point", "coordinates": [2.294, 48.858] },
{ "type": "Point", "coordinates": [2.350, 48.870] },
{ "type": "Point", "coordinates": [2.340, 48.845] },
{ "type": "Point", "coordinates": [2.370, 48.860] }
],
"mode": "auto",
"metric": "time"
}'
ParameterTypeDefaultDescription
locationsarray of GeoJSON Pointsrequired2 to 50 starting points
modestringautoauto, foot, bicycle, truck, motorcycle or motor_scooter
truck, bicycle, pedestrian, motorcycle, motor_scooterobjectnoneThe mode’s options, as on Directions. Sent without its mode, a 422
metricstringtimeTotal to minimise: time or distance
excludestring or arraynoneRoad types to avoid, same values as directions
datasetsstring or arraynoneAlso travel the roads in these datasets, up to 20 slugs or IDs. See your own roads
avoid_datasetsstring or arraynoneKeep out of the polygons of these datasets, as on directions

A GeoJSON Point Feature at the meeting point:

{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [2.3361, 48.8617] },
"properties": {
"mode": "auto",
"metric": "time",
"total_duration_s": 2104.6,
"total_distance_m": 13870.2,
"max_duration_s": 702.3,
"costs_per_location": [
{ "location_index": 0, "duration_s": 702.3, "distance_m": 4410.0 },
{ "location_index": 1, "duration_s": 455.1, "distance_m": 2980.5 },
{ "location_index": 2, "duration_s": 390.8, "distance_m": 2650.3 },
{ "location_index": 3, "duration_s": 556.4, "distance_m": 3829.4 }
]
}
}

costs_per_location is in the same order as locations. max_duration_s is the longest single trip, handy when you care about the person who travels furthest.

Every location travels to the meeting point, so one-way streets count in the direction people actually drive them, and so do turn restrictions. Plaza searches the road network inside your locations’ bounding box, widened by 0.2° on every side. That is about 22 km north and south; east and west it shrinks with latitude (about 22 km at the equator, 14 km at 50°N). It finds every intersection all of them can reach and keeps the one with the lowest total. When two candidates tie, the one with the shorter longest trip wins.

Two limits keep the search bounded:

  • The locations must lie within 2° of each other in latitude and in longitude, roughly a metro area or two.
  • Nobody’s trip may exceed 2 hours (150 km with metric: "distance"). Points further than that from any one location aren’t considered.

metric: "distance" minimises total road distance instead of time. The two can disagree: a fast ring road might win on time while a slow, direct street wins on distance. The response always reports both.

StatusWhen
400Fewer than 2 or more than 50 locations, locations more than 2° apart, an unknown metric or exclude value
404 not_foundA location is too far from any road, or no point is reachable from every location within the 2-hour (150 km) trip limit, for example when two of them are on islands with no ferry
408 request_timeoutThe search ran past your plan’s request timeout
$meet = centroid(
locations: [point(48.858, 2.294), point(48.870, 2.350), point(48.845, 2.340)],
mode: "foot"
);
search(node, amenity: "cafe").around(300, $meet).limit(5);

centroid() takes the same locations, mode, metric, exclude, datasets and avoid_datasets. As an output it returns the same Feature as the REST endpoint. Because it is a point, you can use it as a geometry, as above: find cafes near the meeting point in one query. The centroid still runs, so that query is billed as one meeting-point request.