@unmap/routing 0.3.0 → 0.5.0
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 +2 -2
- package/dist/index.d.ts +39 -10
- package/dist/index.js +43 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @unmap/routing
|
|
2
2
|
|
|
3
3
|
Typed client for the [unmap](https://unmap.dev) routing API: point-to-point routes and
|
|
4
|
-
travel-time isochrones over the Canadian road graph, for driving, cycling, and walking. No map
|
|
4
|
+
travel-time isochrones over the Canadian road graph, for driving, trucking, cycling, and walking. No map
|
|
5
5
|
dependency; works in Node and the browser.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
@@ -22,7 +22,7 @@ const bands = await router.isochrone([-114.07, 51.05], { minutes: [10, 20], mode
|
|
|
22
22
|
bands.features // one GeoJSON polygon per minute value
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Coordinates are always `[lng, lat]`. `mode` is `'auto' | 'bicycle' | 'pedestrian'` and defaults
|
|
25
|
+
Coordinates are always `[lng, lat]`. `mode` is `'auto' | 'car' | 'bicycle' | 'pedestrian' | 'truck'` and defaults
|
|
26
26
|
to `'auto'`; `minutes` is required and takes up to four values of at most 120 minutes. Both methods accept an
|
|
27
27
|
`AbortSignal` as `signal`. A non-2xx response throws a `RouterError` carrying the HTTP `status`.
|
|
28
28
|
|
package/dist/index.d.ts
CHANGED
|
@@ -3,9 +3,39 @@ interface RouterOptions {
|
|
|
3
3
|
gateway?: string;
|
|
4
4
|
fetch?: typeof fetch;
|
|
5
5
|
}
|
|
6
|
-
|
|
6
|
+
/** `car` is the same costing as `auto`, spelled for people who do not think in Valhalla terms. */
|
|
7
|
+
type TravelMode = "auto" | "car" | "bicycle" | "pedestrian" | "truck";
|
|
7
8
|
/** [longitude, latitude]. */
|
|
8
9
|
type LngLat = [number, number];
|
|
10
|
+
/**
|
|
11
|
+
* Vehicle profile for `mode: "truck"`. Metric throughout: metres and tonnes. Every field is
|
|
12
|
+
* optional; the gateway falls back to Valhalla's defaults (4.11 m high, 2.6 m wide, 21.64 m
|
|
13
|
+
* long, 21.77 t, 9.07 t axle load, 5 axles, no hazmat) for anything omitted.
|
|
14
|
+
*/
|
|
15
|
+
interface TruckProfile {
|
|
16
|
+
/** Vehicle height in metres. */
|
|
17
|
+
height?: number;
|
|
18
|
+
/** Vehicle width in metres. */
|
|
19
|
+
width?: number;
|
|
20
|
+
/** Vehicle length in metres. */
|
|
21
|
+
length?: number;
|
|
22
|
+
/** Gross vehicle weight in tonnes. */
|
|
23
|
+
weight?: number;
|
|
24
|
+
/** Load per axle in tonnes (0–40). */
|
|
25
|
+
axleLoad?: number;
|
|
26
|
+
/** Number of axles (2–20). */
|
|
27
|
+
axleCount?: number;
|
|
28
|
+
/** Carrying hazardous materials: avoids roads tagged `hazmat=no`. */
|
|
29
|
+
hazmat?: boolean;
|
|
30
|
+
/** Preference for designated truck routes, 0 (none) to 1 (strong). */
|
|
31
|
+
useTruckRoute?: number;
|
|
32
|
+
}
|
|
33
|
+
interface RouteOptions {
|
|
34
|
+
mode?: TravelMode;
|
|
35
|
+
/** Only meaningful with `mode: "truck"`; the gateway rejects it on any other mode. */
|
|
36
|
+
truck?: TruckProfile;
|
|
37
|
+
signal?: AbortSignal;
|
|
38
|
+
}
|
|
9
39
|
interface RouteResult {
|
|
10
40
|
distanceMeters: number;
|
|
11
41
|
durationSeconds: number;
|
|
@@ -19,14 +49,16 @@ interface FeatureCollection {
|
|
|
19
49
|
type: "FeatureCollection";
|
|
20
50
|
features: unknown[];
|
|
21
51
|
}
|
|
22
|
-
interface IsochroneOptions {
|
|
52
|
+
interface IsochroneOptions extends RouteOptions {
|
|
23
53
|
minutes: number[];
|
|
24
|
-
mode?: TravelMode;
|
|
25
|
-
signal?: AbortSignal;
|
|
26
54
|
}
|
|
27
55
|
declare class RouterError extends Error {
|
|
28
56
|
readonly status: number;
|
|
29
|
-
|
|
57
|
+
/** The gateway's machine-readable `code`, when the response carried one (e.g. `unauthorized`). */
|
|
58
|
+
readonly code?: string | undefined;
|
|
59
|
+
constructor(status: number, message: string,
|
|
60
|
+
/** The gateway's machine-readable `code`, when the response carried one (e.g. `unauthorized`). */
|
|
61
|
+
code?: string | undefined);
|
|
30
62
|
}
|
|
31
63
|
/** Typed client for the unmap routing API (gateway `/route`, `/isochrone`). */
|
|
32
64
|
declare class Router {
|
|
@@ -34,12 +66,9 @@ declare class Router {
|
|
|
34
66
|
private readonly base;
|
|
35
67
|
private readonly fetchImpl;
|
|
36
68
|
constructor(opts: RouterOptions);
|
|
37
|
-
route(from: LngLat, to: LngLat, opts?:
|
|
38
|
-
mode?: TravelMode;
|
|
39
|
-
signal?: AbortSignal;
|
|
40
|
-
}): Promise<RouteResult>;
|
|
69
|
+
route(from: LngLat, to: LngLat, opts?: RouteOptions): Promise<RouteResult>;
|
|
41
70
|
isochrone(center: LngLat, opts: IsochroneOptions): Promise<FeatureCollection>;
|
|
42
71
|
private get;
|
|
43
72
|
}
|
|
44
73
|
|
|
45
|
-
export { type FeatureCollection, type IsochroneOptions, type LngLat, type RouteResult, Router, RouterError, type RouterOptions, type TravelMode };
|
|
74
|
+
export { type FeatureCollection, type IsochroneOptions, type LngLat, type RouteOptions, type RouteResult, Router, RouterError, type RouterOptions, type TravelMode, type TruckProfile };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,46 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { DEFAULT_GATEWAY } from "@unmap/core";
|
|
3
|
+
var TRUCK_PARAMS = {
|
|
4
|
+
height: "height",
|
|
5
|
+
width: "width",
|
|
6
|
+
length: "length",
|
|
7
|
+
weight: "weight",
|
|
8
|
+
axleLoad: "axle_load",
|
|
9
|
+
axleCount: "axle_count",
|
|
10
|
+
hazmat: "hazmat",
|
|
11
|
+
useTruckRoute: "use_truck_route"
|
|
12
|
+
};
|
|
13
|
+
function truckParams(truck) {
|
|
14
|
+
const out = {};
|
|
15
|
+
if (!truck) return out;
|
|
16
|
+
for (const [field, param] of Object.entries(TRUCK_PARAMS)) {
|
|
17
|
+
const v = truck[field];
|
|
18
|
+
if (v != null) out[param] = String(v);
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
3
22
|
var RouterError = class extends Error {
|
|
4
|
-
constructor(status, message) {
|
|
23
|
+
constructor(status, message, code) {
|
|
5
24
|
super(message);
|
|
6
25
|
this.status = status;
|
|
26
|
+
this.code = code;
|
|
7
27
|
this.name = "RouterError";
|
|
8
28
|
}
|
|
9
29
|
status;
|
|
30
|
+
code;
|
|
10
31
|
};
|
|
32
|
+
async function errorFrom(res, path) {
|
|
33
|
+
const fallback = `route ${res.status} on ${path}`;
|
|
34
|
+
let body;
|
|
35
|
+
try {
|
|
36
|
+
body = await res.json();
|
|
37
|
+
} catch {
|
|
38
|
+
body = void 0;
|
|
39
|
+
}
|
|
40
|
+
const reason = typeof body?.error === "string" ? body.error : void 0;
|
|
41
|
+
const code = typeof body?.code === "string" ? body.code : void 0;
|
|
42
|
+
return new RouterError(res.status, reason ? `${reason} (${fallback})` : fallback, code);
|
|
43
|
+
}
|
|
11
44
|
var Router = class {
|
|
12
45
|
key;
|
|
13
46
|
base;
|
|
@@ -20,14 +53,20 @@ var Router = class {
|
|
|
20
53
|
route(from, to, opts = {}) {
|
|
21
54
|
return this.get(
|
|
22
55
|
"/route",
|
|
23
|
-
{ from: from.join(","), to: to.join(","), mode: opts.mode },
|
|
56
|
+
{ from: from.join(","), to: to.join(","), mode: opts.mode, ...truckParams(opts.truck) },
|
|
24
57
|
opts.signal
|
|
25
58
|
);
|
|
26
59
|
}
|
|
27
60
|
isochrone(center, opts) {
|
|
28
61
|
return this.get(
|
|
29
62
|
"/isochrone",
|
|
30
|
-
{
|
|
63
|
+
{
|
|
64
|
+
lon: String(center[0]),
|
|
65
|
+
lat: String(center[1]),
|
|
66
|
+
minutes: opts.minutes.join(","),
|
|
67
|
+
mode: opts.mode,
|
|
68
|
+
...truckParams(opts.truck)
|
|
69
|
+
},
|
|
31
70
|
opts.signal
|
|
32
71
|
);
|
|
33
72
|
}
|
|
@@ -38,7 +77,7 @@ var Router = class {
|
|
|
38
77
|
signal,
|
|
39
78
|
headers: { accept: "application/json" }
|
|
40
79
|
});
|
|
41
|
-
if (!res.ok) throw
|
|
80
|
+
if (!res.ok) throw await errorFrom(res, path);
|
|
42
81
|
return res.json();
|
|
43
82
|
}
|
|
44
83
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { DEFAULT_GATEWAY } from \"@unmap/core\";\n\nexport interface RouterOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n}\n\nexport type TravelMode = \"auto\" | \"bicycle\" | \"pedestrian\";\n/** [longitude, latitude]. */\nexport type LngLat = [number, number];\n\nexport interface RouteResult {\n distanceMeters: number;\n durationSeconds: number;\n geometry: { type: \"LineString\"; coordinates: LngLat[] };\n}\n\n/** Minimal GeoJSON FeatureCollection (isochrone bands). */\nexport interface FeatureCollection {\n type: \"FeatureCollection\";\n features: unknown[];\n}\n\nexport interface IsochroneOptions {\n minutes: number[];\n
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { DEFAULT_GATEWAY } from \"@unmap/core\";\n\nexport interface RouterOptions {\n key: string;\n gateway?: string;\n fetch?: typeof fetch;\n}\n\n/** `car` is the same costing as `auto`, spelled for people who do not think in Valhalla terms. */\nexport type TravelMode = \"auto\" | \"car\" | \"bicycle\" | \"pedestrian\" | \"truck\";\n/** [longitude, latitude]. */\nexport type LngLat = [number, number];\n\n/**\n * Vehicle profile for `mode: \"truck\"`. Metric throughout: metres and tonnes. Every field is\n * optional; the gateway falls back to Valhalla's defaults (4.11 m high, 2.6 m wide, 21.64 m\n * long, 21.77 t, 9.07 t axle load, 5 axles, no hazmat) for anything omitted.\n */\nexport interface TruckProfile {\n /** Vehicle height in metres. */\n height?: number;\n /** Vehicle width in metres. */\n width?: number;\n /** Vehicle length in metres. */\n length?: number;\n /** Gross vehicle weight in tonnes. */\n weight?: number;\n /** Load per axle in tonnes (0–40). */\n axleLoad?: number;\n /** Number of axles (2–20). */\n axleCount?: number;\n /** Carrying hazardous materials: avoids roads tagged `hazmat=no`. */\n hazmat?: boolean;\n /** Preference for designated truck routes, 0 (none) to 1 (strong). */\n useTruckRoute?: number;\n}\n\nexport interface RouteOptions {\n mode?: TravelMode;\n /** Only meaningful with `mode: \"truck\"`; the gateway rejects it on any other mode. */\n truck?: TruckProfile;\n signal?: AbortSignal;\n}\n\n/** `TruckProfile` field → the gateway's snake_case query parameter. */\nconst TRUCK_PARAMS: Record<keyof TruckProfile, string> = {\n height: \"height\",\n width: \"width\",\n length: \"length\",\n weight: \"weight\",\n axleLoad: \"axle_load\",\n axleCount: \"axle_count\",\n hazmat: \"hazmat\",\n useTruckRoute: \"use_truck_route\",\n};\n\nfunction truckParams(truck?: TruckProfile): Record<string, string | undefined> {\n const out: Record<string, string | undefined> = {};\n if (!truck) return out;\n for (const [field, param] of Object.entries(TRUCK_PARAMS) as [keyof TruckProfile, string][]) {\n const v = truck[field];\n if (v != null) out[param] = String(v);\n }\n return out;\n}\n\nexport interface RouteResult {\n distanceMeters: number;\n durationSeconds: number;\n geometry: { type: \"LineString\"; coordinates: LngLat[] };\n}\n\n/** Minimal GeoJSON FeatureCollection (isochrone bands). */\nexport interface FeatureCollection {\n type: \"FeatureCollection\";\n features: unknown[];\n}\n\nexport interface IsochroneOptions extends RouteOptions {\n minutes: number[];\n}\n\nexport class RouterError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n /** The gateway's machine-readable `code`, when the response carried one (e.g. `unauthorized`). */\n public readonly code?: string,\n ) {\n super(message);\n this.name = \"RouterError\";\n }\n}\n\n/**\n * The gateway answers every error with a JSON body whose `error` says what went wrong\n * (\"mode must be one of auto, car, bicycle, pedestrian, truck\", \"q required\"). Put that in the\n * message so a caller who logs the thrown error reads the reason, not just the status.\n */\nasync function errorFrom(res: { status: number; json(): Promise<unknown> }, path: string): Promise<RouterError> {\n const fallback = `route ${res.status} on ${path}`;\n let body: { error?: unknown; code?: unknown } | undefined;\n try {\n body = (await res.json()) as { error?: unknown; code?: unknown };\n } catch {\n body = undefined;\n }\n const reason = typeof body?.error === \"string\" ? body.error : undefined;\n const code = typeof body?.code === \"string\" ? body.code : undefined;\n return new RouterError(res.status, reason ? `${reason} (${fallback})` : fallback, code);\n}\n\n/** Typed client for the unmap routing API (gateway `/route`, `/isochrone`). */\nexport class Router {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(opts: RouterOptions) {\n this.key = opts.key;\n this.base = (opts.gateway ?? DEFAULT_GATEWAY).replace(/\\/+$/, \"\");\n // Bound to globalThis: stored on an instance and called as `this.fetchImpl(...)`, a bare\n // `fetch` reference is invoked with the Router as its receiver, and browsers reject that\n // with \"Illegal invocation\". Node is lenient, so this only ever surfaces in the browser.\n this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n route(from: LngLat, to: LngLat, opts: RouteOptions = {}): Promise<RouteResult> {\n return this.get<RouteResult>(\n \"/route\",\n { from: from.join(\",\"), to: to.join(\",\"), mode: opts.mode, ...truckParams(opts.truck) },\n opts.signal,\n );\n }\n\n isochrone(center: LngLat, opts: IsochroneOptions): Promise<FeatureCollection> {\n return this.get<FeatureCollection>(\n \"/isochrone\",\n {\n lon: String(center[0]),\n lat: String(center[1]),\n minutes: opts.minutes.join(\",\"),\n mode: opts.mode,\n ...truckParams(opts.truck),\n },\n opts.signal,\n );\n }\n\n private async get<T>(\n path: string,\n params: Record<string, string | undefined>,\n signal?: AbortSignal,\n ): Promise<T> {\n const q = new URLSearchParams({ key: this.key });\n for (const [k, v] of Object.entries(params)) if (v != null && v !== \"\") q.set(k, v);\n const res = await this.fetchImpl(`${this.base}${path}?${q}`, {\n signal,\n headers: { accept: \"application/json\" },\n });\n if (!res.ok) throw await errorFrom(res, path);\n return res.json() as Promise<T>;\n }\n}\n"],"mappings":";AAAA,SAAS,uBAAuB;AA6ChC,IAAM,eAAmD;AAAA,EACvD,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,eAAe;AACjB;AAEA,SAAS,YAAY,OAA0D;AAC7E,QAAM,MAA0C,CAAC;AACjD,MAAI,CAAC,MAAO,QAAO;AACnB,aAAW,CAAC,OAAO,KAAK,KAAK,OAAO,QAAQ,YAAY,GAAqC;AAC3F,UAAM,IAAI,MAAM,KAAK;AACrB,QAAI,KAAK,KAAM,KAAI,KAAK,IAAI,OAAO,CAAC;AAAA,EACtC;AACA,SAAO;AACT;AAkBO,IAAM,cAAN,cAA0B,MAAM;AAAA,EACrC,YACkB,QAChB,SAEgB,MAChB;AACA,UAAM,OAAO;AALG;AAGA;AAGhB,SAAK,OAAO;AAAA,EACd;AAAA,EAPkB;AAAA,EAGA;AAKpB;AAOA,eAAe,UAAU,KAAmD,MAAoC;AAC9G,QAAM,WAAW,SAAS,IAAI,MAAM,OAAO,IAAI;AAC/C,MAAI;AACJ,MAAI;AACF,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,SAAS,OAAO,MAAM,UAAU,WAAW,KAAK,QAAQ;AAC9D,QAAM,OAAO,OAAO,MAAM,SAAS,WAAW,KAAK,OAAO;AAC1D,SAAO,IAAI,YAAY,IAAI,QAAQ,SAAS,GAAG,MAAM,KAAK,QAAQ,MAAM,UAAU,IAAI;AACxF;AAGO,IAAM,SAAN,MAAa;AAAA,EACD;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,MAAqB;AAC/B,SAAK,MAAM,KAAK;AAChB,SAAK,QAAQ,KAAK,WAAW,iBAAiB,QAAQ,QAAQ,EAAE;AAIhE,SAAK,YAAY,KAAK,SAAS,WAAW,MAAM,KAAK,UAAU;AAAA,EACjE;AAAA,EAEA,MAAM,MAAc,IAAY,OAAqB,CAAC,GAAyB;AAC7E,WAAO,KAAK;AAAA,MACV;AAAA,MACA,EAAE,MAAM,KAAK,KAAK,GAAG,GAAG,IAAI,GAAG,KAAK,GAAG,GAAG,MAAM,KAAK,MAAM,GAAG,YAAY,KAAK,KAAK,EAAE;AAAA,MACtF,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEA,UAAU,QAAgB,MAAoD;AAC5E,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,QACE,KAAK,OAAO,OAAO,CAAC,CAAC;AAAA,QACrB,KAAK,OAAO,OAAO,CAAC,CAAC;AAAA,QACrB,SAAS,KAAK,QAAQ,KAAK,GAAG;AAAA,QAC9B,MAAM,KAAK;AAAA,QACX,GAAG,YAAY,KAAK,KAAK;AAAA,MAC3B;AAAA,MACA,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEA,MAAc,IACZ,MACA,QACA,QACY;AACZ,UAAM,IAAI,IAAI,gBAAgB,EAAE,KAAK,KAAK,IAAI,CAAC;AAC/C,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,EAAG,KAAI,KAAK,QAAQ,MAAM,GAAI,GAAE,IAAI,GAAG,CAAC;AAClF,UAAM,MAAM,MAAM,KAAK,UAAU,GAAG,KAAK,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI;AAAA,MAC3D;AAAA,MACA,SAAS,EAAE,QAAQ,mBAAmB;AAAA,IACxC,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,OAAM,MAAM,UAAU,KAAK,IAAI;AAC5C,WAAO,IAAI,KAAK;AAAA,EAClB;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unmap/routing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Typed client for the unmap routing API (route + isochrone)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@unmap/core": "0.
|
|
41
|
+
"@unmap/core": "0.5.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"tsup": "^8.3.0",
|