osmfeatures 0.1.0 → 0.2.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 +228 -17
- package/dist/index.d.ts +85 -4
- package/dist/index.js +248 -26
- package/dist/preset/catalog.js +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# OSM Features API client
|
|
2
2
|
|
|
3
|
-
Official
|
|
3
|
+
Official client for the [MapLark OSM Features API](https://maplark.com) to get GeoJSON, FlatGeobuf, GeoParquet, or CSV from OpenStreetMap. The API gets data from dedicated postgis OSM servers separate from public Overpass.
|
|
4
4
|
|
|
5
|
-
Query OpenStreetMap features such as buildings, streets, and POIs easily. Search for OSM features by bounding box, tags, and geometry shape and get GeoJSON back within less than 250ms (dependent on query size). No converting between formats manually. The API keeps
|
|
5
|
+
Query OpenStreetMap features such as buildings, streets, and POIs easily. Search for OSM features by bounding box, tags, and geometry shape and get GeoJSON back within less than 250ms (dependent on query size). No converting between formats manually. The API keeps OSM semantics intact, like tags and ways, and returns OSM features you can feed straight into Leaflet, MapLibre, OpenLayers, or any geospatial toolchain. It is backed by postgis with tiered API keys and rate limiting to keep noisy neighbours out to give you low, predictable latency for real traffic. It also has self-host path for those willing to host complex infrastructure themselves.
|
|
6
6
|
|
|
7
7
|
The translation layer is very simple:
|
|
8
8
|
|
|
@@ -24,16 +24,10 @@ For example, to get all buildings in an area:
|
|
|
24
24
|
|
|
25
25
|
This is the equivalent of the Overpass query `way[building]`.
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# How to use it
|
|
27
|
+
# Quick start
|
|
30
28
|
|
|
31
29
|
```bash
|
|
32
|
-
# From npm (when published):
|
|
33
30
|
npm install osmfeatures
|
|
34
|
-
|
|
35
|
-
# Until then, from git:
|
|
36
|
-
npm install git+https://github.com/MapLark/osmfeatures-ts.git
|
|
37
31
|
```
|
|
38
32
|
|
|
39
33
|
```ts
|
|
@@ -45,23 +39,240 @@ const page = await client.query({
|
|
|
45
39
|
tags: ['building'],
|
|
46
40
|
});
|
|
47
41
|
|
|
48
|
-
//
|
|
49
|
-
map.getSource('buildings').setData(page.data);
|
|
42
|
+
// Get GeoJSON FeatureCollection
|
|
50
43
|
console.log(page.data.features.length);
|
|
51
44
|
|
|
52
|
-
// Header
|
|
45
|
+
// Header meta for paging + usage
|
|
53
46
|
console.log(page.meta.has_more, page.meta.next_cursor, page.meta.units_charged);
|
|
54
47
|
|
|
55
|
-
// Binary / table encodings via Accept
|
|
48
|
+
// Binary / table encodings via Accept param
|
|
56
49
|
const fgb = await client.query({
|
|
57
50
|
bbox: '18.06,59.32,18.09,59.34',
|
|
58
51
|
tags: ['building'],
|
|
59
52
|
accept: 'application/flatgeobuf',
|
|
60
53
|
});
|
|
61
|
-
console.log(fgb.data
|
|
54
|
+
console.log(fgb.data);
|
|
55
|
+
console.log(fgb.meta.has_more, fgb.meta.next_cursor);
|
|
62
56
|
```
|
|
63
57
|
|
|
64
58
|
Talks to `https://api.maplark.com` by default.
|
|
65
59
|
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
# Functions and Parameters
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## `query()`
|
|
65
|
+
|
|
66
|
+
Fetches a single page from the API. Returns `{ data, meta }` where `data` is a GeoJSON FeatureCollection (default) or an `ArrayBuffer` for binary encodings.
|
|
67
|
+
|
|
68
|
+
#### Spatial anchors (required)
|
|
69
|
+
|
|
70
|
+
The geographical area for the request in terms of GPS coordinates or specific OSM ids.
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
| Param | Type | Description |
|
|
74
|
+
| -------- | -------- | -------------------------------------------------- |
|
|
75
|
+
| `bbox` | `string` | Bounding box as `min_lon,min_lat,max_lon,max_lat`. |
|
|
76
|
+
| `location` | `string` | Point for a radius search as `lat,lng`. Requires `radius`. |
|
|
77
|
+
| `radius` | `number` | Search radius in metres. Requires `location`. |
|
|
78
|
+
| `osmIds` | `string` | Comma-separated OSM IDs to fetch by id. |
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
#### Tags
|
|
84
|
+
|
|
85
|
+
The feature tags to filter on.
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
| Param | Type | Description |
|
|
89
|
+
| --------- | ---------- | -------------------------------------------------------------------------------- |
|
|
90
|
+
| `tags` | `string[]` | Tag filters that must all match (AND). Values like `building` or `amenity=cafe`. |
|
|
91
|
+
| `orTags` | `string[]` | Tag filters where any may match (OR). |
|
|
92
|
+
| `notTags` | `string[]` | Tag filters to exclude. |
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
#### Geometry
|
|
98
|
+
|
|
99
|
+
Geometric filters such specific OSM element type, min length, or including centroid.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
| Param | Type | Default | Description |
|
|
103
|
+
| ------------ | ---------------------- | ------- | --------------------------------------------------------------------------------------- |
|
|
104
|
+
| `type` | `string` | all | OSM element types, e.g. `node`, `way`, `relation`, or comma-separated (`way,relation`). |
|
|
105
|
+
| `shape` | `line | polygon | all` | `all` | Geometry shape filter for ways and relations. |
|
|
106
|
+
| `centroid` | `boolean` | `false` | When `true`, include a centroid on non-point features. |
|
|
107
|
+
| `clipGeometry` | `boolean` | `true` | When `true`, clip returned geometry to the requested `bbox`. Set `false` for full geometry. |
|
|
108
|
+
| `minLengthM` | `number` | | Minimum length in metres (lines). |
|
|
109
|
+
| `maxLengthM` | `number` | | Maximum length in metres (lines). |
|
|
110
|
+
| `minAreaM2` | `number` | | Minimum area in square metres (polygons). |
|
|
111
|
+
| `maxAreaM2` | `number` | | Maximum area in square metres (polygons). |
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
#### Other
|
|
117
|
+
|
|
118
|
+
Extra filters to for pagination, output format (accept),
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
| Param | Type | Default | Description |
|
|
122
|
+
| ---------------------- | --------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
123
|
+
| `accept` | `string` | `application/geo+json` | Response media type in header. Options - `application/geo+json`, `text/csv`, `text/tab-separated-values`, `application/flatgeobuf`, and `application/vnd.apache.parquet`. |
|
|
124
|
+
| `limit` | `number` | `1000` | Page size. Max `6000`. |
|
|
125
|
+
| `cursor` | `string` | | Pagination cursor from a previous `meta.next_cursor`. |
|
|
126
|
+
| `disableBudgetWarning` | `boolean` | `false` | Ignore warnings for large queries that consume budget quotas. |
|
|
127
|
+
| `zoom` | `number` | | Map zoom hint (used by presets / server-side simplification policies). |
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
### Meta
|
|
133
|
+
|
|
134
|
+
Fields for pagination and usage.
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
| Field | Description |
|
|
138
|
+
| --------------- | --------------------------------------------------------------------- |
|
|
139
|
+
| `returned` | Features in this page. |
|
|
140
|
+
| `has_more` | Whether more pages exist. |
|
|
141
|
+
| `next_cursor` | Pass as `cursor` on the next `query` call, or `null` when done. |
|
|
142
|
+
| `units_charged` | Usage for this request when present in terms of cpu and ram consumed. |
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
## `query_all`
|
|
148
|
+
|
|
149
|
+
Auto-paginates (and optionally tiles the bbox) until the result is complete or a client-side cap is hit. GeoJSON only — for FlatGeobuf / other encodings, use `query` with `accept`.
|
|
150
|
+
|
|
151
|
+
Does not take `limit` or `cursor`; paging is handled internally.
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
const all = await client.query_all({
|
|
155
|
+
bbox: '18.06,59.32,18.09,59.34',
|
|
156
|
+
tags: ['building'],
|
|
157
|
+
limitPerPage: 1000,
|
|
158
|
+
bboxTiles: 2,
|
|
159
|
+
maxPages: 15,
|
|
160
|
+
maxFeatures: 55_000,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
console.log(all.data.features.length);
|
|
164
|
+
console.log(all.meta.page_count, all.meta.has_more, all.meta.units_charged);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
### Params
|
|
170
|
+
|
|
171
|
+
Same filter params as `query` (`bbox`, `tags`, `orTags`, `notTags`, `type`, `shape`, `zoom`, `location`, `radius`, `osmIds`, `minLengthM`, `maxLengthM`, `minAreaM2`, `maxAreaM2`, `centroid`, `clipGeometry`, `disableBudgetWarning`), plus:
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
| Param | Type | Default | Description |
|
|
175
|
+
| -------------- | --------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
176
|
+
| `limitPerPage` | `number` | `1000` | Upstream `limit` per HTTP request (page size). |
|
|
177
|
+
| `bboxTiles` | `number` | `2` | Split `bbox` into this many tiles (must be a power of 2: `1`, `2`, `4`, `8`, …). Each tile is paginated separately, then features are merged and deduped. |
|
|
178
|
+
| `maxPages` | `number` | `15` | Max pages fetched **per tile**. |
|
|
179
|
+
| `maxFeatures` | `number | null` | `55000` | Cap on merged features after dedupe. Pass `null` for no cap. |
|
|
180
|
+
| `accept` | `string` | `application/geo+json` | Must be GeoJSON (or omitted). Non-GeoJSON throws. |
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
### Meta
|
|
186
|
+
|
|
187
|
+
Same fields as `query`, plus:
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
| Field | Description |
|
|
191
|
+
| ---------------------- | ------------------------------------------------------------------------------------ |
|
|
192
|
+
| `page_count` | Total upstream pages fetched. |
|
|
193
|
+
| `has_more` | `true` if stopped early (caps), or upstream still had more, or a partial relay stop. |
|
|
194
|
+
| `next_cursor` | Last cursor when incomplete; otherwise the final page cursor. |
|
|
195
|
+
| `units_charged` | Sum of units charged across pages when present. |
|
|
196
|
+
| `relay_partial` | `true` if paging stopped after a mid-stream 400/429 (partial result kept). |
|
|
197
|
+
| `relay_partial_reason` | e.g. `upstream_rejected_cursor` or `upstream_rate_limited_after_retries`. |
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
Also exports layer presets (`resolveLayerFromQuery`, `OSM_FEATURES_LAYER_PRESETS`, ...)
|
|
201
|
+
and GeoJSON payload helpers (`featureCentroid`, `geometryBounds`, `parseFeatureId`, ...).
|
|
202
|
+
|
|
203
|
+
## `estimate_cost`
|
|
204
|
+
|
|
205
|
+
Preflight credit cost via `GET /v2/osm_features/cost`. Same filter params as `query`. No OSM data is fetched.
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
const estimate = await client.estimate_cost({
|
|
209
|
+
bbox: '18.06,59.32,18.09,59.34',
|
|
210
|
+
tags: ['building'],
|
|
211
|
+
});
|
|
212
|
+
console.log(estimate.estimated_credits);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## `usage`
|
|
216
|
+
|
|
217
|
+
This month's unit-budget usage via `GET /v1/usage`.
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
const usage = await client.usage();
|
|
221
|
+
console.log(usage.tier, usage.usage_this_month, usage.remaining_this_month);
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## `places_search` / `places_nearby` / `places_details`
|
|
225
|
+
|
|
226
|
+
Place discovery and lookup. Search is a bbox or `location`+`radius`. Nearby ranks one set from a point. Details refetches a search/nearby feature id (`node/123`). Search and nearby hours use each place's local timezone; optional `asOf` pins the evaluation instant.
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
const origin = { lat: 59.316, lon: 18.075 };
|
|
230
|
+
|
|
231
|
+
const cafes = await client.places_search({
|
|
232
|
+
location: origin,
|
|
233
|
+
radius: 800,
|
|
234
|
+
orTags: ['amenity=cafe'],
|
|
235
|
+
openNow: true,
|
|
236
|
+
asOf: '2026-08-10T18:00:00+02:00',
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const nearby = await client.places_nearby({
|
|
240
|
+
location: origin,
|
|
241
|
+
orTags: ['amenity=cafe'],
|
|
242
|
+
limit: 5,
|
|
243
|
+
openNow: true,
|
|
244
|
+
asOf: '2026-08-10T18:00:00+02:00',
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const first = (cafes.features as { id: string }[])[0];
|
|
248
|
+
const details = await client.places_details({ osmType: first.id });
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
`places_details` also accepts `{ osmType: 'node', osmId: 123 }`. Hours are annotated at request time in the place's local timezone.
|
|
252
|
+
|
|
253
|
+
## `routes_isochrone` / `routes_path` / `routes_optimized_path`
|
|
254
|
+
|
|
255
|
+
Walk or bicycle routing via `POST /v1/routes/*`. Points accept `lon` or `lng`.
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
const origin = { lon: 18.075, lat: 59.316 };
|
|
259
|
+
const cafe = { lon: 18.08, lat: 59.318 };
|
|
260
|
+
|
|
261
|
+
const iso = await client.routes_isochrone({
|
|
262
|
+
origin,
|
|
263
|
+
durationS: 600,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const path = await client.routes_path({
|
|
267
|
+
stops: [origin, cafe],
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const tour = await client.routes_optimized_path({
|
|
271
|
+
start: origin,
|
|
272
|
+
stops: [cafe],
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
`routes_isochrone` takes exactly one of `maxDistanceM` or `durationS`. `routes_path` follows `stops` in listed order (no TSP). `routes_optimized_path` orders `stops` from `start`; `loop` (default true) returns to start. Optional `searchBufferM` and `travelMode` (`WALK` or `BICYCLE`).
|
|
277
|
+
|
|
278
|
+
Read the full API reference here [https://maplark.com/developer](https://maplark.com/developer) such as the OpenAPI 2.0 HTTP docs.
|
package/dist/index.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export type OSMFeaturesResult = {
|
|
|
27
27
|
};
|
|
28
28
|
/** Layer filters from presets / custom resolve. Pass into `resolveRequest` or spread into `query`. */
|
|
29
29
|
export type OSMFeaturesLayer = {
|
|
30
|
-
bbox
|
|
30
|
+
bbox?: string;
|
|
31
31
|
tags?: string[];
|
|
32
32
|
orTags?: string[];
|
|
33
33
|
notTags?: string[];
|
|
@@ -39,7 +39,10 @@ export type OSMFeaturesParams = OSMFeaturesLayer & {
|
|
|
39
39
|
limit?: number;
|
|
40
40
|
cursor?: string;
|
|
41
41
|
zoom?: number;
|
|
42
|
-
|
|
42
|
+
/** Point for a radius search as `lat,lng`. Requires `radius`. */
|
|
43
|
+
location?: string;
|
|
44
|
+
/** Search radius in metres. Requires `location`. */
|
|
45
|
+
radius?: number;
|
|
43
46
|
osmIds?: string;
|
|
44
47
|
minLengthM?: number;
|
|
45
48
|
maxLengthM?: number;
|
|
@@ -47,11 +50,67 @@ export type OSMFeaturesParams = OSMFeaturesLayer & {
|
|
|
47
50
|
maxAreaM2?: number;
|
|
48
51
|
disableBudgetWarning?: boolean;
|
|
49
52
|
centroid?: boolean;
|
|
53
|
+
/** When true (default), clip returned geometry to the requested bbox. */
|
|
54
|
+
clipGeometry?: boolean;
|
|
50
55
|
/** Accept media type. Default application/geo+json; other types put bytes in ``data``. */
|
|
51
56
|
accept?: string;
|
|
52
57
|
};
|
|
53
58
|
type QueryValue = unknown;
|
|
54
59
|
export type OSMFeaturesQuery = Record<string, QueryValue>;
|
|
60
|
+
/** Routing point. Accepts ``lon`` or ``lng``. */
|
|
61
|
+
export type LonLat = {
|
|
62
|
+
lat: number;
|
|
63
|
+
} & ({
|
|
64
|
+
lon: number;
|
|
65
|
+
} | {
|
|
66
|
+
lng: number;
|
|
67
|
+
});
|
|
68
|
+
export type RouteTravelMode = 'WALK' | 'BICYCLE';
|
|
69
|
+
export type RoutesIsochroneParams = {
|
|
70
|
+
origin: LonLat;
|
|
71
|
+
maxDistanceM?: number;
|
|
72
|
+
durationS?: number;
|
|
73
|
+
searchBufferM?: number;
|
|
74
|
+
travelMode?: RouteTravelMode;
|
|
75
|
+
};
|
|
76
|
+
export type RoutesPathParams = {
|
|
77
|
+
stops: LonLat[];
|
|
78
|
+
searchBufferM?: number;
|
|
79
|
+
travelMode?: RouteTravelMode;
|
|
80
|
+
};
|
|
81
|
+
export type RoutesOptimizedPathParams = {
|
|
82
|
+
start: LonLat;
|
|
83
|
+
stops: LonLat[];
|
|
84
|
+
searchBufferM?: number;
|
|
85
|
+
loop?: boolean;
|
|
86
|
+
travelMode?: RouteTravelMode;
|
|
87
|
+
};
|
|
88
|
+
export type PlacesSearchParams = {
|
|
89
|
+
bbox?: string;
|
|
90
|
+
location?: LonLat;
|
|
91
|
+
radius?: number;
|
|
92
|
+
type?: string;
|
|
93
|
+
tags?: string[];
|
|
94
|
+
orTags?: string[];
|
|
95
|
+
limit?: number;
|
|
96
|
+
openNow?: boolean;
|
|
97
|
+
asOf?: string;
|
|
98
|
+
};
|
|
99
|
+
export type PlacesNearbyParams = {
|
|
100
|
+
location: LonLat;
|
|
101
|
+
radius?: number;
|
|
102
|
+
type?: string;
|
|
103
|
+
tags?: string[];
|
|
104
|
+
orTags?: string[];
|
|
105
|
+
limit?: number;
|
|
106
|
+
openNow?: boolean;
|
|
107
|
+
asOf?: string;
|
|
108
|
+
};
|
|
109
|
+
export type PlacesDetailsParams = {
|
|
110
|
+
/** ``node`` / ``way`` / ``relation``, or a full ``node/123`` feature id. */
|
|
111
|
+
osmType: string;
|
|
112
|
+
osmId?: number | string;
|
|
113
|
+
};
|
|
55
114
|
type OSMFeaturesDependencies = {
|
|
56
115
|
fetchFn?: typeof fetch;
|
|
57
116
|
sleepFn?: (ms: number) => Promise<void>;
|
|
@@ -78,12 +137,18 @@ export declare class OSMFeatures {
|
|
|
78
137
|
/** Map Express/query params + resolved layer into flat `query` / `query_all` params. */
|
|
79
138
|
resolveRequest(query: OSMFeaturesQuery, layer: OSMFeaturesLayer): OSMFeaturesParams;
|
|
80
139
|
private throwUpstreamError;
|
|
140
|
+
/** GET/POST with 429 retry. Throws on non-OK. */
|
|
141
|
+
private _fetchOk;
|
|
142
|
+
/** POST JSON to a geo-agent path. */
|
|
143
|
+
private _postJson;
|
|
144
|
+
/** GET JSON from a geo-agent or account path. */
|
|
145
|
+
private _getJson;
|
|
81
146
|
/** Single HTTP request with retry. Throws on non-OK (same role as Python `_raw_query`). */
|
|
82
147
|
private _rawQuery;
|
|
83
148
|
/** Single upstream page. Params map 1:1 to server query string (no tiling). */
|
|
84
|
-
query({ bbox, tags, orTags, notTags, type, shape, limit, cursor, zoom,
|
|
149
|
+
query({ bbox, tags, orTags, notTags, type, shape, limit, cursor, zoom, location, radius, osmIds, minLengthM, maxLengthM, minAreaM2, maxAreaM2, disableBudgetWarning, centroid, clipGeometry, accept, }: OSMFeaturesParams, dependencies?: OSMFeaturesDependencies): Promise<OSMFeaturesResult>;
|
|
85
150
|
/** Auto-paginate (and optionally tile) until complete. Each page uses `_rawQuery`. */
|
|
86
|
-
query_all({ bbox, tags, orTags, notTags, type, shape, zoom,
|
|
151
|
+
query_all({ bbox, tags, orTags, notTags, type, shape, zoom, location, radius, osmIds, minLengthM, maxLengthM, minAreaM2, maxAreaM2, disableBudgetWarning, centroid, clipGeometry, accept, limitPerPage, bboxTiles, maxPages, maxFeatures, }: Omit<OSMFeaturesParams, 'limit' | 'cursor'> & {
|
|
87
152
|
/** Upstream `limit` per HTTP request (page size). */
|
|
88
153
|
limitPerPage?: number;
|
|
89
154
|
bboxTiles?: number;
|
|
@@ -91,6 +156,22 @@ export declare class OSMFeatures {
|
|
|
91
156
|
/** Cap on merged features. `null` = no cap. */
|
|
92
157
|
maxFeatures?: number | null;
|
|
93
158
|
}, dependencies?: OSMFeaturesDependencies): Promise<OSMGeoJSONResult>;
|
|
159
|
+
/** Preflight credit cost (``GET /v2/osm_features/cost``). */
|
|
160
|
+
estimate_cost(params?: OSMFeaturesParams, dependencies?: OSMFeaturesDependencies): Promise<Record<string, unknown>>;
|
|
161
|
+
/** This month's unit-budget usage (``GET /v1/usage``). */
|
|
162
|
+
usage(dependencies?: OSMFeaturesDependencies): Promise<Record<string, unknown>>;
|
|
163
|
+
/** Find places in a bbox or radius (``POST /v1/places/search``). */
|
|
164
|
+
places_search(params: PlacesSearchParams, dependencies?: OSMFeaturesDependencies): Promise<Record<string, unknown>>;
|
|
165
|
+
/** Nearest places from a point (``POST /v1/places/nearby``). */
|
|
166
|
+
places_nearby(params: PlacesNearbyParams, dependencies?: OSMFeaturesDependencies): Promise<Record<string, unknown>>;
|
|
167
|
+
/** One place by OSM id (``GET /v1/places/{osm_type}/{osm_id}``). */
|
|
168
|
+
places_details(params: PlacesDetailsParams, dependencies?: OSMFeaturesDependencies): Promise<Record<string, unknown>>;
|
|
169
|
+
/** Reach polygon along the walk/bike network (``POST /v1/routes/isochrone``). */
|
|
170
|
+
routes_isochrone(params: RoutesIsochroneParams, dependencies?: OSMFeaturesDependencies): Promise<Record<string, unknown>>;
|
|
171
|
+
/** Given-order walk/bike path (``POST /v1/routes/path``). */
|
|
172
|
+
routes_path(params: RoutesPathParams, dependencies?: OSMFeaturesDependencies): Promise<Record<string, unknown>>;
|
|
173
|
+
/** TSP walk/bike tour from ``start`` (``POST /v1/routes/optimized_path``). ``loop`` returns to start. */
|
|
174
|
+
routes_optimized_path(params: RoutesOptimizedPathParams, dependencies?: OSMFeaturesDependencies): Promise<Record<string, unknown>>;
|
|
94
175
|
}
|
|
95
176
|
export * from './geojson-feature.js';
|
|
96
177
|
export * from './preset/index.js';
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ function metaFromHeaders(headers, featureCount) {
|
|
|
21
21
|
}
|
|
22
22
|
return meta;
|
|
23
23
|
}
|
|
24
|
+
const PLACE_TYPES = new Set(['node', 'way', 'relation']);
|
|
24
25
|
const DEFAULT_BASE_URL = 'https://api.maplark.com';
|
|
25
26
|
const DEFAULT_LIMIT = 1000;
|
|
26
27
|
const MAX_LIMIT = 6000;
|
|
@@ -145,7 +146,9 @@ export function splitBbox(bbox, tileCount) {
|
|
|
145
146
|
}
|
|
146
147
|
function buildFeaturesQuery(params) {
|
|
147
148
|
const query = new URLSearchParams();
|
|
148
|
-
|
|
149
|
+
if (params.bbox) {
|
|
150
|
+
query.set('bbox', params.bbox);
|
|
151
|
+
}
|
|
149
152
|
query.set('limit', String(params.limit));
|
|
150
153
|
if (params.cursor) {
|
|
151
154
|
query.set('cursor', params.cursor);
|
|
@@ -153,8 +156,11 @@ function buildFeaturesQuery(params) {
|
|
|
153
156
|
if (params.zoom != null) {
|
|
154
157
|
query.set('zoom', String(params.zoom));
|
|
155
158
|
}
|
|
156
|
-
if (params.
|
|
157
|
-
query.set('
|
|
159
|
+
if (params.location) {
|
|
160
|
+
query.set('location', params.location);
|
|
161
|
+
}
|
|
162
|
+
if (params.radius != null) {
|
|
163
|
+
query.set('radius', String(params.radius));
|
|
158
164
|
}
|
|
159
165
|
if (params.osmIds) {
|
|
160
166
|
query.set('osm_ids', params.osmIds);
|
|
@@ -177,6 +183,9 @@ function buildFeaturesQuery(params) {
|
|
|
177
183
|
if (params.centroid != null) {
|
|
178
184
|
query.set('centroid', String(params.centroid));
|
|
179
185
|
}
|
|
186
|
+
if (params.clipGeometry != null) {
|
|
187
|
+
query.set('clipGeometry', String(params.clipGeometry));
|
|
188
|
+
}
|
|
180
189
|
if (params.type) {
|
|
181
190
|
query.set('type', params.type);
|
|
182
191
|
}
|
|
@@ -194,6 +203,122 @@ function buildFeaturesQuery(params) {
|
|
|
194
203
|
}
|
|
195
204
|
return query;
|
|
196
205
|
}
|
|
206
|
+
function lonlat(point) {
|
|
207
|
+
const lon = 'lon' in point ? point.lon : point.lng;
|
|
208
|
+
return { lon, lat: point.lat };
|
|
209
|
+
}
|
|
210
|
+
function latlng(point) {
|
|
211
|
+
const lng = 'lng' in point ? point.lng : point.lon;
|
|
212
|
+
return { lat: point.lat, lng };
|
|
213
|
+
}
|
|
214
|
+
function parsePlaceRef(osmType, osmId) {
|
|
215
|
+
if (osmId == null) {
|
|
216
|
+
const raw = osmType.trim();
|
|
217
|
+
const slash = raw.indexOf('/');
|
|
218
|
+
if (slash < 0) {
|
|
219
|
+
throw appError(400, 'invalid_place_id', 'place id must be node|way|relation plus a positive osm_id');
|
|
220
|
+
}
|
|
221
|
+
return parsePlaceRef(raw.slice(0, slash), raw.slice(slash + 1));
|
|
222
|
+
}
|
|
223
|
+
const kind = osmType.trim().toLowerCase();
|
|
224
|
+
if (!PLACE_TYPES.has(kind)) {
|
|
225
|
+
throw appError(400, 'invalid_place_id', 'osm_type must be node, way, or relation');
|
|
226
|
+
}
|
|
227
|
+
const n = typeof osmId === 'number' ? osmId : Number.parseInt(String(osmId).trim(), 10);
|
|
228
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
229
|
+
throw appError(400, 'invalid_place_id', 'osm_id must be a positive integer');
|
|
230
|
+
}
|
|
231
|
+
return { osmType: kind, osmId: n };
|
|
232
|
+
}
|
|
233
|
+
function placesSearchBody(params) {
|
|
234
|
+
const body = { limit: params.limit ?? 100 };
|
|
235
|
+
if (params.bbox != null) {
|
|
236
|
+
body['bbox'] = params.bbox;
|
|
237
|
+
}
|
|
238
|
+
if (params.location != null) {
|
|
239
|
+
body['location'] = latlng(params.location);
|
|
240
|
+
}
|
|
241
|
+
if (params.radius != null) {
|
|
242
|
+
body['radius'] = params.radius;
|
|
243
|
+
}
|
|
244
|
+
if (params.type != null) {
|
|
245
|
+
body['type'] = params.type;
|
|
246
|
+
}
|
|
247
|
+
if (params.tags?.length) {
|
|
248
|
+
body['tags'] = params.tags;
|
|
249
|
+
}
|
|
250
|
+
if (params.orTags?.length) {
|
|
251
|
+
body['orTags'] = params.orTags;
|
|
252
|
+
}
|
|
253
|
+
if (params.openNow) {
|
|
254
|
+
body['openNow'] = true;
|
|
255
|
+
}
|
|
256
|
+
if (params.asOf != null) {
|
|
257
|
+
body['asOf'] = params.asOf;
|
|
258
|
+
}
|
|
259
|
+
return body;
|
|
260
|
+
}
|
|
261
|
+
function placesNearbyBody(params) {
|
|
262
|
+
const body = {
|
|
263
|
+
location: latlng(params.location),
|
|
264
|
+
radius: params.radius ?? 1000,
|
|
265
|
+
limit: params.limit ?? 10,
|
|
266
|
+
};
|
|
267
|
+
if (params.type != null) {
|
|
268
|
+
body['type'] = params.type;
|
|
269
|
+
}
|
|
270
|
+
if (params.tags?.length) {
|
|
271
|
+
body['tags'] = params.tags;
|
|
272
|
+
}
|
|
273
|
+
if (params.orTags?.length) {
|
|
274
|
+
body['orTags'] = params.orTags;
|
|
275
|
+
}
|
|
276
|
+
if (params.openNow) {
|
|
277
|
+
body['openNow'] = true;
|
|
278
|
+
}
|
|
279
|
+
if (params.asOf != null) {
|
|
280
|
+
body['asOf'] = params.asOf;
|
|
281
|
+
}
|
|
282
|
+
return body;
|
|
283
|
+
}
|
|
284
|
+
function routesIsochroneBody(params) {
|
|
285
|
+
const body = {
|
|
286
|
+
origin: lonlat(params.origin),
|
|
287
|
+
travelMode: params.travelMode ?? 'WALK',
|
|
288
|
+
};
|
|
289
|
+
if (params.maxDistanceM != null) {
|
|
290
|
+
body['max_distance_m'] = params.maxDistanceM;
|
|
291
|
+
}
|
|
292
|
+
if (params.durationS != null) {
|
|
293
|
+
body['duration_s'] = params.durationS;
|
|
294
|
+
}
|
|
295
|
+
if (params.searchBufferM != null) {
|
|
296
|
+
body['search_buffer_m'] = params.searchBufferM;
|
|
297
|
+
}
|
|
298
|
+
return body;
|
|
299
|
+
}
|
|
300
|
+
function routesPathBody(params) {
|
|
301
|
+
const body = {
|
|
302
|
+
stops: params.stops.map(lonlat),
|
|
303
|
+
travelMode: params.travelMode ?? 'WALK',
|
|
304
|
+
};
|
|
305
|
+
if (params.searchBufferM != null) {
|
|
306
|
+
body['search_buffer_m'] = params.searchBufferM;
|
|
307
|
+
}
|
|
308
|
+
return body;
|
|
309
|
+
}
|
|
310
|
+
function routesOptimizedPathBody(params) {
|
|
311
|
+
const body = {
|
|
312
|
+
start: lonlat(params.start),
|
|
313
|
+
stops: params.stops.map(lonlat),
|
|
314
|
+
loop: params.loop ?? true,
|
|
315
|
+
travelMode: params.travelMode ?? 'WALK',
|
|
316
|
+
};
|
|
317
|
+
if (params.searchBufferM != null) {
|
|
318
|
+
body['search_buffer_m'] = params.searchBufferM;
|
|
319
|
+
}
|
|
320
|
+
return body;
|
|
321
|
+
}
|
|
197
322
|
function sleep(ms) {
|
|
198
323
|
return new Promise((resolve) => {
|
|
199
324
|
setTimeout(resolve, ms);
|
|
@@ -262,7 +387,8 @@ export class OSMFeatures {
|
|
|
262
387
|
limit: parseLimit(query),
|
|
263
388
|
cursor: optionalString(query, 'cursor'),
|
|
264
389
|
zoom: optionalNumber(query, 'zoom'),
|
|
265
|
-
|
|
390
|
+
location: optionalString(query, 'location'),
|
|
391
|
+
radius: optionalNumber(query, 'radius'),
|
|
266
392
|
osmIds: optionalString(query, 'osm_ids'),
|
|
267
393
|
minLengthM: optionalNumber(query, 'min_length_m'),
|
|
268
394
|
maxLengthM: optionalNumber(query, 'max_length_m'),
|
|
@@ -270,6 +396,7 @@ export class OSMFeatures {
|
|
|
270
396
|
maxAreaM2: optionalNumber(query, 'max_area_m2'),
|
|
271
397
|
disableBudgetWarning: optionalBoolean(query, 'disable_budget_warning'),
|
|
272
398
|
centroid: optionalBoolean(query, 'centroid'),
|
|
399
|
+
clipGeometry: optionalBoolean(query, 'clipGeometry'),
|
|
273
400
|
};
|
|
274
401
|
}
|
|
275
402
|
async throwUpstreamError(upstream) {
|
|
@@ -283,25 +410,15 @@ export class OSMFeatures {
|
|
|
283
410
|
err.upstreamDetail = upstreamDetail;
|
|
284
411
|
throw err;
|
|
285
412
|
}
|
|
286
|
-
/**
|
|
287
|
-
async
|
|
288
|
-
const query = buildFeaturesQuery(params);
|
|
289
|
-
const upstreamUrl = new URL(`${this.apiBaseUrl}/v2/osm_features`);
|
|
290
|
-
for (const [key, value] of query.entries()) {
|
|
291
|
-
upstreamUrl.searchParams.append(key, value);
|
|
292
|
-
}
|
|
293
|
-
let upstream;
|
|
413
|
+
/** GET/POST with 429 retry. Throws on non-OK. */
|
|
414
|
+
async _fetchOk(url, init, fetchFn, sleepFn, nowFn) {
|
|
294
415
|
let retryAttempt = 0;
|
|
295
416
|
while (true) {
|
|
417
|
+
let upstream;
|
|
296
418
|
try {
|
|
297
|
-
upstream = await fetchFn(
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
301
|
-
Accept: params.accept || GEOJSON_ACCEPT,
|
|
302
|
-
'User-Agent': 'osmfeatures',
|
|
303
|
-
},
|
|
304
|
-
signal: AbortSignal.timeout(this.timeoutMs),
|
|
419
|
+
upstream = await fetchFn(url, {
|
|
420
|
+
...init,
|
|
421
|
+
signal: init.signal ?? AbortSignal.timeout(this.timeoutMs),
|
|
305
422
|
});
|
|
306
423
|
}
|
|
307
424
|
catch (error) {
|
|
@@ -313,7 +430,7 @@ export class OSMFeatures {
|
|
|
313
430
|
throw err;
|
|
314
431
|
}
|
|
315
432
|
if (upstream.ok) {
|
|
316
|
-
|
|
433
|
+
return upstream;
|
|
317
434
|
}
|
|
318
435
|
if (upstream.status === 429 && retryAttempt < this.retryAttempts) {
|
|
319
436
|
retryAttempt += 1;
|
|
@@ -335,11 +452,60 @@ export class OSMFeatures {
|
|
|
335
452
|
}
|
|
336
453
|
}
|
|
337
454
|
}
|
|
455
|
+
// ponytail: simple stderr-visible warning for 429 retries; upgrade to injected logger if consumers need structured logs.
|
|
456
|
+
console.warn(`[osmfeatures] Upstream returned 429; retry ${retryAttempt}/${this.retryAttempts} in ${Math.round(waitMs)}ms.`);
|
|
338
457
|
await sleepFn(waitMs);
|
|
339
458
|
continue;
|
|
340
459
|
}
|
|
341
460
|
await this.throwUpstreamError(upstream);
|
|
342
461
|
}
|
|
462
|
+
}
|
|
463
|
+
/** POST JSON to a geo-agent path. */
|
|
464
|
+
async _postJson(path, body, fetchFn, sleepFn, nowFn) {
|
|
465
|
+
const upstream = await this._fetchOk(`${this.apiBaseUrl}${path}`, {
|
|
466
|
+
method: 'POST',
|
|
467
|
+
headers: {
|
|
468
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
469
|
+
Accept: 'application/json',
|
|
470
|
+
'Content-Type': 'application/json',
|
|
471
|
+
'User-Agent': 'osmfeatures',
|
|
472
|
+
},
|
|
473
|
+
body: JSON.stringify(body),
|
|
474
|
+
}, fetchFn, sleepFn, nowFn);
|
|
475
|
+
return (await upstream.json());
|
|
476
|
+
}
|
|
477
|
+
/** GET JSON from a geo-agent or account path. */
|
|
478
|
+
async _getJson(path, query, fetchFn, sleepFn, nowFn) {
|
|
479
|
+
const url = new URL(`${this.apiBaseUrl}${path}`);
|
|
480
|
+
const entries = query instanceof URLSearchParams ? query.entries() : Object.entries(query);
|
|
481
|
+
for (const [key, value] of entries) {
|
|
482
|
+
url.searchParams.append(key, value);
|
|
483
|
+
}
|
|
484
|
+
const upstream = await this._fetchOk(url.toString(), {
|
|
485
|
+
method: 'GET',
|
|
486
|
+
headers: {
|
|
487
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
488
|
+
Accept: 'application/json',
|
|
489
|
+
'User-Agent': 'osmfeatures',
|
|
490
|
+
},
|
|
491
|
+
}, fetchFn, sleepFn, nowFn);
|
|
492
|
+
return (await upstream.json());
|
|
493
|
+
}
|
|
494
|
+
/** Single HTTP request with retry. Throws on non-OK (same role as Python `_raw_query`). */
|
|
495
|
+
async _rawQuery(params, fetchFn, sleepFn, nowFn) {
|
|
496
|
+
const query = buildFeaturesQuery(params);
|
|
497
|
+
const upstreamUrl = new URL(`${this.apiBaseUrl}/v2/osm_features`);
|
|
498
|
+
for (const [key, value] of query.entries()) {
|
|
499
|
+
upstreamUrl.searchParams.append(key, value);
|
|
500
|
+
}
|
|
501
|
+
const upstream = await this._fetchOk(upstreamUrl.toString(), {
|
|
502
|
+
method: 'GET',
|
|
503
|
+
headers: {
|
|
504
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
505
|
+
Accept: params.accept || GEOJSON_ACCEPT,
|
|
506
|
+
'User-Agent': 'osmfeatures',
|
|
507
|
+
},
|
|
508
|
+
}, fetchFn, sleepFn, nowFn);
|
|
343
509
|
if (isGeojsonAccept(params.accept)) {
|
|
344
510
|
const body = (await upstream.json());
|
|
345
511
|
const features = Array.isArray(body.features) ? body.features : [];
|
|
@@ -352,7 +518,7 @@ export class OSMFeatures {
|
|
|
352
518
|
};
|
|
353
519
|
}
|
|
354
520
|
/** Single upstream page. Params map 1:1 to server query string (no tiling). */
|
|
355
|
-
async query({ bbox, tags, orTags, notTags, type, shape, limit = DEFAULT_LIMIT, cursor, zoom,
|
|
521
|
+
async query({ bbox, tags, orTags, notTags, type, shape, limit = DEFAULT_LIMIT, cursor, zoom, location, radius, osmIds, minLengthM, maxLengthM, minAreaM2, maxAreaM2, disableBudgetWarning, centroid, clipGeometry, accept, }, dependencies = {}) {
|
|
356
522
|
const payload = await this._rawQuery({
|
|
357
523
|
bbox,
|
|
358
524
|
tags,
|
|
@@ -363,7 +529,8 @@ export class OSMFeatures {
|
|
|
363
529
|
limit,
|
|
364
530
|
cursor,
|
|
365
531
|
zoom,
|
|
366
|
-
|
|
532
|
+
location,
|
|
533
|
+
radius,
|
|
367
534
|
osmIds,
|
|
368
535
|
minLengthM,
|
|
369
536
|
maxLengthM,
|
|
@@ -371,12 +538,13 @@ export class OSMFeatures {
|
|
|
371
538
|
maxAreaM2,
|
|
372
539
|
disableBudgetWarning,
|
|
373
540
|
centroid,
|
|
541
|
+
clipGeometry,
|
|
374
542
|
accept,
|
|
375
543
|
}, dependencies.fetchFn ?? fetch, dependencies.sleepFn ?? sleep, dependencies.nowFn ?? Date.now);
|
|
376
544
|
return payload;
|
|
377
545
|
}
|
|
378
546
|
/** Auto-paginate (and optionally tile) until complete. Each page uses `_rawQuery`. */
|
|
379
|
-
async query_all({ bbox, tags, orTags, notTags, type, shape, zoom,
|
|
547
|
+
async query_all({ bbox, tags, orTags, notTags, type, shape, zoom, location, radius, osmIds, minLengthM, maxLengthM, minAreaM2, maxAreaM2, disableBudgetWarning, centroid, clipGeometry, accept, limitPerPage = DEFAULT_LIMIT, bboxTiles = 2, maxPages = 15, maxFeatures = 55_000, }, dependencies = {}) {
|
|
380
548
|
if (!isGeojsonAccept(accept)) {
|
|
381
549
|
throw appError(400, 'invalid_accept', 'query_all only supports GeoJSON; use query({ accept }) for binary encodings.');
|
|
382
550
|
}
|
|
@@ -387,7 +555,7 @@ export class OSMFeatures {
|
|
|
387
555
|
const sleepFn = dependencies.sleepFn ?? sleep;
|
|
388
556
|
const nowFn = dependencies.nowFn ?? Date.now;
|
|
389
557
|
const featureCap = maxFeatures == null ? Number.POSITIVE_INFINITY : maxFeatures;
|
|
390
|
-
const tileBboxes = splitBbox(bbox, bboxTiles);
|
|
558
|
+
const tileBboxes = bbox ? splitBbox(bbox, bboxTiles) : [undefined];
|
|
391
559
|
const allFeatures = [];
|
|
392
560
|
let pageCount = 0;
|
|
393
561
|
let lastPage = null;
|
|
@@ -405,7 +573,8 @@ export class OSMFeatures {
|
|
|
405
573
|
shape,
|
|
406
574
|
limit: limitPerPage,
|
|
407
575
|
zoom,
|
|
408
|
-
|
|
576
|
+
location,
|
|
577
|
+
radius,
|
|
409
578
|
osmIds,
|
|
410
579
|
minLengthM,
|
|
411
580
|
maxLengthM,
|
|
@@ -413,6 +582,7 @@ export class OSMFeatures {
|
|
|
413
582
|
maxAreaM2,
|
|
414
583
|
disableBudgetWarning,
|
|
415
584
|
centroid,
|
|
585
|
+
clipGeometry,
|
|
416
586
|
};
|
|
417
587
|
for (const tileBbox of tileBboxes) {
|
|
418
588
|
if (allFeatures.length >= featureCap) {
|
|
@@ -492,6 +662,58 @@ export class OSMFeatures {
|
|
|
492
662
|
}
|
|
493
663
|
return resultFromFeatures(features, meta);
|
|
494
664
|
}
|
|
665
|
+
/** Preflight credit cost (``GET /v2/osm_features/cost``). */
|
|
666
|
+
async estimate_cost(params = {}, dependencies = {}) {
|
|
667
|
+
return this._getJson('/v2/osm_features/cost', buildFeaturesQuery({
|
|
668
|
+
bbox: params.bbox,
|
|
669
|
+
tags: params.tags,
|
|
670
|
+
orTags: params.orTags,
|
|
671
|
+
notTags: params.notTags,
|
|
672
|
+
type: params.type,
|
|
673
|
+
shape: params.shape,
|
|
674
|
+
limit: params.limit ?? DEFAULT_LIMIT,
|
|
675
|
+
zoom: params.zoom,
|
|
676
|
+
location: params.location,
|
|
677
|
+
radius: params.radius,
|
|
678
|
+
osmIds: params.osmIds,
|
|
679
|
+
minLengthM: params.minLengthM,
|
|
680
|
+
maxLengthM: params.maxLengthM,
|
|
681
|
+
minAreaM2: params.minAreaM2,
|
|
682
|
+
maxAreaM2: params.maxAreaM2,
|
|
683
|
+
disableBudgetWarning: params.disableBudgetWarning,
|
|
684
|
+
centroid: params.centroid,
|
|
685
|
+
clipGeometry: params.clipGeometry,
|
|
686
|
+
}), dependencies.fetchFn ?? fetch, dependencies.sleepFn ?? sleep, dependencies.nowFn ?? Date.now);
|
|
687
|
+
}
|
|
688
|
+
/** This month's unit-budget usage (``GET /v1/usage``). */
|
|
689
|
+
async usage(dependencies = {}) {
|
|
690
|
+
return this._getJson('/v1/usage', {}, dependencies.fetchFn ?? fetch, dependencies.sleepFn ?? sleep, dependencies.nowFn ?? Date.now);
|
|
691
|
+
}
|
|
692
|
+
/** Find places in a bbox or radius (``POST /v1/places/search``). */
|
|
693
|
+
async places_search(params, dependencies = {}) {
|
|
694
|
+
return this._postJson('/v1/places/search', placesSearchBody(params), dependencies.fetchFn ?? fetch, dependencies.sleepFn ?? sleep, dependencies.nowFn ?? Date.now);
|
|
695
|
+
}
|
|
696
|
+
/** Nearest places from a point (``POST /v1/places/nearby``). */
|
|
697
|
+
async places_nearby(params, dependencies = {}) {
|
|
698
|
+
return this._postJson('/v1/places/nearby', placesNearbyBody(params), dependencies.fetchFn ?? fetch, dependencies.sleepFn ?? sleep, dependencies.nowFn ?? Date.now);
|
|
699
|
+
}
|
|
700
|
+
/** One place by OSM id (``GET /v1/places/{osm_type}/{osm_id}``). */
|
|
701
|
+
async places_details(params, dependencies = {}) {
|
|
702
|
+
const { osmType, osmId } = parsePlaceRef(params.osmType, params.osmId);
|
|
703
|
+
return this._getJson(`/v1/places/${osmType}/${osmId}`, {}, dependencies.fetchFn ?? fetch, dependencies.sleepFn ?? sleep, dependencies.nowFn ?? Date.now);
|
|
704
|
+
}
|
|
705
|
+
/** Reach polygon along the walk/bike network (``POST /v1/routes/isochrone``). */
|
|
706
|
+
async routes_isochrone(params, dependencies = {}) {
|
|
707
|
+
return this._postJson('/v1/routes/isochrone', routesIsochroneBody(params), dependencies.fetchFn ?? fetch, dependencies.sleepFn ?? sleep, dependencies.nowFn ?? Date.now);
|
|
708
|
+
}
|
|
709
|
+
/** Given-order walk/bike path (``POST /v1/routes/path``). */
|
|
710
|
+
async routes_path(params, dependencies = {}) {
|
|
711
|
+
return this._postJson('/v1/routes/path', routesPathBody(params), dependencies.fetchFn ?? fetch, dependencies.sleepFn ?? sleep, dependencies.nowFn ?? Date.now);
|
|
712
|
+
}
|
|
713
|
+
/** TSP walk/bike tour from ``start`` (``POST /v1/routes/optimized_path``). ``loop`` returns to start. */
|
|
714
|
+
async routes_optimized_path(params, dependencies = {}) {
|
|
715
|
+
return this._postJson('/v1/routes/optimized_path', routesOptimizedPathBody(params), dependencies.fetchFn ?? fetch, dependencies.sleepFn ?? sleep, dependencies.nowFn ?? Date.now);
|
|
716
|
+
}
|
|
495
717
|
}
|
|
496
718
|
export * from './geojson-feature.js';
|
|
497
719
|
export * from './preset/index.js';
|
package/dist/preset/catalog.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "osmfeatures",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Get OpenStreetMap features such as buildings, roads, or points of interest from dedicated servers with a single API call in GeoJSON, FlatGeobuf, Geoparquet, and CSV formats.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|