@ferrybig/cgeodata-reader 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +313 -2
- package/binary-format.md +2366 -0
- package/dist/cache.d.ts +24 -0
- package/dist/cache.js +60 -0
- package/dist/cellBounds.d.ts +19 -0
- package/dist/cellBounds.js +36 -0
- package/dist/chunk.d.ts +34 -0
- package/dist/chunk.js +269 -0
- package/dist/chunkDecoder.d.ts +39 -0
- package/dist/chunkDecoder.js +116 -0
- package/dist/chunkDecoderWasm.d.ts +2 -0
- package/dist/chunkDecoderWasm.js +4 -0
- package/dist/cli/buildLocateIndex.d.ts +2 -0
- package/dist/cli/buildLocateIndex.js +46 -0
- package/dist/cli/buildSearchIndex.d.ts +2 -0
- package/dist/cli/buildSearchIndex.js +90 -0
- package/dist/cli/chunk.d.ts +2 -0
- package/dist/cli/chunk.js +67 -0
- package/dist/cli/info.d.ts +2 -0
- package/dist/cli/info.js +87 -0
- package/dist/cli/list.d.ts +2 -0
- package/dist/cli/list.js +71 -0
- package/dist/cli/locate.d.ts +2 -0
- package/dist/cli/locate.js +64 -0
- package/dist/cli/lookup.d.ts +2 -0
- package/dist/cli/lookup.js +128 -0
- package/dist/cli/search.d.ts +2 -0
- package/dist/cli/search.js +119 -0
- package/dist/cli/shared.d.ts +50 -0
- package/dist/cli/shared.js +140 -0
- package/dist/cli/show.d.ts +2 -0
- package/dist/cli/show.js +38 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +107 -0
- package/dist/container.d.ts +17 -0
- package/dist/container.js +68 -0
- package/dist/directory.d.ts +21 -0
- package/dist/directory.js +31 -0
- package/dist/errors.d.ts +52 -0
- package/dist/errors.js +56 -0
- package/dist/format.d.ts +21 -0
- package/dist/format.js +22 -0
- package/dist/header.d.ts +29 -0
- package/dist/header.js +69 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +18 -0
- package/dist/indexes.d.ts +11 -0
- package/dist/indexes.js +12 -0
- package/dist/inflate.d.ts +21 -0
- package/dist/inflate.js +61 -0
- package/dist/locateIndex.d.ts +142 -0
- package/dist/locateIndex.js +561 -0
- package/dist/morton.d.ts +28 -0
- package/dist/morton.js +51 -0
- package/dist/node.d.ts +65 -0
- package/dist/node.js +137 -0
- package/dist/placeTable.d.ts +69 -0
- package/dist/placeTable.js +177 -0
- package/dist/reader.d.ts +534 -0
- package/dist/reader.js +1128 -0
- package/dist/relevance.d.ts +184 -0
- package/dist/relevance.js +266 -0
- package/dist/searchIndex.d.ts +148 -0
- package/dist/searchIndex.js +786 -0
- package/dist/source.d.ts +49 -0
- package/dist/source.js +50 -0
- package/dist/turns.d.ts +19 -0
- package/dist/turns.js +40 -0
- package/dist/varint.d.ts +52 -0
- package/dist/varint.js +137 -0
- package/package.json +76 -5
package/dist/reader.d.ts
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import { type BoundingBox } from "./cellBounds.ts";
|
|
2
|
+
import { type ChunkRecords } from "./chunk.ts";
|
|
3
|
+
import type { CGeoDataDirectory } from "./directory.ts";
|
|
4
|
+
import type { CGeoDataHeader } from "./header.ts";
|
|
5
|
+
import { type Inflater } from "./inflate.ts";
|
|
6
|
+
import { LocateIndex } from "./locateIndex.ts";
|
|
7
|
+
import { SearchIndex } from "./searchIndex.ts";
|
|
8
|
+
import { type SourceInput } from "./source.ts";
|
|
9
|
+
/** `lookup()`'s result: where a point resolves, without names. */
|
|
10
|
+
export interface PlaceIdResult {
|
|
11
|
+
/** The global id (Section 7.5) of the finest place resolved -- the city,
|
|
12
|
+
* else the region, else the country -- or null where nothing resolved.
|
|
13
|
+
* Two results with the same id have the same names, so a caller doing
|
|
14
|
+
* many lookups can key per-place work (a color, a label) on it instead
|
|
15
|
+
* of on the names. */
|
|
16
|
+
placeId: number | null;
|
|
17
|
+
/**
|
|
18
|
+
* The lat/lon rectangle this result is valid for -- every coordinate in
|
|
19
|
+
* this box resolves to the exact same result as the one just queried.
|
|
20
|
+
* Callers doing many nearby lookups (e.g. a tile renderer sampling a
|
|
21
|
+
* grid of pixels) can cache on this to skip repeat queries that would
|
|
22
|
+
* land in the same underlying cell. It's as tight as the cell that
|
|
23
|
+
* produced the result when one was matched; for "unknown" results it's
|
|
24
|
+
* the whole empty chunk when possible, otherwise the single finest-grain
|
|
25
|
+
* cell containing the query point (always correct, just not maximal --
|
|
26
|
+
* the actual empty region in a gap between records isn't generally a
|
|
27
|
+
* plain rectangle, so this doesn't try to find its full extent).
|
|
28
|
+
*/
|
|
29
|
+
boundingBox: BoundingBox;
|
|
30
|
+
/** Index of the chunk the query point falls in (Section 6.1's
|
|
31
|
+
* `r * W + c`), whether or not that chunk holds any records. Useful for
|
|
32
|
+
* narrowing a rebuild to one chunk with the compressor's `--only-chunks`
|
|
33
|
+
* when a query resolves unexpectedly. */
|
|
34
|
+
chunkIndex: number;
|
|
35
|
+
}
|
|
36
|
+
/** `query()`'s result: `lookup()`'s, with the place and its ancestors
|
|
37
|
+
* named. A level that doesn't apply is null: `city` for a point resolved
|
|
38
|
+
* only to a region, all three where nothing resolved. */
|
|
39
|
+
export interface QueryResult extends PlaceIdResult {
|
|
40
|
+
/** The country the point lies in. */
|
|
41
|
+
country: PlaceRef | null;
|
|
42
|
+
/** The region (province, state, ...) the point lies in. */
|
|
43
|
+
region: PlaceRef | null;
|
|
44
|
+
/** The city -- or whatever finest division the country has; see the
|
|
45
|
+
* README -- the point lies in. */
|
|
46
|
+
city: PlaceRef | null;
|
|
47
|
+
}
|
|
48
|
+
/** The three tiers of the place hierarchy (Section 7.1). */
|
|
49
|
+
export type PlaceLevel = "country" | "region" | "city";
|
|
50
|
+
/** A place's global id (Section 7.5) and its name entry (Section 7.4). */
|
|
51
|
+
export interface PlaceRef {
|
|
52
|
+
/** The place's global id (Section 7.5): what `place()`, `placeIds()`
|
|
53
|
+
* and `locate()` take. Stable within one file, not between builds. */
|
|
54
|
+
id: number;
|
|
55
|
+
/**
|
|
56
|
+
* The place's name in its own language and script, as people there
|
|
57
|
+
* write it: "東京都", "Den Haag". Field 0 of its name entry, never
|
|
58
|
+
* empty.
|
|
59
|
+
*/
|
|
60
|
+
localName: string;
|
|
61
|
+
/**
|
|
62
|
+
* The place's international (English) name -- "Tokyo", "The Hague" --
|
|
63
|
+
* or `localName` where it has none, which is also the case for a place
|
|
64
|
+
* whose names are the same. Field 1 of its name entry. Pick this one
|
|
65
|
+
* when a name is shown to everyone regardless of their language, or
|
|
66
|
+
* stored once for all of them.
|
|
67
|
+
*/
|
|
68
|
+
internationalName: string;
|
|
69
|
+
/**
|
|
70
|
+
* Every name, in name-entry order (Section 7.4): `localName`, then the
|
|
71
|
+
* international name (`""` where it has none but alternatives follow),
|
|
72
|
+
* then alternative names and spellings ("'s-Gravenhage"). For search
|
|
73
|
+
* and for showing alternatives; to display one name, use `localName`
|
|
74
|
+
* or `internationalName` rather than indexing into this.
|
|
75
|
+
*/
|
|
76
|
+
names: string[];
|
|
77
|
+
}
|
|
78
|
+
/** A place's level and the global ids of it and its ancestors, shaped
|
|
79
|
+
* like `Place` without the names. */
|
|
80
|
+
export interface PlaceIds {
|
|
81
|
+
/** The place's own level. */
|
|
82
|
+
level: PlaceLevel;
|
|
83
|
+
/** Id of its country (the place itself, for a country). */
|
|
84
|
+
country: number;
|
|
85
|
+
/** Id of its region (the place itself, for a region); null for a
|
|
86
|
+
* country. */
|
|
87
|
+
region: number | null;
|
|
88
|
+
/** The place's own id for a city; null otherwise. */
|
|
89
|
+
city: number | null;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* One entry of the place hierarchy with its ancestors resolved. Shaped
|
|
93
|
+
* like QueryResult's country/region/city trio so the two are easy to
|
|
94
|
+
* relate: a country has `region` and `city` null, a region has `city`
|
|
95
|
+
* null and `region` pointing at itself, a city has all three set.
|
|
96
|
+
*/
|
|
97
|
+
export interface Place extends PlaceRef {
|
|
98
|
+
/** The place's own level. */
|
|
99
|
+
level: PlaceLevel;
|
|
100
|
+
/** Its country (itself, for a country). */
|
|
101
|
+
country: PlaceRef;
|
|
102
|
+
/** Its region (itself, for a region); null for a country. */
|
|
103
|
+
region: PlaceRef | null;
|
|
104
|
+
/** Itself, for a city; null otherwise. */
|
|
105
|
+
city: PlaceRef | null;
|
|
106
|
+
}
|
|
107
|
+
/** Narrows `places()` and `search()`. Filters combine: `{ level: "city",
|
|
108
|
+
* country }` is that country's cities. */
|
|
109
|
+
export interface PlaceFilter {
|
|
110
|
+
/** Only places of this level. */
|
|
111
|
+
level?: PlaceLevel;
|
|
112
|
+
/** Only places inside this country (by id); the country itself included. */
|
|
113
|
+
country?: number;
|
|
114
|
+
/** Only places inside this region (by id); the region itself included. */
|
|
115
|
+
region?: number;
|
|
116
|
+
}
|
|
117
|
+
/** Options for `search()`. */
|
|
118
|
+
export interface SearchOptions extends PlaceFilter {
|
|
119
|
+
/** Stop after this many matches. */
|
|
120
|
+
limit?: number;
|
|
121
|
+
/**
|
|
122
|
+
* Rank matches by relevance instead of id order (Appendix B.5): an
|
|
123
|
+
* exact name match outranks a match on a whole word of the name, which
|
|
124
|
+
* outranks a plain substring match, each tier further split by whether
|
|
125
|
+
* the query's own spelling (not just its folded form) matched; among
|
|
126
|
+
* equal matches, a country outranks a city, which outranks a region. `limit` still applies,
|
|
127
|
+
* but to the top of the ranked list rather than the first N found in
|
|
128
|
+
* id order -- so with `sort` the whole match set has to be scored
|
|
129
|
+
* before anything is yielded, since the best match isn't necessarily
|
|
130
|
+
* the first one found.
|
|
131
|
+
*/
|
|
132
|
+
sort?: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* With `sort`, whether to break ties by area (Appendix B.5.1): among
|
|
135
|
+
* equally good matches, larger places first, so "berlin" puts Berlin,
|
|
136
|
+
* Germany ahead of the many small Berlins in the United States, while a
|
|
137
|
+
* province still follows its own same-named city. Areas come from a
|
|
138
|
+
* locate index (`useLocateIndex()`):
|
|
139
|
+
*
|
|
140
|
+
* - `"auto"` (the default): by area when a locate index is attached,
|
|
141
|
+
* otherwise by Appendix B.5's plain order. Unlike everything else an
|
|
142
|
+
* index does, attaching one therefore changes the order of results
|
|
143
|
+
* (never which places match).
|
|
144
|
+
* - `true`: by area; rejects without a locate index.
|
|
145
|
+
* - `false`: Appendix B.5's plain order, index or not.
|
|
146
|
+
*/
|
|
147
|
+
areaTieBreak?: "auto" | boolean;
|
|
148
|
+
}
|
|
149
|
+
/** One record of a chunk (Section 8.2) with its cell and place resolved. */
|
|
150
|
+
export interface ChunkRecord {
|
|
151
|
+
/** The record's stored Morton code within its chunk (Section 8.1). */
|
|
152
|
+
code: number;
|
|
153
|
+
/** The record's depth in the chunk's quadtree (0 = the whole chunk). */
|
|
154
|
+
depth: number;
|
|
155
|
+
/** The cell the record covers. */
|
|
156
|
+
boundingBox: BoundingBox;
|
|
157
|
+
/** null for a "no place" record (palette index 0). */
|
|
158
|
+
place: Place | null;
|
|
159
|
+
}
|
|
160
|
+
/** How many places of each level a file has. */
|
|
161
|
+
export interface PlaceCounts {
|
|
162
|
+
/** Number of countries; their ids are `0 .. countries - 1`. */
|
|
163
|
+
countries: number;
|
|
164
|
+
/** Number of regions; their ids follow the countries'. */
|
|
165
|
+
regions: number;
|
|
166
|
+
/** Number of cities; their ids follow the regions'. */
|
|
167
|
+
cities: number;
|
|
168
|
+
}
|
|
169
|
+
/** `locate()`'s result: a point inside a place, and the cell it's the
|
|
170
|
+
* centre of. */
|
|
171
|
+
export interface Location {
|
|
172
|
+
/** Latitude of the centre of `boundingBox`. */
|
|
173
|
+
lat: number;
|
|
174
|
+
/** Longitude of the centre of `boundingBox`. */
|
|
175
|
+
lon: number;
|
|
176
|
+
/** The place's representative cell (Section C.3). */
|
|
177
|
+
boundingBox: BoundingBox;
|
|
178
|
+
/**
|
|
179
|
+
* Roughly how large the place is, everything under it included, in
|
|
180
|
+
* square degrees -- from its area byte (Section C.3), so within about
|
|
181
|
+
* 9% either way. Useful for zooming a map to it: a square of this area
|
|
182
|
+
* is `sqrt(area)` degrees on a side, which a Web Mercator map shows
|
|
183
|
+
* across one 256-pixel tile at zoom `log2(360 / sqrt(area))`.
|
|
184
|
+
*/
|
|
185
|
+
area: number;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* A sidecar index for `OpenOptions`: the index itself (an open one stays
|
|
189
|
+
* the caller's to close), its file as a source, or a function that finds
|
|
190
|
+
* it on first use and resolves to undefined when there is none.
|
|
191
|
+
*/
|
|
192
|
+
export type IndexOption<I> = I | SourceInput | (() => Promise<I | SourceInput | undefined>);
|
|
193
|
+
/** Options for `CGeoDataReader.open()`. */
|
|
194
|
+
export interface OpenOptions {
|
|
195
|
+
/**
|
|
196
|
+
* Memory budget, in bytes, for decoded chunks and name blocks; least
|
|
197
|
+
* recently used ones are dropped past it. Default 64 MiB. A worldwide
|
|
198
|
+
* file decodes to about 1 GB in all, its largest chunk to about 3 MB,
|
|
199
|
+
* so a budget much below that makes lookups scattered across the
|
|
200
|
+
* world re-decode often. 0 disables caching, except for the one most
|
|
201
|
+
* recently used chunk.
|
|
202
|
+
*/
|
|
203
|
+
cacheBytes?: number;
|
|
204
|
+
/** How compressed blocks are inflated. Default `streamInflater`, which
|
|
205
|
+
* works everywhere; on Node.js, `nodeInflater` from `./node` is
|
|
206
|
+
* several times faster. */
|
|
207
|
+
inflater?: Inflater;
|
|
208
|
+
/** A search index (Appendix B) for `search()`; see `useSearchIndex()`.
|
|
209
|
+
* Not read until the first search that can use it. */
|
|
210
|
+
searchIndex?: IndexOption<SearchIndex>;
|
|
211
|
+
/** A locate index (Appendix C) for `locate()`; see `useLocateIndex()`.
|
|
212
|
+
* Not read until the first `locate()`. */
|
|
213
|
+
locateIndex?: IndexOption<LocateIndex>;
|
|
214
|
+
}
|
|
215
|
+
/** `OpenOptions.cacheBytes` when not given. */
|
|
216
|
+
export declare const DEFAULT_CACHE_BYTES: number;
|
|
217
|
+
/**
|
|
218
|
+
* A CGEODATA file opened for reading: answers which country, region and
|
|
219
|
+
* city a point lies in (`query`, `lookup`), what a place is called and
|
|
220
|
+
* where it sits in the hierarchy (`place`, `placeIds`), which places
|
|
221
|
+
* match a name (`search`), and where a place is (`locate`).
|
|
222
|
+
*
|
|
223
|
+
* Opening reads only the header, directory and place counts; each lookup
|
|
224
|
+
* reads and decodes the one chunk its point lies in, which then stays
|
|
225
|
+
* cached (`OpenOptions.cacheBytes`).
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```ts
|
|
229
|
+
* // Node.js
|
|
230
|
+
* import { openFile } from "@ferrybig/cgeodata-reader/node";
|
|
231
|
+
* const reader = await openFile("world.bin");
|
|
232
|
+
*
|
|
233
|
+
* // Anywhere else
|
|
234
|
+
* import { CGeoDataReader } from "@ferrybig/cgeodata-reader";
|
|
235
|
+
* const reader = await CGeoDataReader.open(await (await fetch(url)).arrayBuffer());
|
|
236
|
+
*
|
|
237
|
+
* const { city, region, country } = await reader.query(52.0799, 4.3113);
|
|
238
|
+
* city?.internationalName; // "The Hague"
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
export declare class CGeoDataReader {
|
|
242
|
+
/** Decoded chunks (keyed by chunk index) and name blocks (keyed by
|
|
243
|
+
* `-1 - blockIndex`) under one byte budget. */
|
|
244
|
+
private readonly cache;
|
|
245
|
+
/** Loads in flight, under the same keys as `cache`, so concurrent
|
|
246
|
+
* lookups in one chunk decode it once. */
|
|
247
|
+
private readonly loading;
|
|
248
|
+
/** The chunk the last lookup used, held outside `cache`: lookups close
|
|
249
|
+
* together (a tile's samples) mostly land in the same chunk, and this
|
|
250
|
+
* answers those without touching the LRU's bookkeeping. */
|
|
251
|
+
private lastChunkIndex;
|
|
252
|
+
private lastChunk;
|
|
253
|
+
/** Decodes take turns, one per macrotask; see `TurnQueue`. */
|
|
254
|
+
private readonly decodes;
|
|
255
|
+
/** The full-precision cell of the point `gridCell()` last placed; read
|
|
256
|
+
* right after, in the same synchronous run. */
|
|
257
|
+
private gx;
|
|
258
|
+
private gy;
|
|
259
|
+
private readonly search_;
|
|
260
|
+
private readonly locate_;
|
|
261
|
+
/** Rank areas of the regions searches have matched so far (Appendix
|
|
262
|
+
* B.5.1), -1 where not yet known, for the area bytes they came from. */
|
|
263
|
+
private rankAreas;
|
|
264
|
+
private placeTableChecksumPromise;
|
|
265
|
+
private prefixChecksumPromise;
|
|
266
|
+
private readonly source;
|
|
267
|
+
private readonly inflater;
|
|
268
|
+
/** The file's header (Section 5), including its `attribution`. */
|
|
269
|
+
readonly header: CGeoDataHeader;
|
|
270
|
+
/** The file's directory (Section 6): where each chunk is. */
|
|
271
|
+
readonly directory: CGeoDataDirectory;
|
|
272
|
+
private readonly placeTable;
|
|
273
|
+
private constructor();
|
|
274
|
+
/**
|
|
275
|
+
* Opens a CGEODATA file: reads and validates its header, directory and
|
|
276
|
+
* the uncompressed part of its place table (Sections 5-7.2), and
|
|
277
|
+
* nothing else -- chunks and names are read on first use. The reader
|
|
278
|
+
* takes ownership of `input`, if it's a `ByteSource`: `close()` closes
|
|
279
|
+
* it, as does a failed open.
|
|
280
|
+
*/
|
|
281
|
+
static open(input: SourceInput, options?: OpenOptions): Promise<CGeoDataReader>;
|
|
282
|
+
/** Closes the source, and any index this reader opened itself. */
|
|
283
|
+
close(): Promise<void>;
|
|
284
|
+
/** How many countries, regions and cities the file has. */
|
|
285
|
+
get placeCounts(): PlaceCounts;
|
|
286
|
+
/**
|
|
287
|
+
* The country, region and city a point lies in: Section 9's lookup
|
|
288
|
+
* procedure (`lookup()`) with the names resolved (`place()`). Rejects
|
|
289
|
+
* with a `RangeError` for a latitude outside [-90, 90) or a longitude
|
|
290
|
+
* outside [-180, 180).
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```ts
|
|
294
|
+
* const result = await reader.query(35.6762, 139.6503);
|
|
295
|
+
* result.city?.localName; // "杉並区"
|
|
296
|
+
* result.city?.internationalName; // "Suginami"
|
|
297
|
+
* result.country?.internationalName; // "Japan"
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
300
|
+
query(lat: number, lon: number): Promise<QueryResult>;
|
|
301
|
+
/**
|
|
302
|
+
* Section 9's lookup procedure: the place a point lies in, as an id, and
|
|
303
|
+
* the cell the answer holds for. Reads and decodes the point's chunk if
|
|
304
|
+
* it isn't cached; resolve the id with `place()` or `placeIds()`.
|
|
305
|
+
*/
|
|
306
|
+
lookup(lat: number, lon: number): Promise<PlaceIdResult>;
|
|
307
|
+
/**
|
|
308
|
+
* `lookup()` answered from memory only: the same result when the
|
|
309
|
+
* point's chunk is already decoded, undefined when answering would
|
|
310
|
+
* need a read. For callers doing many lookups close together, which
|
|
311
|
+
* mostly hit the same few chunks:
|
|
312
|
+
*
|
|
313
|
+
* ```ts
|
|
314
|
+
* const result = reader.lookupCached(lat, lon) ?? (await reader.lookup(lat, lon));
|
|
315
|
+
* ```
|
|
316
|
+
*
|
|
317
|
+
* waits only for the lookups that actually read something.
|
|
318
|
+
*/
|
|
319
|
+
lookupCached(lat: number, lon: number): PlaceIdResult | undefined;
|
|
320
|
+
/** The index of the chunk a point lies in (Section 6.1). */
|
|
321
|
+
chunkIndexAt(lat: number, lon: number): number;
|
|
322
|
+
/** Validates a point and places it on the full-precision grid
|
|
323
|
+
* (`this.gx`, `this.gy`), returning its chunk's index. */
|
|
324
|
+
private gridCell;
|
|
325
|
+
/** Section 9 steps 5-8 for the point `gridCell()` just placed, in its
|
|
326
|
+
* (decoded, or null for empty) chunk. */
|
|
327
|
+
private resolve;
|
|
328
|
+
/** The names of one place by global id, without resolving its ancestors.
|
|
329
|
+
* Cheaper than `place()` when only the names are needed. */
|
|
330
|
+
placeNames(id: number): Promise<string[]>;
|
|
331
|
+
/** FNV-1a 64-bit hash of the place table bytes, as defined by
|
|
332
|
+
* binary-format.md Section B.6. Used to bind a search index to its source. */
|
|
333
|
+
placeTableChecksum(): Promise<bigint>;
|
|
334
|
+
/** FNV-1a 64-bit hash of everything before the first chunk -- header,
|
|
335
|
+
* directory and place table -- as defined by binary-format.md Section
|
|
336
|
+
* C.4. Binds a locate index to its source: the chunk lengths it covers
|
|
337
|
+
* change whenever the chunks do. */
|
|
338
|
+
prefixChecksum(): Promise<bigint>;
|
|
339
|
+
/**
|
|
340
|
+
* Attaches a text search index (Appendix B, built by `buildSearchIndex`
|
|
341
|
+
* / `cgeodata build-search-index`) so `search()` only checks the places
|
|
342
|
+
* the index says could match instead of every place. Results are
|
|
343
|
+
* identical with or without it -- only the speed differs.
|
|
344
|
+
*
|
|
345
|
+
* Takes the index's file (as for `open()`; read on demand) or an open
|
|
346
|
+
* `SearchIndex` (which the caller keeps ownership of), or undefined to
|
|
347
|
+
* search without one. Replaces `OpenOptions.searchIndex`. Rejects with
|
|
348
|
+
* a `CGeoDataIndexMismatchError` if it was built from a different file.
|
|
349
|
+
*/
|
|
350
|
+
useSearchIndex(index: SearchIndex | SourceInput | undefined): Promise<void>;
|
|
351
|
+
/** Whether `search()` will use an index. Reads (and validates) the one
|
|
352
|
+
* from `OpenOptions.searchIndex` if that hasn't happened yet. */
|
|
353
|
+
hasSearchIndex(): Promise<boolean>;
|
|
354
|
+
/** The attached search index, opening `OpenOptions.searchIndex` on
|
|
355
|
+
* first use. One that's there but unusable (stale, corrupt) rejects on
|
|
356
|
+
* every call rather than silently falling back to a linear scan. */
|
|
357
|
+
private activeSearchIndex;
|
|
358
|
+
private checkSearchIndex;
|
|
359
|
+
/**
|
|
360
|
+
* Attaches a locate index (Appendix C, built by `buildLocateIndex` /
|
|
361
|
+
* `cgeodata build-locate-index`) so `locate()` reads one entry instead
|
|
362
|
+
* of scanning every chunk. Results are identical with or without it --
|
|
363
|
+
* only the speed differs -- except that its areas also let a sorted
|
|
364
|
+
* `search()` rank equal matches by size (`SearchOptions.areaTieBreak`).
|
|
365
|
+
*
|
|
366
|
+
* Takes the index's file (as for `open()`; read on demand) or an open
|
|
367
|
+
* `LocateIndex` (which the caller keeps ownership of), or undefined to
|
|
368
|
+
* locate without one. Replaces `OpenOptions.locateIndex`. Rejects with
|
|
369
|
+
* a `CGeoDataIndexMismatchError` if it was built from a different file.
|
|
370
|
+
*/
|
|
371
|
+
useLocateIndex(index: LocateIndex | SourceInput | undefined): Promise<void>;
|
|
372
|
+
/** Whether `locate()` will use an index. Reads (and validates) the one
|
|
373
|
+
* from `OpenOptions.locateIndex` if that hasn't happened yet. */
|
|
374
|
+
hasLocateIndex(): Promise<boolean>;
|
|
375
|
+
/** The attached locate index, opening `OpenOptions.locateIndex` on
|
|
376
|
+
* first use. One that's there but unusable (stale, corrupt) rejects on
|
|
377
|
+
* every call rather than silently falling back to a chunk scan. */
|
|
378
|
+
private activeLocateIndex;
|
|
379
|
+
private checkLocateIndex;
|
|
380
|
+
/** `useSearchIndex`/`useLocateIndex`, for either slot. */
|
|
381
|
+
private attach;
|
|
382
|
+
/** The slot's index, trying its pending one first. */
|
|
383
|
+
private activate;
|
|
384
|
+
private tryPending;
|
|
385
|
+
/** Resolves one place id (Section 7.5) to its names and ancestors. */
|
|
386
|
+
place(id: number): Promise<Place>;
|
|
387
|
+
/** `place()` when every name it needs is cached, else undefined. */
|
|
388
|
+
private loadedPlace;
|
|
389
|
+
/** `place()` loading each name block in turn, for when the cache is
|
|
390
|
+
* too small to hold them all at once. */
|
|
391
|
+
private placeFrom;
|
|
392
|
+
/** `place()` without the names: the place's level and the global ids
|
|
393
|
+
* of it and its ancestors, from the place table's counts alone, so no
|
|
394
|
+
* name block is read -- and so synchronous. */
|
|
395
|
+
placeIds(id: number): PlaceIds;
|
|
396
|
+
/** How many places sit directly under `id`: a country's regions, a
|
|
397
|
+
* region's cities, always 0 for a city. From the place table's counts
|
|
398
|
+
* alone, like `placeIds()`, so no name block is read and it is
|
|
399
|
+
* synchronous: `childCount(region) === 0` finds a region with no city
|
|
400
|
+
* mapped to it. */
|
|
401
|
+
childCount(id: number): number;
|
|
402
|
+
/**
|
|
403
|
+
* Walks the place hierarchy in id order (countries, then regions, then
|
|
404
|
+
* cities), optionally restricted to one level and/or one ancestor. Each
|
|
405
|
+
* level's ids for a given parent are contiguous (Section 7.5), so a
|
|
406
|
+
* filtered walk only touches the ids it yields.
|
|
407
|
+
*/
|
|
408
|
+
places(filter?: PlaceFilter): AsyncIterableIterator<Place>;
|
|
409
|
+
/**
|
|
410
|
+
* Diacritic- and case-insensitive substring match against every name of
|
|
411
|
+
* every place (all levels unless filtered) -- Section B.5's "matches":
|
|
412
|
+
* the folded form (`foldDiacritics`) of a name contains the folded form
|
|
413
|
+
* of the query. Without a search index this is a plain linear scan over
|
|
414
|
+
* the name blocks; with one (`useSearchIndex()`), only the places the
|
|
415
|
+
* index can't rule out are compared. Same results either way.
|
|
416
|
+
*
|
|
417
|
+
* A text with commas also reads as a place and the places it lies in
|
|
418
|
+
* (Appendix B.13): "Paris, Texas" finds a place named "Paris, Texas"
|
|
419
|
+
* and every "Paris" whose region or country matches "Texas";
|
|
420
|
+
* "Springfield, Illinois, United States" narrows by both. Each part
|
|
421
|
+
* after a comma has to match a different ancestor, nearest first.
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* ```ts
|
|
425
|
+
* for await (const place of reader.search("munchen", { sort: true, limit: 20 }))
|
|
426
|
+
* console.log(place.internationalName, place.country.internationalName);
|
|
427
|
+
* // Munich Germany
|
|
428
|
+
* // Grafing bei München Germany
|
|
429
|
+
* // ...
|
|
430
|
+
* ```
|
|
431
|
+
*/
|
|
432
|
+
search(text: string, options?: SearchOptions): AsyncIterableIterator<Place>;
|
|
433
|
+
/** The `sort: true` path of `search()`: scores every match in relevance
|
|
434
|
+
* order (Appendix B.5, or B.5.1 when ranking by area; `relevance.ts`),
|
|
435
|
+
* then yields the best first. `limit` is applied after sorting, not
|
|
436
|
+
* during the scan -- the top match can turn up anywhere in id order. */
|
|
437
|
+
private searchRanked;
|
|
438
|
+
/** `text`'s readings (Appendix B.13) as needles, and the places a search
|
|
439
|
+
* index says could match any of them (ascending), or undefined when
|
|
440
|
+
* there's no index or it can't narrow one of the readings down. */
|
|
441
|
+
private prepareQuery;
|
|
442
|
+
/** Whether place `id` matches any reading of `query`. With `compare`,
|
|
443
|
+
* instead the best-ranked way it does (by that order), or undefined;
|
|
444
|
+
* `rankArea` is left at 0 for the caller to fill in. */
|
|
445
|
+
private matchQuery;
|
|
446
|
+
/** The names of place `id`'s ancestors, nearest first: a city's region
|
|
447
|
+
* and country, a region's country, nothing for a country. */
|
|
448
|
+
private ancestorNames;
|
|
449
|
+
/** `areaTieBreak` resolved to the area bytes to rank by, or undefined
|
|
450
|
+
* for Appendix B.5's plain order. */
|
|
451
|
+
private searchAreas;
|
|
452
|
+
/**
|
|
453
|
+
* The area byte (binary-format.md Section C.3) of every place, by id --
|
|
454
|
+
* the size of the place and everything under it, on a log scale, a
|
|
455
|
+
* quarter of a doubling per step -- read from the attached locate index
|
|
456
|
+
* (`useLocateIndex()`), or undefined without one. What
|
|
457
|
+
* `search({ sort: true })` breaks ties with (`SearchOptions.areaTieBreak`).
|
|
458
|
+
* Read once and then kept; the array is shared, so don't modify it.
|
|
459
|
+
*/
|
|
460
|
+
placeAreas(): Promise<Uint8Array | undefined>;
|
|
461
|
+
/** Place `id`'s rank area (Appendix B.5.1, `regionRankArea`): its area
|
|
462
|
+
* byte, except for a region with a city of the same local name. That
|
|
463
|
+
* takes reading the region's city names, so it's worked out only for
|
|
464
|
+
* the regions a search actually matched, once each. */
|
|
465
|
+
private rankArea;
|
|
466
|
+
/** The ids (ascending) of `level` under the filter that `search()` has
|
|
467
|
+
* to check: the whole `idRange`, or, when a search index narrowed the
|
|
468
|
+
* query to `candidates` (ascending), just those within it. */
|
|
469
|
+
private searchScope;
|
|
470
|
+
/** The parsed records of one chunk (Section 8), or undefined when the
|
|
471
|
+
* chunk is empty. Cached like lookups, unless `cache` is false: a
|
|
472
|
+
* one-pass walk over every chunk should skip the cache, so it doesn't
|
|
473
|
+
* evict everything lookups had cached for chunks it only visits once. */
|
|
474
|
+
chunk(index: number, { cache }?: {
|
|
475
|
+
cache?: boolean;
|
|
476
|
+
}): Promise<ChunkRecords | undefined>;
|
|
477
|
+
/** The lat/lon extent of a chunk (Section 6.1). */
|
|
478
|
+
chunkBoundingBox(index: number): BoundingBox;
|
|
479
|
+
/**
|
|
480
|
+
* A representative lat/lon for a place id: the centre of the place's
|
|
481
|
+
* representative cell (binary-format.md Section C.3), a cell near the
|
|
482
|
+
* middle of the place's area -- its own cells plus those of every place
|
|
483
|
+
* under it -- that is guaranteed to lie in that area, so `query()` at
|
|
484
|
+
* the returned point resolves to the place or one of its descendants.
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* ```ts
|
|
488
|
+
* const position = await reader.locate(place.id);
|
|
489
|
+
* if (position) map.setView([position.lat, position.lon]);
|
|
490
|
+
* ```
|
|
491
|
+
*
|
|
492
|
+
* Null if neither the place nor anything under it has any cells -- and
|
|
493
|
+
* that happens to real places, not only to broken files: a place
|
|
494
|
+
* smaller than the file's finest cell (a tiny island, an enclave, a
|
|
495
|
+
* boundary sliver) loses every cell to its neighbours. On a worldwide
|
|
496
|
+
* build that's about 0.15% of places, every one of them still found
|
|
497
|
+
* by `search()`; drop them from results that need a position rather
|
|
498
|
+
* than defaulting to a coordinate.
|
|
499
|
+
*
|
|
500
|
+
* With a locate index (`useLocateIndex()`) this reads one entry;
|
|
501
|
+
* without one it scans every chunk. Same result either way.
|
|
502
|
+
*/
|
|
503
|
+
locate(id: number): Promise<Location | null>;
|
|
504
|
+
/**
|
|
505
|
+
* The ids of place `id` and of every place under it, as half-open
|
|
506
|
+
* ranges: `[id, id + 1]` first, then for a country its regions and
|
|
507
|
+
* their cities, for a region its cities. Each level's children are
|
|
508
|
+
* contiguous (Section 7.1), so this is at most three ranges.
|
|
509
|
+
*/
|
|
510
|
+
subtreeIdRanges(id: number): Array<[number, number]>;
|
|
511
|
+
/** One chunk's records with their cells and places resolved -- what a
|
|
512
|
+
* lookup in that chunk can answer with, laid out for inspection. */
|
|
513
|
+
chunkRecords(index: number): AsyncIterableIterator<ChunkRecord>;
|
|
514
|
+
private recordBoundingBox;
|
|
515
|
+
/** The half-open global id range of `level` under the filter's
|
|
516
|
+
* narrowest ancestor, via the Section 7.5 prefix arrays. */
|
|
517
|
+
private idRange;
|
|
518
|
+
private checkPlaceId;
|
|
519
|
+
/** A chunk without reading anything: its records when decoded and
|
|
520
|
+
* cached, null when it's empty, undefined when it would need a read. */
|
|
521
|
+
private loadedChunk;
|
|
522
|
+
/** Reads and decodes a chunk, sharing one decode among concurrent
|
|
523
|
+
* callers. */
|
|
524
|
+
private loadChunk;
|
|
525
|
+
private decodeChunk;
|
|
526
|
+
private checkPalette;
|
|
527
|
+
/** A place's names when its name block is cached, else undefined. */
|
|
528
|
+
private loadedNames;
|
|
529
|
+
/** Reads and decodes a place's name block (sharing one read among
|
|
530
|
+
* concurrent callers), and returns its names. */
|
|
531
|
+
private loadNames;
|
|
532
|
+
private get nameBlockSize();
|
|
533
|
+
private entryOf;
|
|
534
|
+
}
|