@tileflow/routing 0.1.0-alpha.0 → 0.1.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -2
- package/dist/{chunk-MCATD4FU.js → chunk-3P5UQQH3.js} +8 -3
- package/dist/{chunk-ECBK2QPL.js → chunk-CDZKLI32.js} +33 -2
- package/dist/client.d.ts +3 -2
- package/dist/client.js +2 -2
- package/dist/contract.d.ts +7 -1
- package/dist/contract.js +1 -1
- package/dist/index.js +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -47,12 +47,14 @@ for (const step of fastest?.legs[0]?.steps ?? []) console.log(step.instruction);
|
|
|
47
47
|
|
|
48
48
|
**Request:**
|
|
49
49
|
|
|
50
|
-
- `locations`: two to
|
|
50
|
+
- `locations`: two to 25 `[longitude, latitude]` positions: an origin, optional stops and a
|
|
51
51
|
destination. Each stretch between consecutive positions is a leg.
|
|
52
52
|
- `mode`: `driving`, `walking`, `cycling` or `truck`.
|
|
53
53
|
- `alternatives` (optional, 0–2): only between two positions. Fewer come back when no distinct
|
|
54
54
|
alternative exists.
|
|
55
55
|
- `language` (optional): writes the instructions in one of `routingLanguages`.
|
|
56
|
+
- `optimize` (optional): see [Visit stops in the fastest order](#visit-stops-in-the-fastest-order).
|
|
57
|
+
- `departAt` (optional): see [Choose a departure time](#choose-a-departure-time).
|
|
56
58
|
- `vehicle`, `avoid` and `traffic` (optional): see
|
|
57
59
|
[Route a truck around tolls](#route-a-truck-around-tolls).
|
|
58
60
|
|
|
@@ -67,6 +69,41 @@ for (const step of fastest?.legs[0]?.steps ?? []) console.log(step.instruction);
|
|
|
67
69
|
|
|
68
70
|
Durations are estimates without traffic; see [Travel times and traffic](#travel-times-and-traffic).
|
|
69
71
|
|
|
72
|
+
## Visit stops in the fastest order
|
|
73
|
+
|
|
74
|
+
Set `optimize` to visit the stops between the first and the last location in the fastest order the
|
|
75
|
+
service finds. The first and the last location stay in place; repeat the first to return to it.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import {route} from '@tileflow/routing';
|
|
79
|
+
|
|
80
|
+
const stops: [number, number][] = [
|
|
81
|
+
[-80.1918, 25.7617],
|
|
82
|
+
[-80.1312, 25.7795],
|
|
83
|
+
[-80.2569, 25.7215],
|
|
84
|
+
[-80.2195, 25.7655],
|
|
85
|
+
[-80.1918, 25.7617],
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
const answer = await route({locations: stops, mode: 'driving', optimize: true}, {apiKey});
|
|
89
|
+
|
|
90
|
+
// The requested index of each location, in the order the route visits them.
|
|
91
|
+
for (const index of answer.locationOrder ?? []) console.log(stops[index]);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The legs of the route follow `locationOrder`. Every two locations must lie within the reach of a
|
|
95
|
+
[matrix](#calculate-a-travel-time-matrix) for the mode: 200 km driving, 10 km walking or
|
|
96
|
+
cycling. An optimized route counts 10 API units, whatever its locations; a route in the given
|
|
97
|
+
order counts 5.
|
|
98
|
+
|
|
99
|
+
## Choose a departure time
|
|
100
|
+
|
|
101
|
+
`departAt` says when the route leaves the first location, as `YYYY-MM-DDTHH:mm` in that location's
|
|
102
|
+
local time. The route then respects roads closed at that time where OpenStreetMap records it, and
|
|
103
|
+
each leg states `arriveAt`: its local time at the end of the leg with the UTC offset, such as
|
|
104
|
+
`2027-03-01T16:47-06:00`. It does not add traffic: durations stay
|
|
105
|
+
[without traffic](#travel-times-and-traffic), whatever the time.
|
|
106
|
+
|
|
70
107
|
## Route a truck around tolls
|
|
71
108
|
|
|
72
109
|
<!-- docs:check -->
|
|
@@ -305,7 +342,7 @@ export function showRoute(map: RouteMap, answer: RoutingRouteResponse) {
|
|
|
305
342
|
|
|
306
343
|
To colour the route with the map's theme and draw it on progressively, use Core's route inks and
|
|
307
344
|
line progress; see the
|
|
308
|
-
[route drawing guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/
|
|
345
|
+
[route drawing guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/README.md).
|
|
309
346
|
Keep the answer's `attribution` visible with the route. Like the map, routes come from
|
|
310
347
|
OpenStreetMap data.
|
|
311
348
|
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
routingNearestResponseSchema,
|
|
13
13
|
routingRouteRequestSchema,
|
|
14
14
|
routingRouteResponseSchema
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-CDZKLI32.js";
|
|
16
16
|
|
|
17
17
|
// src/client.ts
|
|
18
18
|
import { z } from "zod";
|
|
@@ -30,16 +30,21 @@ var RoutingError = class extends Error {
|
|
|
30
30
|
this.retryAfterSeconds = input.retryAfterSeconds ?? null;
|
|
31
31
|
}
|
|
32
32
|
};
|
|
33
|
+
function answersOrder(order, locations, optimize) {
|
|
34
|
+
if (!optimize) return order === void 0;
|
|
35
|
+
return order?.length === locations && new Set(order).size === locations && order[0] === 0 && order.at(-1) === locations - 1 && order.every((index) => index < locations);
|
|
36
|
+
}
|
|
33
37
|
async function route(request, options) {
|
|
34
38
|
const parsed = routingRouteRequestSchema.safeParse(request);
|
|
35
39
|
if (!parsed.success) throw new Error("Invalid Tileflow route request");
|
|
36
|
-
const
|
|
40
|
+
const { locations, optimize } = parsed.data;
|
|
41
|
+
const legs = locations.length - 1;
|
|
37
42
|
return requestRouting(
|
|
38
43
|
"/v1/routing/route",
|
|
39
44
|
parsed.data,
|
|
40
45
|
options,
|
|
41
46
|
routingRouteResponseSchema,
|
|
42
|
-
(response) => response.routes.length <= (parsed.data.alternatives ?? 0) + 1 && response.routes.every((candidate) => candidate.legs.length === legs)
|
|
47
|
+
(response) => response.routes.length <= (parsed.data.alternatives ?? 0) + 1 && response.routes.every((candidate) => candidate.legs.length === legs) && answersOrder(response.locationOrder, locations.length, optimize)
|
|
43
48
|
);
|
|
44
49
|
}
|
|
45
50
|
async function isochrone(request, options) {
|
|
@@ -42,7 +42,7 @@ var routingLimits = Object.freeze({
|
|
|
42
42
|
maximumRequestBytes: 8 * 1024,
|
|
43
43
|
maximumResponseBytes: 4 * 1024 * 1024,
|
|
44
44
|
minimumLocations: 2,
|
|
45
|
-
maximumLocations:
|
|
45
|
+
maximumLocations: 25,
|
|
46
46
|
maximumAlternatives: 2,
|
|
47
47
|
/** Sum of straight-line distances between consecutive locations, per mode. */
|
|
48
48
|
maximumDirectDistanceMeters: Object.freeze({
|
|
@@ -114,11 +114,22 @@ function refineTravelOptions(request, context) {
|
|
|
114
114
|
context.addIssue({ code: "custom", path: ["avoid"], message: "Avoid each kind once." });
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
+
var localDateTime = z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/u, "Use YYYY-MM-DDTHH:mm, local to the first location.").refine((value) => {
|
|
118
|
+
const date = /* @__PURE__ */ new Date(`${value}:00Z`);
|
|
119
|
+
return !Number.isNaN(date.getTime()) && date.toISOString().startsWith(value);
|
|
120
|
+
}, "This date does not exist.");
|
|
117
121
|
var routingRouteRequestSchema = z.object({
|
|
118
122
|
locations: z.array(routingPositionSchema).min(routingLimits.minimumLocations).max(routingLimits.maximumLocations),
|
|
119
123
|
...travelOptions,
|
|
120
124
|
alternatives: z.number().int().min(0).max(routingLimits.maximumAlternatives).optional(),
|
|
121
|
-
language: z.enum(routingLanguages).optional()
|
|
125
|
+
language: z.enum(routingLanguages).optional(),
|
|
126
|
+
/**
|
|
127
|
+
* Visits the stops between the first and the last location in the fastest order the engine
|
|
128
|
+
* finds. The first and the last location stay in place; repeat the first to return to it.
|
|
129
|
+
*/
|
|
130
|
+
optimize: z.boolean().optional(),
|
|
131
|
+
/** When the route leaves the first location, in that location's local time. */
|
|
132
|
+
departAt: localDateTime.optional()
|
|
122
133
|
}).strict().superRefine((request, context) => {
|
|
123
134
|
refineTravelOptions(request, context);
|
|
124
135
|
if ((request.alternatives ?? 0) > 0 && request.locations.length !== 2) {
|
|
@@ -128,6 +139,20 @@ var routingRouteRequestSchema = z.object({
|
|
|
128
139
|
message: "Alternatives are available only between two locations."
|
|
129
140
|
});
|
|
130
141
|
}
|
|
142
|
+
if (request.optimize) {
|
|
143
|
+
const limit = routingLimits.maximumMatrixDistanceMeters[request.mode];
|
|
144
|
+
const tooFar = request.locations.some(
|
|
145
|
+
(a, index) => request.locations.slice(index + 1).some((b) => routingDirectDistance(a, b) > limit)
|
|
146
|
+
);
|
|
147
|
+
if (tooFar) {
|
|
148
|
+
context.addIssue({
|
|
149
|
+
code: "custom",
|
|
150
|
+
path: ["locations"],
|
|
151
|
+
message: `Two locations are too far apart to optimize for ${request.mode}.`
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
131
156
|
let distance = 0;
|
|
132
157
|
for (let index = 1; index < request.locations.length; index += 1) {
|
|
133
158
|
distance += routingDirectDistance(request.locations[index - 1], request.locations[index]);
|
|
@@ -279,6 +304,8 @@ var routingLineStringSchema = z.object({
|
|
|
279
304
|
var routingLegSchema = z.object({
|
|
280
305
|
distance: meters,
|
|
281
306
|
duration: seconds,
|
|
307
|
+
/** With `departAt`: when the leg ends, in local time at its end, with that UTC offset. */
|
|
308
|
+
arriveAt: z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}[+-]\d{2}:\d{2}$/u).optional(),
|
|
282
309
|
steps: z.array(routingStepSchema).min(1).max(routingLimits.maximumSteps)
|
|
283
310
|
}).strict();
|
|
284
311
|
var routingRouteSchema = z.object({
|
|
@@ -351,6 +378,10 @@ var answer = {
|
|
|
351
378
|
var routingRouteResponseSchema = z.object({
|
|
352
379
|
...answer,
|
|
353
380
|
routes: z.array(routingRouteSchema).min(1).max(routingLimits.maximumAlternatives + 1),
|
|
381
|
+
/** With `optimize`: the requested index of each location, in the order the route visits them. */
|
|
382
|
+
locationOrder: z.array(
|
|
383
|
+
z.number().int().min(0).max(routingLimits.maximumLocations - 1)
|
|
384
|
+
).min(routingLimits.minimumLocations).max(routingLimits.maximumLocations).optional(),
|
|
354
385
|
usage: routingUsageSchema
|
|
355
386
|
}).strict();
|
|
356
387
|
var routingIsochroneResponseSchema = z.object({ ...answer, isochrones: routingIsochroneCollectionSchema, usage: routingUsageSchema }).strict();
|
package/dist/client.d.ts
CHANGED
|
@@ -30,9 +30,10 @@ declare class RoutingError extends Error {
|
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
* The fastest route through two to
|
|
33
|
+
* The fastest route through two to 25 positions, with up to two alternatives between two
|
|
34
34
|
* positions. Each route has its line, distance, duration, turn-by-turn steps and whether it uses
|
|
35
|
-
* tolls, highways or ferries.
|
|
35
|
+
* tolls, highways or ferries. With `optimize`, the stops between the first and the last position
|
|
36
|
+
* are visited in the fastest order found, which `locationOrder` states.
|
|
36
37
|
*/
|
|
37
38
|
declare function route(request: RoutingRouteRequest, options: RoutingOptions): Promise<RoutingRouteResponse>;
|
|
38
39
|
/** The areas reachable from a position in one to four times, one polygon per time. */
|
package/dist/client.js
CHANGED
package/dist/contract.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ declare const routingLimits: Readonly<{
|
|
|
22
22
|
maximumRequestBytes: number;
|
|
23
23
|
maximumResponseBytes: number;
|
|
24
24
|
minimumLocations: 2;
|
|
25
|
-
maximumLocations:
|
|
25
|
+
maximumLocations: 25;
|
|
26
26
|
maximumAlternatives: 2;
|
|
27
27
|
/** Sum of straight-line distances between consecutive locations, per mode. */
|
|
28
28
|
maximumDirectDistanceMeters: Readonly<{
|
|
@@ -132,6 +132,8 @@ declare const routingRouteRequestSchema: z.ZodObject<{
|
|
|
132
132
|
"uk-UA": "uk-UA";
|
|
133
133
|
"vi-VN": "vi-VN";
|
|
134
134
|
}>>;
|
|
135
|
+
optimize: z.ZodOptional<z.ZodBoolean>;
|
|
136
|
+
departAt: z.ZodOptional<z.ZodString>;
|
|
135
137
|
}, z.core.$strict>;
|
|
136
138
|
type RoutingRouteRequest = z.input<typeof routingRouteRequestSchema>;
|
|
137
139
|
type NormalizedRoutingRouteRequest = z.output<typeof routingRouteRequestSchema>;
|
|
@@ -282,6 +284,7 @@ declare const routingLineStringSchema: z.ZodObject<{
|
|
|
282
284
|
declare const routingLegSchema: z.ZodObject<{
|
|
283
285
|
distance: z.ZodNumber;
|
|
284
286
|
duration: z.ZodNumber;
|
|
287
|
+
arriveAt: z.ZodOptional<z.ZodString>;
|
|
285
288
|
steps: z.ZodArray<z.ZodObject<{
|
|
286
289
|
maneuver: z.ZodEnum<{
|
|
287
290
|
arrive: "arrive";
|
|
@@ -338,6 +341,7 @@ declare const routingRouteSchema: z.ZodObject<{
|
|
|
338
341
|
legs: z.ZodArray<z.ZodObject<{
|
|
339
342
|
distance: z.ZodNumber;
|
|
340
343
|
duration: z.ZodNumber;
|
|
344
|
+
arriveAt: z.ZodOptional<z.ZodString>;
|
|
341
345
|
steps: z.ZodArray<z.ZodObject<{
|
|
342
346
|
maneuver: z.ZodEnum<{
|
|
343
347
|
arrive: "arrive";
|
|
@@ -480,6 +484,7 @@ declare const routingRouteResponseSchema: z.ZodObject<{
|
|
|
480
484
|
legs: z.ZodArray<z.ZodObject<{
|
|
481
485
|
distance: z.ZodNumber;
|
|
482
486
|
duration: z.ZodNumber;
|
|
487
|
+
arriveAt: z.ZodOptional<z.ZodString>;
|
|
483
488
|
steps: z.ZodArray<z.ZodObject<{
|
|
484
489
|
maneuver: z.ZodEnum<{
|
|
485
490
|
arrive: "arrive";
|
|
@@ -524,6 +529,7 @@ declare const routingRouteResponseSchema: z.ZodObject<{
|
|
|
524
529
|
}, z.core.$strict>>;
|
|
525
530
|
}, z.core.$strict>>;
|
|
526
531
|
}, z.core.$strict>>;
|
|
532
|
+
locationOrder: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
|
|
527
533
|
usage: z.ZodObject<{
|
|
528
534
|
units: z.ZodNumber;
|
|
529
535
|
}, z.core.$strict>;
|
package/dist/contract.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
matrix,
|
|
6
6
|
nearest,
|
|
7
7
|
route
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-3P5UQQH3.js";
|
|
9
9
|
import {
|
|
10
10
|
routingAttribution,
|
|
11
11
|
routingAvoidances,
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
routingTrafficKinds,
|
|
42
42
|
routingUsageSchema,
|
|
43
43
|
routingVehicleSchema
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-CDZKLI32.js";
|
|
45
45
|
export {
|
|
46
46
|
RoutingError,
|
|
47
47
|
isochrone,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tileflow/routing",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Routes, isochrones, matrices, map matching and nearest-road client and wire contracts for Tileflow.",
|
|
6
6
|
"type": "module",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"access": "public"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"zod": "^4.
|
|
55
|
+
"zod": "^4.6.7"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsup src/index.ts src/contract.ts src/client.ts --format esm --dts --clean --splitting && node ../../scripts/canonicalize-zod-enums.mjs dist",
|