@unmap/geocoding 0.0.1 → 0.1.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 +34 -0
- package/dist/index.d.ts +23 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @unmap/geocoding
|
|
2
|
+
|
|
3
|
+
Typed client for the [unmap](https://unmap.dev) geocoding API: full-text search, type-ahead
|
|
4
|
+
autocomplete, category search, and reverse lookup over Canadian addresses and places, with
|
|
5
|
+
English, French, and Indigenous-language names on the same record. No map dependency; works in
|
|
6
|
+
Node and the browser.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm i @unmap/geocoding
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { Geocoder } from '@unmap/geocoding'
|
|
14
|
+
|
|
15
|
+
const geocoder = new Geocoder({ key: 'um_live_...' })
|
|
16
|
+
|
|
17
|
+
await geocoder.search('Château Frontenac', { lang: 'fr', limit: 5 })
|
|
18
|
+
await geocoder.autocomplete('rue sainte-cath', { lang: 'fr' })
|
|
19
|
+
await geocoder.nearby('pharmacy', { near: [-114.07, 51.05], radius: 2000 })
|
|
20
|
+
await geocoder.reverse(-68.517, 63.749)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Every method resolves to a `GeocodeResult[]`: `id`, `name`, `layer`, `lng`, `lat`, plus `score`
|
|
24
|
+
(search and autocomplete), `distance` in metres and `category` (nearby), `names` when the place
|
|
25
|
+
has names in more than one language, and a structured `address` when one exists.
|
|
26
|
+
|
|
27
|
+
`search` and `autocomplete` take `{ limit, lang, bbox, signal }`; `nearby` takes exactly one of
|
|
28
|
+
`near: [lng, lat]` or `bbox`, plus `{ radius, limit, lang, signal }`; `reverse` takes
|
|
29
|
+
`{ lang, signal }`. `category` accepts taxonomy ids (`health.pharmacy`) or plain English or
|
|
30
|
+
French words (`pharmacy`, `pharmacie`). A non-2xx response throws a `GeocoderError` carrying the
|
|
31
|
+
HTTP `status`.
|
|
32
|
+
|
|
33
|
+
Parameters, defaults, the category list, and error bodies are documented at
|
|
34
|
+
[unmap.dev/docs/geocoding-api](https://unmap.dev/docs/geocoding-api).
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,10 @@ interface GeocodeResult {
|
|
|
12
12
|
lng: number;
|
|
13
13
|
lat: number;
|
|
14
14
|
score?: number;
|
|
15
|
+
/** Canonical category id (e.g. "health.pharmacy") when the feature carries one. */
|
|
16
|
+
category?: string;
|
|
17
|
+
/** Spheroidal metres from the query anchor; only `/geocode/nearby` results carry it. */
|
|
18
|
+
distance?: number;
|
|
15
19
|
names?: Record<string, string>;
|
|
16
20
|
address?: {
|
|
17
21
|
housenumber?: string;
|
|
@@ -29,6 +33,17 @@ interface SearchOptions {
|
|
|
29
33
|
bbox?: [number, number, number, number];
|
|
30
34
|
signal?: AbortSignal;
|
|
31
35
|
}
|
|
36
|
+
interface NearbyOptions {
|
|
37
|
+
/** [lng, lat] anchor to sort by distance from. Exactly one of `near` or `bbox`. */
|
|
38
|
+
near?: [number, number];
|
|
39
|
+
/** [minLng, minLat, maxLng, maxLat]; the API anchors at its centre. */
|
|
40
|
+
bbox?: [number, number, number, number];
|
|
41
|
+
/** Search radius in metres (API default 5000, clamped to [50, 50000]). */
|
|
42
|
+
radius?: number;
|
|
43
|
+
limit?: number;
|
|
44
|
+
lang?: string;
|
|
45
|
+
signal?: AbortSignal;
|
|
46
|
+
}
|
|
32
47
|
declare class GeocoderError extends Error {
|
|
33
48
|
readonly status: number;
|
|
34
49
|
constructor(status: number, message: string);
|
|
@@ -43,6 +58,13 @@ declare class Geocoder {
|
|
|
43
58
|
search(q: string, opts?: SearchOptions): Promise<GeocodeResult[]>;
|
|
44
59
|
/** Type-ahead suggestions for partial input. */
|
|
45
60
|
autocomplete(q: string, opts?: SearchOptions): Promise<GeocodeResult[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Category search: POIs of a kind near a point, sorted by distance (metres, in each
|
|
63
|
+
* result's `distance`). `category` takes canonical taxonomy ids ("health.pharmacy") or
|
|
64
|
+
* plain words in English or French ("pharmacy", "pharmacie") — the gateway resolves
|
|
65
|
+
* aliases, so this client passes the value through untouched.
|
|
66
|
+
*/
|
|
67
|
+
nearby(category: string | string[], opts?: NearbyOptions): Promise<GeocodeResult[]>;
|
|
46
68
|
/** Reverse geocode a coordinate. */
|
|
47
69
|
reverse(lng: number, lat: number, opts?: {
|
|
48
70
|
lang?: string;
|
|
@@ -51,4 +73,4 @@ declare class Geocoder {
|
|
|
51
73
|
private query;
|
|
52
74
|
}
|
|
53
75
|
|
|
54
|
-
export { type GeocodeLayer, type GeocodeResult, Geocoder, GeocoderError, type GeocoderOptions, type SearchOptions };
|
|
76
|
+
export { type GeocodeLayer, type GeocodeResult, Geocoder, GeocoderError, type GeocoderOptions, type NearbyOptions, type SearchOptions };
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,26 @@ var Geocoder = class {
|
|
|
25
25
|
autocomplete(q, opts = {}) {
|
|
26
26
|
return this.query("/geocode/autocomplete", { q, ...toParams(opts) }, opts.signal);
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Category search: POIs of a kind near a point, sorted by distance (metres, in each
|
|
30
|
+
* result's `distance`). `category` takes canonical taxonomy ids ("health.pharmacy") or
|
|
31
|
+
* plain words in English or French ("pharmacy", "pharmacie") — the gateway resolves
|
|
32
|
+
* aliases, so this client passes the value through untouched.
|
|
33
|
+
*/
|
|
34
|
+
nearby(category, opts = {}) {
|
|
35
|
+
return this.query(
|
|
36
|
+
"/geocode/nearby",
|
|
37
|
+
{
|
|
38
|
+
category: Array.isArray(category) ? category.join(",") : category,
|
|
39
|
+
near: opts.near ? opts.near.join(",") : void 0,
|
|
40
|
+
bbox: opts.bbox ? opts.bbox.join(",") : void 0,
|
|
41
|
+
radius: opts.radius != null ? String(opts.radius) : void 0,
|
|
42
|
+
limit: opts.limit != null ? String(opts.limit) : void 0,
|
|
43
|
+
lang: opts.lang
|
|
44
|
+
},
|
|
45
|
+
opts.signal
|
|
46
|
+
);
|
|
47
|
+
}
|
|
28
48
|
/** Reverse geocode a coordinate. */
|
|
29
49
|
reverse(lng, lat, opts = {}) {
|
|
30
50
|
return this.query(
|
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 GeocoderOptions {\n key: string;\n gateway?: string;\n /** Override the fetch implementation (tests, non-global environments). */\n fetch?: typeof fetch;\n}\n\nexport type GeocodeLayer = \"address\" | \"street\" | \"locality\" | \"region\" | \"poi\";\n\nexport interface GeocodeResult {\n id: string;\n name: string;\n layer: GeocodeLayer;\n lng: number;\n lat: number;\n score?: number;\n names?: Record<string, string>;\n address?: {\n housenumber?: string;\n street?: string;\n city?: string;\n region?: string;\n postcode?: string;\n country?: string;\n };\n}\n\nexport interface SearchOptions {\n limit?: number;\n lang?: string;\n /** [minLng, minLat, maxLng, maxLat] bias/filter. */\n bbox?: [number, number, number, number];\n signal?: AbortSignal;\n}\n\nexport class GeocoderError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = \"GeocoderError\";\n }\n}\n\n/** Typed client for the unmap geocoding API (gateway `/geocode/*`). */\nexport class Geocoder {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(opts: GeocoderOptions) {\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 Geocoder 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 /** Full-text search. */\n search(q: string, opts: SearchOptions = {}): Promise<GeocodeResult[]> {\n return this.query(\"/geocode/search\", { q, ...toParams(opts) }, opts.signal);\n }\n\n /** Type-ahead suggestions for partial input. */\n autocomplete(q: string, opts: SearchOptions = {}): Promise<GeocodeResult[]> {\n return this.query(\"/geocode/autocomplete\", { q, ...toParams(opts) }, opts.signal);\n }\n\n /** Reverse geocode a coordinate. */\n reverse(\n lng: number,\n lat: number,\n opts: { lang?: string; signal?: AbortSignal } = {},\n ): Promise<GeocodeResult[]> {\n return this.query(\n \"/geocode/reverse\",\n { lon: String(lng), lat: String(lat), lang: opts.lang },\n opts.signal,\n );\n }\n\n private async query(\n path: string,\n params: Record<string, string | undefined>,\n signal?: AbortSignal,\n ): Promise<GeocodeResult[]> {\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 new GeocoderError(res.status, `geocode ${res.status} on ${path}`);\n const body = (await res.json()) as { results?: GeocodeResult[] } | GeocodeResult[];\n return Array.isArray(body) ? body : (body.results ?? []);\n }\n}\n\nfunction toParams(o: SearchOptions): Record<string, string | undefined> {\n return {\n limit: o.limit != null ? String(o.limit) : undefined,\n lang: o.lang,\n bbox: o.bbox ? o.bbox.join(\",\") : undefined,\n };\n}\n"],"mappings":";AAAA,SAAS,uBAAuB;
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { DEFAULT_GATEWAY } from \"@unmap/core\";\n\nexport interface GeocoderOptions {\n key: string;\n gateway?: string;\n /** Override the fetch implementation (tests, non-global environments). */\n fetch?: typeof fetch;\n}\n\nexport type GeocodeLayer = \"address\" | \"street\" | \"locality\" | \"region\" | \"poi\";\n\nexport interface GeocodeResult {\n id: string;\n name: string;\n layer: GeocodeLayer;\n lng: number;\n lat: number;\n score?: number;\n /** Canonical category id (e.g. \"health.pharmacy\") when the feature carries one. */\n category?: string;\n /** Spheroidal metres from the query anchor; only `/geocode/nearby` results carry it. */\n distance?: number;\n names?: Record<string, string>;\n address?: {\n housenumber?: string;\n street?: string;\n city?: string;\n region?: string;\n postcode?: string;\n country?: string;\n };\n}\n\nexport interface SearchOptions {\n limit?: number;\n lang?: string;\n /** [minLng, minLat, maxLng, maxLat] bias/filter. */\n bbox?: [number, number, number, number];\n signal?: AbortSignal;\n}\n\nexport interface NearbyOptions {\n /** [lng, lat] anchor to sort by distance from. Exactly one of `near` or `bbox`. */\n near?: [number, number];\n /** [minLng, minLat, maxLng, maxLat]; the API anchors at its centre. */\n bbox?: [number, number, number, number];\n /** Search radius in metres (API default 5000, clamped to [50, 50000]). */\n radius?: number;\n limit?: number;\n lang?: string;\n signal?: AbortSignal;\n}\n\nexport class GeocoderError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = \"GeocoderError\";\n }\n}\n\n/** Typed client for the unmap geocoding API (gateway `/geocode/*`). */\nexport class Geocoder {\n private readonly key: string;\n private readonly base: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(opts: GeocoderOptions) {\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 Geocoder 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 /** Full-text search. */\n search(q: string, opts: SearchOptions = {}): Promise<GeocodeResult[]> {\n return this.query(\"/geocode/search\", { q, ...toParams(opts) }, opts.signal);\n }\n\n /** Type-ahead suggestions for partial input. */\n autocomplete(q: string, opts: SearchOptions = {}): Promise<GeocodeResult[]> {\n return this.query(\"/geocode/autocomplete\", { q, ...toParams(opts) }, opts.signal);\n }\n\n /**\n * Category search: POIs of a kind near a point, sorted by distance (metres, in each\n * result's `distance`). `category` takes canonical taxonomy ids (\"health.pharmacy\") or\n * plain words in English or French (\"pharmacy\", \"pharmacie\") — the gateway resolves\n * aliases, so this client passes the value through untouched.\n */\n nearby(category: string | string[], opts: NearbyOptions = {}): Promise<GeocodeResult[]> {\n return this.query(\n \"/geocode/nearby\",\n {\n category: Array.isArray(category) ? category.join(\",\") : category,\n near: opts.near ? opts.near.join(\",\") : undefined,\n bbox: opts.bbox ? opts.bbox.join(\",\") : undefined,\n radius: opts.radius != null ? String(opts.radius) : undefined,\n limit: opts.limit != null ? String(opts.limit) : undefined,\n lang: opts.lang,\n },\n opts.signal,\n );\n }\n\n /** Reverse geocode a coordinate. */\n reverse(\n lng: number,\n lat: number,\n opts: { lang?: string; signal?: AbortSignal } = {},\n ): Promise<GeocodeResult[]> {\n return this.query(\n \"/geocode/reverse\",\n { lon: String(lng), lat: String(lat), lang: opts.lang },\n opts.signal,\n );\n }\n\n private async query(\n path: string,\n params: Record<string, string | undefined>,\n signal?: AbortSignal,\n ): Promise<GeocodeResult[]> {\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 new GeocoderError(res.status, `geocode ${res.status} on ${path}`);\n const body = (await res.json()) as { results?: GeocodeResult[] } | GeocodeResult[];\n return Array.isArray(body) ? body : (body.results ?? []);\n }\n}\n\nfunction toParams(o: SearchOptions): Record<string, string | undefined> {\n return {\n limit: o.limit != null ? String(o.limit) : undefined,\n lang: o.lang,\n bbox: o.bbox ? o.bbox.join(\",\") : undefined,\n };\n}\n"],"mappings":";AAAA,SAAS,uBAAuB;AAqDzB,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YACkB,QAChB,SACA;AACA,UAAM,OAAO;AAHG;AAIhB,SAAK,OAAO;AAAA,EACd;AAAA,EALkB;AAMpB;AAGO,IAAM,WAAN,MAAe;AAAA,EACH;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,MAAuB;AACjC,SAAK,MAAM,KAAK;AAChB,SAAK,QAAQ,KAAK,WAAW,iBAAiB,QAAQ,QAAQ,EAAE;AAIhE,SAAK,YAAY,KAAK,SAAS,WAAW,MAAM,KAAK,UAAU;AAAA,EACjE;AAAA;AAAA,EAGA,OAAO,GAAW,OAAsB,CAAC,GAA6B;AACpE,WAAO,KAAK,MAAM,mBAAmB,EAAE,GAAG,GAAG,SAAS,IAAI,EAAE,GAAG,KAAK,MAAM;AAAA,EAC5E;AAAA;AAAA,EAGA,aAAa,GAAW,OAAsB,CAAC,GAA6B;AAC1E,WAAO,KAAK,MAAM,yBAAyB,EAAE,GAAG,GAAG,SAAS,IAAI,EAAE,GAAG,KAAK,MAAM;AAAA,EAClF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,UAA6B,OAAsB,CAAC,GAA6B;AACtF,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,QACE,UAAU,MAAM,QAAQ,QAAQ,IAAI,SAAS,KAAK,GAAG,IAAI;AAAA,QACzD,MAAM,KAAK,OAAO,KAAK,KAAK,KAAK,GAAG,IAAI;AAAA,QACxC,MAAM,KAAK,OAAO,KAAK,KAAK,KAAK,GAAG,IAAI;AAAA,QACxC,QAAQ,KAAK,UAAU,OAAO,OAAO,KAAK,MAAM,IAAI;AAAA,QACpD,OAAO,KAAK,SAAS,OAAO,OAAO,KAAK,KAAK,IAAI;AAAA,QACjD,MAAM,KAAK;AAAA,MACb;AAAA,MACA,KAAK;AAAA,IACP;AAAA,EACF;AAAA;AAAA,EAGA,QACE,KACA,KACA,OAAgD,CAAC,GACvB;AAC1B,WAAO,KAAK;AAAA,MACV;AAAA,MACA,EAAE,KAAK,OAAO,GAAG,GAAG,KAAK,OAAO,GAAG,GAAG,MAAM,KAAK,KAAK;AAAA,MACtD,KAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEA,MAAc,MACZ,MACA,QACA,QAC0B;AAC1B,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,IAAI,cAAc,IAAI,QAAQ,WAAW,IAAI,MAAM,OAAO,IAAI,EAAE;AACnF,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO,MAAM,QAAQ,IAAI,IAAI,OAAQ,KAAK,WAAW,CAAC;AAAA,EACxD;AACF;AAEA,SAAS,SAAS,GAAsD;AACtE,SAAO;AAAA,IACL,OAAO,EAAE,SAAS,OAAO,OAAO,EAAE,KAAK,IAAI;AAAA,IAC3C,MAAM,EAAE;AAAA,IACR,MAAM,EAAE,OAAO,EAAE,KAAK,KAAK,GAAG,IAAI;AAAA,EACpC;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unmap/geocoding",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Typed client for the unmap geocoding API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@unmap/core": "0.0
|
|
23
|
+
"@unmap/core": "0.1.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"tsup": "^8.3.0",
|