@fieldia/map 0.17.0 → 0.19.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 +75 -10
- package/package.json +4 -4
- package/src/index.d.mts +5 -0
- package/src/index.d.ts +5 -0
- package/src/index.js +4 -1
- package/src/index.js.map +1 -1
- package/src/index.mjs +2 -0
- package/src/index.mjs.map +1 -1
- package/src/lib/address-labels.d.mts +21 -0
- package/src/lib/address-labels.d.ts +21 -0
- package/src/lib/address-labels.js +16 -0
- package/src/lib/address-labels.js.map +1 -0
- package/src/lib/address-labels.mjs +12 -0
- package/src/lib/address-labels.mjs.map +1 -0
- package/src/lib/address-line.d.mts +2 -0
- package/src/lib/address-line.d.ts +2 -0
- package/src/lib/address-line.js +34 -0
- package/src/lib/address-line.js.map +1 -0
- package/src/lib/address-line.mjs +31 -0
- package/src/lib/address-line.mjs.map +1 -0
- package/src/lib/address-parts.d.mts +28 -0
- package/src/lib/address-parts.d.ts +28 -0
- package/src/lib/address-parts.js +47 -0
- package/src/lib/address-parts.js.map +1 -0
- package/src/lib/address-parts.mjs +38 -0
- package/src/lib/address-parts.mjs.map +1 -0
- package/src/lib/address-search.d.mts +21 -0
- package/src/lib/address-search.d.ts +21 -0
- package/src/lib/address-search.js +52 -0
- package/src/lib/address-search.js.map +1 -0
- package/src/lib/address-search.mjs +49 -0
- package/src/lib/address-search.mjs.map +1 -0
- package/src/lib/address-suggest.d.mts +18 -0
- package/src/lib/address-suggest.d.ts +18 -0
- package/src/lib/address-suggest.js +140 -0
- package/src/lib/address-suggest.js.map +1 -0
- package/src/lib/address-suggest.mjs +137 -0
- package/src/lib/address-suggest.mjs.map +1 -0
- package/src/lib/area-count.d.mts +47 -0
- package/src/lib/area-count.d.ts +47 -0
- package/src/lib/area-count.js +123 -0
- package/src/lib/area-count.js.map +1 -0
- package/src/lib/area-count.mjs +116 -0
- package/src/lib/area-count.mjs.map +1 -0
- package/src/lib/card.d.mts +16 -6
- package/src/lib/card.d.ts +16 -6
- package/src/lib/card.js +71 -14
- package/src/lib/card.js.map +1 -1
- package/src/lib/card.mjs +70 -14
- package/src/lib/card.mjs.map +1 -1
- package/src/lib/cluster.d.mts +6 -1
- package/src/lib/cluster.d.ts +6 -1
- package/src/lib/cluster.js +11 -5
- package/src/lib/cluster.js.map +1 -1
- package/src/lib/cluster.mjs +11 -5
- package/src/lib/cluster.mjs.map +1 -1
- package/src/lib/embed.d.mts +86 -0
- package/src/lib/embed.d.ts +86 -0
- package/src/lib/embed.js +96 -0
- package/src/lib/embed.js.map +1 -0
- package/src/lib/embed.mjs +93 -0
- package/src/lib/embed.mjs.map +1 -0
- package/src/lib/form-map.d.mts +2 -1
- package/src/lib/form-map.d.ts +2 -1
- package/src/lib/form-map.js +159 -20
- package/src/lib/form-map.js.map +1 -1
- package/src/lib/form-map.mjs +161 -22
- package/src/lib/form-map.mjs.map +1 -1
- package/src/lib/kinds.d.mts +6 -4
- package/src/lib/kinds.d.ts +6 -4
- package/src/lib/kinds.js +49 -2
- package/src/lib/kinds.js.map +1 -1
- package/src/lib/kinds.mjs +49 -2
- package/src/lib/kinds.mjs.map +1 -1
- package/src/lib/labels.d.mts +5 -0
- package/src/lib/labels.d.ts +5 -0
- package/src/lib/labels.js +12 -4
- package/src/lib/labels.js.map +1 -1
- package/src/lib/labels.mjs +12 -4
- package/src/lib/labels.mjs.map +1 -1
- package/src/lib/leaflet.js +1 -0
- package/src/lib/leaflet.js.map +1 -1
- package/src/lib/leaflet.mjs +1 -0
- package/src/lib/leaflet.mjs.map +1 -1
- package/src/lib/list-view.d.mts +37 -1
- package/src/lib/list-view.d.ts +37 -1
- package/src/lib/list-view.js +103 -50
- package/src/lib/list-view.js.map +1 -1
- package/src/lib/list-view.mjs +103 -51
- package/src/lib/list-view.mjs.map +1 -1
- package/src/lib/map-core.d.mts +8 -3
- package/src/lib/map-core.d.ts +8 -3
- package/src/lib/map-core.js +21 -10
- package/src/lib/map-core.js.map +1 -1
- package/src/lib/map-core.mjs +21 -10
- package/src/lib/map-core.mjs.map +1 -1
- package/src/lib/marks.d.mts +11 -0
- package/src/lib/marks.d.ts +11 -0
- package/src/lib/marks.js +26 -1
- package/src/lib/marks.js.map +1 -1
- package/src/lib/marks.mjs +23 -0
- package/src/lib/marks.mjs.map +1 -1
- package/src/lib/pins.d.mts +20 -8
- package/src/lib/pins.d.ts +20 -8
- package/src/lib/pins.js +60 -15
- package/src/lib/pins.js.map +1 -1
- package/src/lib/pins.mjs +60 -17
- package/src/lib/pins.mjs.map +1 -1
- package/src/lib/place.js +2 -1
- package/src/lib/place.js.map +1 -1
- package/src/lib/place.mjs +2 -1
- package/src/lib/place.mjs.map +1 -1
- package/src/lib/providers.d.mts +31 -5
- package/src/lib/providers.d.ts +31 -5
- package/src/lib/providers.js +80 -58
- package/src/lib/providers.js.map +1 -1
- package/src/lib/providers.mjs +80 -58
- package/src/lib/providers.mjs.map +1 -1
- package/src/lib/regions.js +18 -1
- package/src/lib/regions.js.map +1 -1
- package/src/lib/regions.mjs +18 -1
- package/src/lib/regions.mjs.map +1 -1
- package/src/lib/search-box.d.mts +7 -1
- package/src/lib/search-box.d.ts +7 -1
- package/src/lib/search-box.js +15 -10
- package/src/lib/search-box.js.map +1 -1
- package/src/lib/search-box.mjs +15 -10
- package/src/lib/search-box.mjs.map +1 -1
- package/src/lib/settings.d.mts +19 -2
- package/src/lib/settings.d.ts +19 -2
- package/src/lib/styles.d.mts +13 -2
- package/src/lib/styles.d.ts +13 -2
- package/src/lib/styles.js +69 -4
- package/src/lib/styles.js.map +1 -1
- package/src/lib/styles.mjs +67 -3
- package/src/lib/styles.mjs.map +1 -1
- package/src/lib/tools.d.mts +6 -3
- package/src/lib/tools.d.ts +6 -3
- package/src/lib/tools.js +52 -1
- package/src/lib/tools.js.map +1 -1
- package/src/lib/tools.mjs +52 -1
- package/src/lib/tools.mjs.map +1 -1
- package/src/lib/view-box.d.mts +18 -0
- package/src/lib/view-box.d.ts +18 -0
- package/src/lib/view-box.js +31 -0
- package/src/lib/view-box.js.map +1 -0
- package/src/lib/view-box.mjs +25 -0
- package/src/lib/view-box.mjs.map +1 -0
- package/src/lib/widgets.d.mts +3 -0
- package/src/lib/widgets.d.ts +3 -0
- package/src/lib/widgets.js +10 -1
- package/src/lib/widgets.js.map +1 -1
- package/src/lib/widgets.mjs +10 -1
- package/src/lib/widgets.mjs.map +1 -1
package/src/lib/providers.d.mts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Locale } from '@fieldia/core';
|
|
2
2
|
import { type Fetch } from './answers.mjs';
|
|
3
|
+
import type { AddressParts } from './address-parts.mjs';
|
|
3
4
|
import type { Bounds, LatLng } from './geo.mjs';
|
|
4
5
|
/**
|
|
5
|
-
* Where a map's images, credits, address search and routes come from. Every map in
|
|
6
|
+
* Where a map's images, live traffic, credits, address search and routes come from. Every map in
|
|
6
7
|
* the package draws through one of these, so an app changes provider in one
|
|
7
8
|
* place and nothing else moves. Keys never live in Fieldia: the app passes
|
|
8
9
|
* them, or a function that fetches a short-lived token from its own server.
|
|
@@ -13,6 +14,10 @@ export interface TileSpec {
|
|
|
13
14
|
/** With `{z}`, `{x}` and `{y}` where the tile's numbers go. */
|
|
14
15
|
url: string;
|
|
15
16
|
maxZoom: number;
|
|
17
|
+
/** The zoom they start at, when not the world's. */
|
|
18
|
+
minZoom?: number;
|
|
19
|
+
/** Seconds after which they are out of date, as live traffic's are: drawn again then, each address with `&t=` and the time. */
|
|
20
|
+
fresh?: number;
|
|
16
21
|
tileSize?: number;
|
|
17
22
|
zoomOffset?: number;
|
|
18
23
|
/** Headers each image is fetched with: a token that must not sit in an address. */
|
|
@@ -38,7 +43,10 @@ export interface PlaceFound {
|
|
|
38
43
|
lat: number;
|
|
39
44
|
lng: number;
|
|
40
45
|
address?: string;
|
|
46
|
+
parts?: AddressParts;
|
|
41
47
|
}>;
|
|
48
|
+
/** Its address in its parts, when the search asked for them (`parts`); for Google's, `locate` gives them. */
|
|
49
|
+
parts?: AddressParts;
|
|
42
50
|
}
|
|
43
51
|
export interface SearchRequest {
|
|
44
52
|
text: string;
|
|
@@ -48,6 +56,10 @@ export interface SearchRequest {
|
|
|
48
56
|
zoom?: number;
|
|
49
57
|
locale: Locale;
|
|
50
58
|
signal?: AbortSignal;
|
|
59
|
+
/** Only places in these countries, by their ISO codes ("EG"). */
|
|
60
|
+
countries?: string[];
|
|
61
|
+
/** Each place's address in its parts too, for an address field to fill. */
|
|
62
|
+
parts?: boolean;
|
|
51
63
|
}
|
|
52
64
|
export type AddressSearch = (request: SearchRequest) => Promise<PlaceFound[]>;
|
|
53
65
|
export type ReverseSearch = (at: LatLng & {
|
|
@@ -85,8 +97,10 @@ export interface MapProvider {
|
|
|
85
97
|
readonly id: string;
|
|
86
98
|
/** The map images of a kind, or null when the provider has none of it. */
|
|
87
99
|
tiles(kind: BaseMap, locale: Locale): Promise<TileSpec | null>;
|
|
88
|
-
/** What must be credited under the map, for what is in view. */
|
|
89
|
-
credits(kind: BaseMap, view: MapView, locale: Locale): Promise<Credit[]>;
|
|
100
|
+
/** What must be credited under the map, for what is in view: its images', and its traffic's while that is drawn. */
|
|
101
|
+
credits(kind: BaseMap | 'traffic', view: MapView, locale: Locale): Promise<Credit[]>;
|
|
102
|
+
/** Live traffic, drawn over its map images of a kind; a provider with none has no Traffic button. */
|
|
103
|
+
traffic?(kind: BaseMap, locale: Locale): Promise<TileSpec | null>;
|
|
90
104
|
/** The provider's mark, which its terms ask for in the map's corner. */
|
|
91
105
|
readonly logo?: {
|
|
92
106
|
text: string;
|
|
@@ -99,6 +113,8 @@ export interface MapProvider {
|
|
|
99
113
|
readonly searchAsYouType?: boolean;
|
|
100
114
|
/** Free for trying things out, not for an app in use: a warning says so once. */
|
|
101
115
|
readonly demoOnly?: boolean;
|
|
116
|
+
/** What its terms ask to be said under its suggestions where no map of its own shows them; its mark (`logo`) says it for Google and Mapbox. */
|
|
117
|
+
readonly searchCredit?: Credit;
|
|
102
118
|
/** A route along the roads, for a map's routes. */
|
|
103
119
|
readonly route?: RouteFinder;
|
|
104
120
|
/** The area within some minutes of a place, for a map's reachable area. */
|
|
@@ -135,7 +151,7 @@ export interface AzureMapsOptions {
|
|
|
135
151
|
clientId?: string;
|
|
136
152
|
fetch?: Fetch;
|
|
137
153
|
}
|
|
138
|
-
/** Microsoft Azure Maps: road, dark and satellite map images, credits for the view, and address search. */
|
|
154
|
+
/** Microsoft Azure Maps: road, dark and satellite map images, live traffic, credits for the view, and address search. */
|
|
139
155
|
export declare function azureMaps(options: AzureMapsOptions): MapProvider;
|
|
140
156
|
export interface GoogleMapsOptions {
|
|
141
157
|
key: string;
|
|
@@ -148,7 +164,8 @@ export interface GoogleMapsOptions {
|
|
|
148
164
|
/**
|
|
149
165
|
* Google's map images through its Map Tiles API (a session for each kind and
|
|
150
166
|
* language, credits for the view, Google's mark in the corner its terms
|
|
151
|
-
* name
|
|
167
|
+
* name; its traffic, a layer of its own over them), its Places API's
|
|
168
|
+
* suggestions as people type, and its Geocoding API
|
|
152
169
|
* to name a point. The key needs those three APIs enabled, and should be
|
|
153
170
|
* restricted to the app's sites.
|
|
154
171
|
*/
|
|
@@ -159,6 +176,13 @@ export interface MapboxOptions {
|
|
|
159
176
|
style?: string;
|
|
160
177
|
darkStyle?: string;
|
|
161
178
|
satelliteStyle?: string;
|
|
179
|
+
/**
|
|
180
|
+
* A style of the app's own that draws Mapbox Traffic's lines and nothing
|
|
181
|
+
* else (no background), for the Traffic button: Mapbox's traffic is a
|
|
182
|
+
* vector tileset its styles draw, and it has no such style of its own.
|
|
183
|
+
* Without one, no Traffic button.
|
|
184
|
+
*/
|
|
185
|
+
trafficStyle?: string;
|
|
162
186
|
/** Mapbox's wordmark, as an image the app serves; its name is shown until one is given. */
|
|
163
187
|
logo?: string;
|
|
164
188
|
/**
|
|
@@ -176,6 +200,8 @@ export interface TilesOptions {
|
|
|
176
200
|
url: string;
|
|
177
201
|
darkUrl?: string;
|
|
178
202
|
satelliteUrl?: string;
|
|
203
|
+
/** The app's own live traffic, drawn over the map: with it, a Traffic button. */
|
|
204
|
+
trafficUrl?: string;
|
|
179
205
|
credit: Credit | Credit[];
|
|
180
206
|
maxZoom?: number;
|
|
181
207
|
tileSize?: number;
|
package/src/lib/providers.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Locale } from '@fieldia/core';
|
|
2
2
|
import { type Fetch } from './answers';
|
|
3
|
+
import type { AddressParts } from './address-parts';
|
|
3
4
|
import type { Bounds, LatLng } from './geo';
|
|
4
5
|
/**
|
|
5
|
-
* Where a map's images, credits, address search and routes come from. Every map in
|
|
6
|
+
* Where a map's images, live traffic, credits, address search and routes come from. Every map in
|
|
6
7
|
* the package draws through one of these, so an app changes provider in one
|
|
7
8
|
* place and nothing else moves. Keys never live in Fieldia: the app passes
|
|
8
9
|
* them, or a function that fetches a short-lived token from its own server.
|
|
@@ -13,6 +14,10 @@ export interface TileSpec {
|
|
|
13
14
|
/** With `{z}`, `{x}` and `{y}` where the tile's numbers go. */
|
|
14
15
|
url: string;
|
|
15
16
|
maxZoom: number;
|
|
17
|
+
/** The zoom they start at, when not the world's. */
|
|
18
|
+
minZoom?: number;
|
|
19
|
+
/** Seconds after which they are out of date, as live traffic's are: drawn again then, each address with `&t=` and the time. */
|
|
20
|
+
fresh?: number;
|
|
16
21
|
tileSize?: number;
|
|
17
22
|
zoomOffset?: number;
|
|
18
23
|
/** Headers each image is fetched with: a token that must not sit in an address. */
|
|
@@ -38,7 +43,10 @@ export interface PlaceFound {
|
|
|
38
43
|
lat: number;
|
|
39
44
|
lng: number;
|
|
40
45
|
address?: string;
|
|
46
|
+
parts?: AddressParts;
|
|
41
47
|
}>;
|
|
48
|
+
/** Its address in its parts, when the search asked for them (`parts`); for Google's, `locate` gives them. */
|
|
49
|
+
parts?: AddressParts;
|
|
42
50
|
}
|
|
43
51
|
export interface SearchRequest {
|
|
44
52
|
text: string;
|
|
@@ -48,6 +56,10 @@ export interface SearchRequest {
|
|
|
48
56
|
zoom?: number;
|
|
49
57
|
locale: Locale;
|
|
50
58
|
signal?: AbortSignal;
|
|
59
|
+
/** Only places in these countries, by their ISO codes ("EG"). */
|
|
60
|
+
countries?: string[];
|
|
61
|
+
/** Each place's address in its parts too, for an address field to fill. */
|
|
62
|
+
parts?: boolean;
|
|
51
63
|
}
|
|
52
64
|
export type AddressSearch = (request: SearchRequest) => Promise<PlaceFound[]>;
|
|
53
65
|
export type ReverseSearch = (at: LatLng & {
|
|
@@ -85,8 +97,10 @@ export interface MapProvider {
|
|
|
85
97
|
readonly id: string;
|
|
86
98
|
/** The map images of a kind, or null when the provider has none of it. */
|
|
87
99
|
tiles(kind: BaseMap, locale: Locale): Promise<TileSpec | null>;
|
|
88
|
-
/** What must be credited under the map, for what is in view. */
|
|
89
|
-
credits(kind: BaseMap, view: MapView, locale: Locale): Promise<Credit[]>;
|
|
100
|
+
/** What must be credited under the map, for what is in view: its images', and its traffic's while that is drawn. */
|
|
101
|
+
credits(kind: BaseMap | 'traffic', view: MapView, locale: Locale): Promise<Credit[]>;
|
|
102
|
+
/** Live traffic, drawn over its map images of a kind; a provider with none has no Traffic button. */
|
|
103
|
+
traffic?(kind: BaseMap, locale: Locale): Promise<TileSpec | null>;
|
|
90
104
|
/** The provider's mark, which its terms ask for in the map's corner. */
|
|
91
105
|
readonly logo?: {
|
|
92
106
|
text: string;
|
|
@@ -99,6 +113,8 @@ export interface MapProvider {
|
|
|
99
113
|
readonly searchAsYouType?: boolean;
|
|
100
114
|
/** Free for trying things out, not for an app in use: a warning says so once. */
|
|
101
115
|
readonly demoOnly?: boolean;
|
|
116
|
+
/** What its terms ask to be said under its suggestions where no map of its own shows them; its mark (`logo`) says it for Google and Mapbox. */
|
|
117
|
+
readonly searchCredit?: Credit;
|
|
102
118
|
/** A route along the roads, for a map's routes. */
|
|
103
119
|
readonly route?: RouteFinder;
|
|
104
120
|
/** The area within some minutes of a place, for a map's reachable area. */
|
|
@@ -135,7 +151,7 @@ export interface AzureMapsOptions {
|
|
|
135
151
|
clientId?: string;
|
|
136
152
|
fetch?: Fetch;
|
|
137
153
|
}
|
|
138
|
-
/** Microsoft Azure Maps: road, dark and satellite map images, credits for the view, and address search. */
|
|
154
|
+
/** Microsoft Azure Maps: road, dark and satellite map images, live traffic, credits for the view, and address search. */
|
|
139
155
|
export declare function azureMaps(options: AzureMapsOptions): MapProvider;
|
|
140
156
|
export interface GoogleMapsOptions {
|
|
141
157
|
key: string;
|
|
@@ -148,7 +164,8 @@ export interface GoogleMapsOptions {
|
|
|
148
164
|
/**
|
|
149
165
|
* Google's map images through its Map Tiles API (a session for each kind and
|
|
150
166
|
* language, credits for the view, Google's mark in the corner its terms
|
|
151
|
-
* name
|
|
167
|
+
* name; its traffic, a layer of its own over them), its Places API's
|
|
168
|
+
* suggestions as people type, and its Geocoding API
|
|
152
169
|
* to name a point. The key needs those three APIs enabled, and should be
|
|
153
170
|
* restricted to the app's sites.
|
|
154
171
|
*/
|
|
@@ -159,6 +176,13 @@ export interface MapboxOptions {
|
|
|
159
176
|
style?: string;
|
|
160
177
|
darkStyle?: string;
|
|
161
178
|
satelliteStyle?: string;
|
|
179
|
+
/**
|
|
180
|
+
* A style of the app's own that draws Mapbox Traffic's lines and nothing
|
|
181
|
+
* else (no background), for the Traffic button: Mapbox's traffic is a
|
|
182
|
+
* vector tileset its styles draw, and it has no such style of its own.
|
|
183
|
+
* Without one, no Traffic button.
|
|
184
|
+
*/
|
|
185
|
+
trafficStyle?: string;
|
|
162
186
|
/** Mapbox's wordmark, as an image the app serves; its name is shown until one is given. */
|
|
163
187
|
logo?: string;
|
|
164
188
|
/**
|
|
@@ -176,6 +200,8 @@ export interface TilesOptions {
|
|
|
176
200
|
url: string;
|
|
177
201
|
darkUrl?: string;
|
|
178
202
|
satelliteUrl?: string;
|
|
203
|
+
/** The app's own live traffic, drawn over the map: with it, a Traffic button. */
|
|
204
|
+
trafficUrl?: string;
|
|
179
205
|
credit: Credit | Credit[];
|
|
180
206
|
maxZoom?: number;
|
|
181
207
|
tileSize?: number;
|
package/src/lib/providers.js
CHANGED
|
@@ -6,8 +6,11 @@ exports.googleMaps = googleMaps;
|
|
|
6
6
|
exports.mapbox = mapbox;
|
|
7
7
|
exports.tiles = tiles;
|
|
8
8
|
const answers_1 = require("./answers");
|
|
9
|
+
const address_line_1 = require("./address-line");
|
|
9
10
|
/** The providers' routes, loaded the first time one is asked for: a form with only a place or an area never carries them. */
|
|
10
11
|
const routes = () => Promise.resolve().then(() => require('./routes'));
|
|
12
|
+
/** How each provider's addresses read in their parts, loaded the first time a search asks for parts or countries (an address field's): a place's search never carries it. */
|
|
13
|
+
const partsReader = (parts, countries) => (parts || countries?.length ? Promise.resolve().then(() => require('./address-parts')) : undefined);
|
|
11
14
|
const query = (params) => Object.entries(params)
|
|
12
15
|
.filter(([, value]) => value !== undefined)
|
|
13
16
|
.map(([key, value]) => `${key}=${encodeURIComponent(String(value))}`)
|
|
@@ -20,16 +23,8 @@ const textOfHtml = (html) => html
|
|
|
20
23
|
.replace(/<[^>]*>/g, '')
|
|
21
24
|
.replace(/&(#x[0-9a-f]+|#[0-9]+|[a-z]+);/gi, (all, name) => name[0] === '#' ? String.fromCodePoint(parseInt(name.slice(name[1] === 'x' || name[1] === 'X' ? 2 : 1), name[1] === 'x' || name[1] === 'X' ? 16 : 10)) : (ENTITIES[name.toLowerCase()] ?? all))
|
|
22
25
|
.trim();
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
const seen = [];
|
|
26
|
-
for (const part of parts)
|
|
27
|
-
if (typeof part === 'string' && part.trim() && !seen.includes(part.trim()))
|
|
28
|
-
seen.push(part.trim());
|
|
29
|
-
return seen.join(', ');
|
|
30
|
-
};
|
|
31
|
-
/** Places from a GeoJSON FeatureCollection: each named by the first of `labels` it has, with the line under it from `detail`. */
|
|
32
|
-
function placesFrom(collection, labels, detail) {
|
|
26
|
+
/** Places from a GeoJSON FeatureCollection: each named by the first of `labels` it has, with the line under it from `detail`, and its parts from `parts`. */
|
|
27
|
+
function placesFrom(collection, labels, detail, parts) {
|
|
33
28
|
const features = (0, answers_1.pathOf)(collection, 'features');
|
|
34
29
|
if (!Array.isArray(features))
|
|
35
30
|
return [];
|
|
@@ -39,13 +34,14 @@ function placesFrom(collection, labels, detail) {
|
|
|
39
34
|
if (typeof lat !== 'number' || typeof lng !== 'number' || typeof label !== 'string')
|
|
40
35
|
return [];
|
|
41
36
|
const under = detail?.((0, answers_1.pathOf)(feature, 'properties'));
|
|
42
|
-
|
|
37
|
+
const found = parts?.((0, answers_1.pathOf)(feature, 'properties'));
|
|
38
|
+
return [{ label, ...(under && under !== label ? { detail: under } : {}), lat, lng, ...(found ? { parts: found } : {}) }];
|
|
43
39
|
});
|
|
44
40
|
}
|
|
45
41
|
/** Languages Photon names places in; in any other it gives the places' own names. */
|
|
46
42
|
const PHOTON_LANGUAGES = new Set(['en', 'de', 'fr']);
|
|
47
|
-
/** Photon's places: each one's name (or its street and number) on one line, where it is on the next. */
|
|
48
|
-
function photonPlaces(found) {
|
|
43
|
+
/** Photon's places: each one's name (or its street and number) on one line, where it is on the next; their parts when asked. */
|
|
44
|
+
function photonPlaces(found, locale, parts) {
|
|
49
45
|
const features = (0, answers_1.pathOf)(found, 'features');
|
|
50
46
|
if (!Array.isArray(features))
|
|
51
47
|
return [];
|
|
@@ -57,8 +53,9 @@ function photonPlaces(found) {
|
|
|
57
53
|
if (typeof lat !== 'number' || typeof lng !== 'number' || !label)
|
|
58
54
|
return [];
|
|
59
55
|
// Under a named place, its street when it has one; then its district, city and country.
|
|
60
|
-
const detail =
|
|
61
|
-
|
|
56
|
+
const detail = (0, address_line_1.addressLine)([label === streetOf(p) ? '' : streetOf(p), (0, answers_1.pathOf)(p, 'district'), (0, answers_1.pathOf)(p, 'city'), (0, answers_1.pathOf)(p, 'state'), (0, answers_1.pathOf)(p, 'country')], locale);
|
|
57
|
+
const own = parts?.(p);
|
|
58
|
+
return [{ label, ...(detail ? { detail } : {}), lat, lng, ...(own ? { parts: own } : {}) }];
|
|
62
59
|
});
|
|
63
60
|
}
|
|
64
61
|
/**
|
|
@@ -73,6 +70,7 @@ function openStreetMap(options = {}) {
|
|
|
73
70
|
const base = {
|
|
74
71
|
id: 'openstreetmap',
|
|
75
72
|
demoOnly: true,
|
|
73
|
+
searchCredit: { text: '© OpenStreetMap contributors', href: 'https://www.openstreetmap.org/copyright' },
|
|
76
74
|
async route(request) {
|
|
77
75
|
return (await routes()).osrmRoute(fetchWith, osrm, request);
|
|
78
76
|
},
|
|
@@ -87,8 +85,9 @@ function openStreetMap(options = {}) {
|
|
|
87
85
|
return {
|
|
88
86
|
...base,
|
|
89
87
|
searchAsYouType: false,
|
|
90
|
-
async search({ text, locale, signal }) {
|
|
91
|
-
const found = await (0, answers_1.getJson)(fetchWith, `https://nominatim.openstreetmap.org/search?${query({ format: 'jsonv2', limit: 5, q: text, 'accept-language': locale })}`, { signal });
|
|
88
|
+
async search({ text, locale, signal, countries, parts }) {
|
|
89
|
+
const found = await (0, answers_1.getJson)(fetchWith, `https://nominatim.openstreetmap.org/search?${query({ format: 'jsonv2', limit: 5, q: text, 'accept-language': locale, countrycodes: countries?.join(',').toLowerCase() || undefined, addressdetails: parts ? 1 : undefined })}`, { signal });
|
|
90
|
+
const read = await partsReader(parts);
|
|
92
91
|
return Array.isArray(found)
|
|
93
92
|
? found.flatMap((place) => {
|
|
94
93
|
const lat = Number((0, answers_1.pathOf)(place, 'lat'));
|
|
@@ -99,14 +98,15 @@ function openStreetMap(options = {}) {
|
|
|
99
98
|
const name = (0, answers_1.pathOf)(place, 'name');
|
|
100
99
|
const label = typeof name === 'string' && name.trim() ? name : whole.split(',')[0];
|
|
101
100
|
const rest = whole.split(',').map((part) => part.trim()).filter((part, i) => !(i === 0 && part === label));
|
|
102
|
-
|
|
101
|
+
const own = read?.nominatimParts((0, answers_1.pathOf)(place, 'address'));
|
|
102
|
+
const detail = (0, address_line_1.addressLine)(rest, locale);
|
|
103
|
+
return [{ label, ...(detail ? { detail } : {}), lat, lng, ...(own ? { parts: own } : {}) }];
|
|
103
104
|
})
|
|
104
105
|
: [];
|
|
105
106
|
},
|
|
106
107
|
async reverse({ lat, lng, locale, signal }) {
|
|
107
108
|
const found = await (0, answers_1.getJson)(fetchWith, `https://nominatim.openstreetmap.org/reverse?${query({ format: 'jsonv2', lat, lon: lng, 'accept-language': locale })}`, { signal });
|
|
108
|
-
|
|
109
|
-
return typeof label === 'string' ? label : null;
|
|
109
|
+
return (0, address_line_1.addressLine)([(0, answers_1.pathOf)(found, 'display_name')], locale) || null;
|
|
110
110
|
},
|
|
111
111
|
};
|
|
112
112
|
}
|
|
@@ -114,14 +114,16 @@ function openStreetMap(options = {}) {
|
|
|
114
114
|
return {
|
|
115
115
|
...base,
|
|
116
116
|
searchAsYouType: true,
|
|
117
|
-
async search({ text, near, zoom, locale, signal }) {
|
|
117
|
+
async search({ text, near, zoom, locale, signal, countries, parts }) {
|
|
118
118
|
const found = await (0, answers_1.getJson)(fetchWith, `${photon}/api?${query({ q: text, limit: 6, lang: lang(locale), lat: near?.lat, lon: near?.lng, zoom: near && zoom !== undefined ? Math.round(zoom) : undefined })}`, { signal });
|
|
119
|
-
|
|
119
|
+
// Photon cannot be asked to keep to some countries: those outside them are left out here.
|
|
120
|
+
const read = await partsReader(parts, countries);
|
|
121
|
+
return read ? read.inCountries(photonPlaces(found, locale, read.photonParts), countries) : photonPlaces(found, locale);
|
|
120
122
|
},
|
|
121
123
|
async reverse({ lat, lng, locale, signal }) {
|
|
122
124
|
const found = await (0, answers_1.getJson)(fetchWith, `${photon}/reverse?${query({ lat, lon: lng, limit: 1, lang: lang(locale) })}`, { signal });
|
|
123
|
-
const [place] = photonPlaces(found);
|
|
124
|
-
return place ?
|
|
125
|
+
const [place] = photonPlaces(found, locale);
|
|
126
|
+
return place ? (0, address_line_1.addressLine)([place.label, place.detail], locale) : null;
|
|
125
127
|
},
|
|
126
128
|
};
|
|
127
129
|
}
|
|
@@ -133,8 +135,8 @@ function streetOf(properties) {
|
|
|
133
135
|
}
|
|
134
136
|
/** Azure lists the languages it draws; it refuses any other tag, `ar-001` among them. */
|
|
135
137
|
const AZURE_LANGUAGE = { en: 'en-US', ar: 'ar', fr: 'fr-FR', de: 'de-DE' };
|
|
136
|
-
const AZURE_TILESET = { road: 'microsoft.base.road', dark: 'microsoft.base.darkgrey', satellite: 'microsoft.imagery' };
|
|
137
|
-
/** Microsoft Azure Maps: road, dark and satellite map images, credits for the view, and address search. */
|
|
138
|
+
const AZURE_TILESET = { road: 'microsoft.base.road', dark: 'microsoft.base.darkgrey', satellite: 'microsoft.imagery', traffic: 'microsoft.traffic.relative.main' };
|
|
139
|
+
/** Microsoft Azure Maps: road, dark and satellite map images, live traffic, credits for the view, and address search. */
|
|
138
140
|
function azureMaps(options) {
|
|
139
141
|
if (!options.key && !options.token)
|
|
140
142
|
throw new Error('azureMaps needs a key or a token');
|
|
@@ -145,12 +147,16 @@ function azureMaps(options) {
|
|
|
145
147
|
signal,
|
|
146
148
|
headers: { ...(await headers()), ...(locale ? { 'Accept-Language': AZURE_LANGUAGE[locale] } : {}) },
|
|
147
149
|
});
|
|
150
|
+
const tile = (tileset, locale) => `https://atlas.microsoft.com/map/tile?api-version=2024-04-01&tilesetId=${tileset}&zoom={z}&x={x}&y={y}&tileSize=256&language=${AZURE_LANGUAGE[locale]}&view=Auto${auth}`;
|
|
148
151
|
return {
|
|
149
152
|
id: 'azure-maps',
|
|
150
153
|
searchAsYouType: true,
|
|
151
154
|
async tiles(kind, locale) {
|
|
152
|
-
|
|
153
|
-
|
|
155
|
+
return { url: tile(AZURE_TILESET[kind], locale), maxZoom: kind === 'satellite' ? 19 : 22, ...(options.key ? {} : { headers }) };
|
|
156
|
+
},
|
|
157
|
+
// How each road flows against its free flow, in its own colours over the dark map; zooms 6 to 21 (it answers none outside them), out of date in four minutes (its cache-control's max-age).
|
|
158
|
+
async traffic(kind, locale) {
|
|
159
|
+
return { url: tile(kind === 'dark' ? 'microsoft.traffic.relative.dark' : AZURE_TILESET.traffic, locale), minZoom: 6, maxZoom: 21, fresh: 240, ...(options.key ? {} : { headers }) };
|
|
154
160
|
},
|
|
155
161
|
async credits(kind, { zoom, bounds }) {
|
|
156
162
|
try {
|
|
@@ -164,15 +170,17 @@ function azureMaps(options) {
|
|
|
164
170
|
}
|
|
165
171
|
return [{ text: '© Microsoft' }];
|
|
166
172
|
},
|
|
167
|
-
async search({ text, near, locale, signal }) {
|
|
173
|
+
async search({ text, near, locale, signal, countries, parts }) {
|
|
168
174
|
const coordinates = near ? `${near.lng},${near.lat}` : undefined;
|
|
169
175
|
// Its autocomplete reads a word half typed, as geocoding does not; its suggestions carry no place, so a picked one is geocoded then.
|
|
170
176
|
const found = await ask('geocode:autocomplete', { 'api-version': '2026-01-01', top: 5, query: text, coordinates }, locale, signal);
|
|
171
|
-
|
|
177
|
+
// Its parts come with each suggestion; those outside the countries asked for are left out here.
|
|
178
|
+
const read = await partsReader(parts, countries);
|
|
179
|
+
return (read ? read.inCountries(azurePlaces(found, locale, read.azureParts), countries) : azurePlaces(found, locale)).map((place) => ({
|
|
172
180
|
...place,
|
|
173
181
|
locate: async () => {
|
|
174
|
-
const address = [place.label, place.detail]
|
|
175
|
-
const [hit] = azurePlaces(await ask('geocode', { 'api-version': '2023-06-01', top: 1, query: place.whole ?? address, coordinates }, locale));
|
|
182
|
+
const address = (0, address_line_1.addressLine)([place.label, place.detail], locale);
|
|
183
|
+
const [hit] = azurePlaces(await ask('geocode', { 'api-version': '2023-06-01', top: 1, query: place.whole ?? address, coordinates }, locale), locale);
|
|
176
184
|
if (hit?.lat === undefined || hit.lng === undefined)
|
|
177
185
|
throw new Error('Azure Maps found no place');
|
|
178
186
|
// The address as its two lines read: with its town, which Azure's own may leave out.
|
|
@@ -184,7 +192,7 @@ function azureMaps(options) {
|
|
|
184
192
|
const found = await ask('reverseGeocode', { 'api-version': '2023-06-01', coordinates: `${lng},${lat}` }, locale, signal);
|
|
185
193
|
const features = (0, answers_1.pathOf)(found, 'features');
|
|
186
194
|
const formatted = Array.isArray(features) ? clean((0, answers_1.pathOf)(features[0], 'properties', 'address', 'formattedAddress')) : '';
|
|
187
|
-
return formatted || null;
|
|
195
|
+
return (0, address_line_1.addressLine)([formatted], locale) || null;
|
|
188
196
|
},
|
|
189
197
|
async route(request) {
|
|
190
198
|
return (await routes()).azureRoute({ fetchWith, auth, headers }, request);
|
|
@@ -200,7 +208,7 @@ function azureMaps(options) {
|
|
|
200
208
|
* says (geocoding does, autocomplete does not); and the whole address as Azure
|
|
201
209
|
* wrote it, to geocode a suggestion by.
|
|
202
210
|
*/
|
|
203
|
-
function azurePlaces(found) {
|
|
211
|
+
function azurePlaces(found, locale, partsOf) {
|
|
204
212
|
const features = (0, answers_1.pathOf)(found, 'features');
|
|
205
213
|
if (!Array.isArray(features))
|
|
206
214
|
return [];
|
|
@@ -220,14 +228,17 @@ function azurePlaces(found) {
|
|
|
220
228
|
if (town && !rest.includes(town))
|
|
221
229
|
rest = [town, ...rest];
|
|
222
230
|
const at = typeof lat === 'number' && typeof lng === 'number' ? { lat, lng } : {};
|
|
223
|
-
|
|
231
|
+
const own = partsOf?.(address);
|
|
232
|
+
const detail = (0, address_line_1.addressLine)(rest, locale);
|
|
233
|
+
return [{ label, ...(detail ? { detail } : {}), ...at, ...(whole ? { whole } : {}), ...(own ? { parts: own } : {}) }];
|
|
224
234
|
});
|
|
225
235
|
}
|
|
226
|
-
const GOOGLE_MAP_TYPE = { road: 'roadmap', satellite: 'satellite' };
|
|
236
|
+
const GOOGLE_MAP_TYPE = { road: 'roadmap', satellite: 'satellite', traffic: 'roadmap' };
|
|
227
237
|
/**
|
|
228
238
|
* Google's map images through its Map Tiles API (a session for each kind and
|
|
229
239
|
* language, credits for the view, Google's mark in the corner its terms
|
|
230
|
-
* name
|
|
240
|
+
* name; its traffic, a layer of its own over them), its Places API's
|
|
241
|
+
* suggestions as people type, and its Geocoding API
|
|
231
242
|
* to name a point. The key needs those three APIs enabled, and should be
|
|
232
243
|
* restricted to the app's sites.
|
|
233
244
|
*/
|
|
@@ -237,14 +248,15 @@ function googleMaps(options) {
|
|
|
237
248
|
const sessions = new Map();
|
|
238
249
|
function session(kind, locale) {
|
|
239
250
|
const mapType = GOOGLE_MAP_TYPE[kind];
|
|
240
|
-
const id = `${
|
|
251
|
+
const id = `${kind}:${locale}`;
|
|
241
252
|
const known = sessions.get(id);
|
|
242
253
|
return known ?? open();
|
|
243
254
|
function open() {
|
|
244
255
|
const opening = (0, answers_1.getJson)(fetchWith, `https://tile.googleapis.com/v1/createSession?key=${key}`, {
|
|
245
256
|
method: 'POST',
|
|
246
257
|
headers: { 'Content-Type': 'application/json' },
|
|
247
|
-
|
|
258
|
+
// Traffic alone, without the map under it: an overlay.
|
|
259
|
+
body: JSON.stringify({ mapType, language: locale, region: options.region ?? 'US', ...(kind === 'traffic' ? { layerTypes: ['layerTraffic'], overlay: true } : {}) }),
|
|
248
260
|
}).then((answer) => {
|
|
249
261
|
const value = (0, answers_1.pathOf)(answer, 'session');
|
|
250
262
|
if (typeof value !== 'string')
|
|
@@ -266,13 +278,19 @@ function googleMaps(options) {
|
|
|
266
278
|
/** One session from the first letter typed to the place picked: Google charges the suggestions of a session that ends in a pick as the pick. */
|
|
267
279
|
let searching;
|
|
268
280
|
const newSession = () => (globalThis.crypto?.randomUUID?.() ?? 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => ((Math.random() * 16) | (c === 'x' ? 0 : 8)).toString(16).slice(0, 1)));
|
|
281
|
+
async function tilesOf(kind, locale) {
|
|
282
|
+
if (!GOOGLE_MAP_TYPE[kind])
|
|
283
|
+
return null;
|
|
284
|
+
const { session: id } = await session(kind, locale);
|
|
285
|
+
return { url: `https://tile.googleapis.com/v1/2dtiles/{z}/{x}/{y}?session=${id}&key=${key}`, maxZoom: 22 };
|
|
286
|
+
}
|
|
269
287
|
/** How far "near" reaches at a zoom: about what the map shows, within Google's bounds. */
|
|
270
288
|
const radiusAt = (zoom) => Math.round(Math.min(50_000, Math.max(100, 40_075_016 / 2 ** (zoom ?? 12))));
|
|
271
289
|
return {
|
|
272
290
|
id: 'google',
|
|
273
291
|
logo: { text: 'Google Maps', ...(options.logo ? { image: options.logo } : {}) },
|
|
274
292
|
searchAsYouType: true,
|
|
275
|
-
async search({ text, near, zoom, locale, signal }) {
|
|
293
|
+
async search({ text, near, zoom, locale, signal, countries, parts }) {
|
|
276
294
|
searching ??= newSession();
|
|
277
295
|
const token = searching;
|
|
278
296
|
const answer = await (0, answers_1.getJson)(fetchWith, 'https://places.googleapis.com/v1/places:autocomplete', {
|
|
@@ -283,6 +301,7 @@ function googleMaps(options) {
|
|
|
283
301
|
input: text,
|
|
284
302
|
languageCode: locale,
|
|
285
303
|
...(options.region ? { regionCode: options.region } : {}),
|
|
304
|
+
...(countries?.length ? { includedRegionCodes: countries.map((code) => code.toLowerCase()) } : {}),
|
|
286
305
|
sessionToken: token,
|
|
287
306
|
...(near ? { locationBias: { circle: { center: { latitude: near.lat, longitude: near.lng }, radius: radiusAt(zoom) } } } : {}),
|
|
288
307
|
}),
|
|
@@ -296,14 +315,15 @@ function googleMaps(options) {
|
|
|
296
315
|
const label = clean((0, answers_1.pathOf)(prediction, 'structuredFormat', 'mainText', 'text')) || clean((0, answers_1.pathOf)(prediction, 'text', 'text'));
|
|
297
316
|
if (typeof placeId !== 'string' || !label)
|
|
298
317
|
return [];
|
|
299
|
-
const detail =
|
|
318
|
+
const detail = (0, address_line_1.addressLine)([(0, answers_1.pathOf)(prediction, 'structuredFormat', 'secondaryText', 'text')], locale);
|
|
300
319
|
return [
|
|
301
320
|
{
|
|
302
321
|
label,
|
|
303
322
|
...(detail ? { detail } : {}),
|
|
304
323
|
locate: async () => {
|
|
305
324
|
const place = await (0, answers_1.getJson)(fetchWith, `https://places.googleapis.com/v1/places/${encodeURIComponent(placeId)}?${query({ languageCode: locale, sessionToken: token })}`, {
|
|
306
|
-
|
|
325
|
+
// Its parts, when asked for, at the same price as the rest (Place Details Essentials).
|
|
326
|
+
headers: { 'X-Goog-Api-Key': options.key, 'X-Goog-FieldMask': `location,formattedAddress${parts ? ',addressComponents' : ''}` },
|
|
307
327
|
});
|
|
308
328
|
// Picked: the session is over, the next letter typed opens another.
|
|
309
329
|
if (searching === token)
|
|
@@ -312,8 +332,9 @@ function googleMaps(options) {
|
|
|
312
332
|
const longitude = (0, answers_1.pathOf)(place, 'location', 'longitude');
|
|
313
333
|
if (typeof latitude !== 'number' || typeof longitude !== 'number')
|
|
314
334
|
throw new Error('Google gave no place');
|
|
315
|
-
const address =
|
|
316
|
-
|
|
335
|
+
const address = (0, address_line_1.addressLine)([(0, answers_1.pathOf)(place, 'formattedAddress')], locale);
|
|
336
|
+
const own = (await partsReader(parts))?.googleParts((0, answers_1.pathOf)(place, 'addressComponents'));
|
|
337
|
+
return { lat: latitude, lng: longitude, ...(address ? { address } : {}), ...(own ? { parts: own } : {}) };
|
|
317
338
|
},
|
|
318
339
|
},
|
|
319
340
|
];
|
|
@@ -326,18 +347,14 @@ function googleMaps(options) {
|
|
|
326
347
|
if (typeof status === 'string' && status !== 'OK' && status !== 'ZERO_RESULTS')
|
|
327
348
|
throw new Error(`Google's Geocoding API answered ${status}: ${String((0, answers_1.pathOf)(answer, 'error_message') ?? '')}`);
|
|
328
349
|
const results = (0, answers_1.pathOf)(answer, 'results');
|
|
329
|
-
const named = Array.isArray(results) ?
|
|
350
|
+
const named = Array.isArray(results) ? (0, address_line_1.addressLine)([(0, answers_1.pathOf)(results[0], 'formatted_address')], locale) : '';
|
|
330
351
|
return named || null;
|
|
331
352
|
},
|
|
332
353
|
async route(request) {
|
|
333
354
|
return (await routes()).googleRoute({ fetchWith, key: options.key }, request);
|
|
334
355
|
},
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
return null;
|
|
338
|
-
const { session: id } = await session(kind, locale);
|
|
339
|
-
return { url: `https://tile.googleapis.com/v1/2dtiles/{z}/{x}/{y}?session=${id}&key=${key}`, maxZoom: 22 };
|
|
340
|
-
},
|
|
356
|
+
tiles: (kind, locale) => tilesOf(kind, locale),
|
|
357
|
+
traffic: (_kind, locale) => tilesOf('traffic', locale),
|
|
341
358
|
async credits(kind, { zoom, bounds }, locale) {
|
|
342
359
|
try {
|
|
343
360
|
const { session: id } = await session(kind === 'dark' ? 'road' : kind, locale);
|
|
@@ -362,6 +379,8 @@ function mapbox(options) {
|
|
|
362
379
|
dark: options.darkStyle ?? 'mapbox/dark-v11',
|
|
363
380
|
satellite: options.satelliteStyle ?? 'mapbox/satellite-streets-v12',
|
|
364
381
|
};
|
|
382
|
+
const styleTiles = (style) => ({ url: `https://api.mapbox.com/styles/v1/${style}/tiles/512/{z}/{x}/{y}@2x?access_token=${token}`, maxZoom: 22, tileSize: 512, zoomOffset: -1 });
|
|
383
|
+
const traffic = options.trafficStyle;
|
|
365
384
|
const permanent = options.permanent ?? true;
|
|
366
385
|
const ask = async (path, params, signal) => (0, answers_1.getJson)(fetchWith, `https://api.mapbox.com/search/geocode/v6/${path}?${query({ ...params, permanent: String(permanent) })}&access_token=${token}`, { signal });
|
|
367
386
|
return {
|
|
@@ -369,8 +388,9 @@ function mapbox(options) {
|
|
|
369
388
|
searchAsYouType: true,
|
|
370
389
|
logo: { text: 'mapbox', ...(options.logo ? { image: options.logo } : {}), href: 'https://www.mapbox.com/' },
|
|
371
390
|
async tiles(kind) {
|
|
372
|
-
return
|
|
391
|
+
return styleTiles(styles[kind]);
|
|
373
392
|
},
|
|
393
|
+
...(traffic ? { traffic: async () => styleTiles(traffic) } : {}),
|
|
374
394
|
async credits() {
|
|
375
395
|
return [
|
|
376
396
|
{ text: '© Mapbox', href: 'https://www.mapbox.com/about/maps/' },
|
|
@@ -378,11 +398,12 @@ function mapbox(options) {
|
|
|
378
398
|
{ text: 'Improve this map', href: 'https://www.mapbox.com/map-feedback/' },
|
|
379
399
|
];
|
|
380
400
|
},
|
|
381
|
-
async search({ text, near, locale, signal }) {
|
|
382
|
-
return placesFrom(await ask('forward', { q: text, limit: 5, language: locale, proximity: near ? `${near.lng},${near.lat}` : undefined }, signal), [['properties', 'name'], ['properties', 'full_address']], (p) =>
|
|
401
|
+
async search({ text, near, locale, signal, countries, parts }) {
|
|
402
|
+
return placesFrom(await ask('forward', { q: text, limit: 5, language: locale, proximity: near ? `${near.lng},${near.lat}` : undefined, country: countries?.join(',').toLowerCase() || undefined }, signal), [['properties', 'name'], ['properties', 'full_address']], (p) => (0, address_line_1.addressLine)([(0, answers_1.pathOf)(p, 'place_formatted')], locale), (await partsReader(parts))?.mapboxParts);
|
|
383
403
|
},
|
|
384
404
|
async reverse({ lat, lng, locale, signal }) {
|
|
385
|
-
|
|
405
|
+
const [named] = placesFrom(await ask('reverse', { longitude: lng, latitude: lat, language: locale, limit: 1 }, signal), [['properties', 'full_address'], ['properties', 'name']]);
|
|
406
|
+
return named ? (0, address_line_1.addressLine)([named.label], locale) : null;
|
|
386
407
|
},
|
|
387
408
|
async route(request) {
|
|
388
409
|
return (await routes()).mapboxRoute({ fetchWith, token }, request);
|
|
@@ -396,6 +417,8 @@ function mapbox(options) {
|
|
|
396
417
|
function tiles(options) {
|
|
397
418
|
const urls = { road: options.url, dark: options.darkUrl, satellite: options.satelliteUrl };
|
|
398
419
|
const credits = Array.isArray(options.credit) ? options.credit : [options.credit];
|
|
420
|
+
const spec = (url) => ({ url, maxZoom: options.maxZoom ?? 19, ...(options.tileSize ? { tileSize: options.tileSize } : {}), ...(options.zoomOffset ? { zoomOffset: options.zoomOffset } : {}) });
|
|
421
|
+
const traffic = options.trafficUrl;
|
|
399
422
|
return {
|
|
400
423
|
id: 'tiles',
|
|
401
424
|
search: options.search,
|
|
@@ -405,10 +428,9 @@ function tiles(options) {
|
|
|
405
428
|
searchAsYouType: options.searchAsYouType ?? !!options.search,
|
|
406
429
|
async tiles(kind) {
|
|
407
430
|
const url = urls[kind];
|
|
408
|
-
|
|
409
|
-
return null;
|
|
410
|
-
return { url, maxZoom: options.maxZoom ?? 19, ...(options.tileSize ? { tileSize: options.tileSize } : {}), ...(options.zoomOffset ? { zoomOffset: options.zoomOffset } : {}) };
|
|
431
|
+
return url ? spec(url) : null;
|
|
411
432
|
},
|
|
433
|
+
...(traffic ? { traffic: async () => spec(traffic) } : {}),
|
|
412
434
|
async credits() {
|
|
413
435
|
return credits;
|
|
414
436
|
},
|