@ferrybig/cgeodata-reader 0.0.0-stage → 0.1.1
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 +2620 -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 +35 -0
- package/dist/chunk.js +277 -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 +22 -0
- package/dist/format.js +23 -0
- package/dist/header.d.ts +30 -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/rangeChunk.d.ts +46 -0
- package/dist/rangeChunk.js +401 -0
- package/dist/rangeDecoder.d.ts +17 -0
- package/dist/rangeDecoder.js +129 -0
- package/dist/rangeDecoderWasm.d.ts +2 -0
- package/dist/rangeDecoderWasm.js +4 -0
- package/dist/reader.d.ts +534 -0
- package/dist/reader.js +1132 -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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fernando van Loenhout
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,314 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ferrybig/cgeodata-reader
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Turns a latitude/longitude into the country, region and city it lies in,
|
|
4
|
+
using real administrative boundaries from OpenStreetMap rather than "the
|
|
5
|
+
nearest labelled point", which goes wrong near borders. Lookups run
|
|
6
|
+
in-process against a single CGEODATA file (a worldwide one is about
|
|
7
|
+
45 MiB), reading and decoding only the small part of the file each lookup
|
|
8
|
+
needs.
|
|
9
|
+
|
|
10
|
+
- No runtime dependencies. Works in Node.js (24 or newer) and in browsers.
|
|
11
|
+
Also reads files of the format's previous version, 0, whose chunks
|
|
12
|
+
decode with WebAssembly SIMD where the platform allows it, and in
|
|
13
|
+
plain JavaScript (about twice as slow) where it doesn't, such as a
|
|
14
|
+
page whose Content Security Policy lacks `'wasm-unsafe-eval'`.
|
|
15
|
+
- Async throughout: reads never block, and the event loop is never held
|
|
16
|
+
for more than one chunk's decode.
|
|
17
|
+
- Includes place search (diacritic-insensitive, all alternative names)
|
|
18
|
+
and a representative coordinate for every place.
|
|
19
|
+
|
|
20
|
+
The file format is specified in [binary-format.md](binary-format.md),
|
|
21
|
+
which ships with this package.
|
|
22
|
+
|
|
23
|
+
## Quick start (Node.js)
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { openFile } from "@ferrybig/cgeodata-reader/node";
|
|
27
|
+
|
|
28
|
+
const reader = await openFile("world.bin");
|
|
29
|
+
|
|
30
|
+
const result = await reader.query(35.6762, 139.6503);
|
|
31
|
+
result.country?.internationalName; // "Japan"
|
|
32
|
+
result.region?.localName; // "東京都"
|
|
33
|
+
result.region?.internationalName; // "Tokyo"
|
|
34
|
+
result.city?.localName; // "杉並区"
|
|
35
|
+
result.city?.internationalName; // "Suginami"
|
|
36
|
+
|
|
37
|
+
await reader.close();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`query()` returns `null` for a level that doesn't apply: `city` for a point
|
|
41
|
+
that only resolves to a region, all three in the open ocean. Its
|
|
42
|
+
`boundingBox` is the area the answer holds for. Any point inside it gives
|
|
43
|
+
the same result.
|
|
44
|
+
|
|
45
|
+
## In a browser
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { CGeoDataReader } from "@ferrybig/cgeodata-reader";
|
|
49
|
+
|
|
50
|
+
// The whole file in memory...
|
|
51
|
+
const bytes = await (await fetch("/data/world.bin")).arrayBuffer();
|
|
52
|
+
const reader = await CGeoDataReader.open(bytes);
|
|
53
|
+
|
|
54
|
+
// ...or a File the user picked, read range by range as lookups need it.
|
|
55
|
+
const fromDisk = await CGeoDataReader.open(fileInput.files[0]);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`CGeoDataReader.open()` accepts an `ArrayBuffer`, a `Uint8Array`, a `Blob`
|
|
59
|
+
or `File`, or any `ByteSource` (an object with a `size` and an async
|
|
60
|
+
`read(offset, length)`). With a `ByteSource` you can serve ranges from
|
|
61
|
+
anywhere, for example HTTP range requests. Compressed blocks are inflated
|
|
62
|
+
with the platform's `DecompressionStream`.
|
|
63
|
+
|
|
64
|
+
## Many lookups close together
|
|
65
|
+
|
|
66
|
+
Each lookup decodes the chunk of the file its point lies in, then keeps
|
|
67
|
+
it cached. Points close together mostly share a chunk, so most lookups
|
|
68
|
+
can answer from memory. `lookupCached()` does exactly that, synchronously,
|
|
69
|
+
and returns `undefined` when answering would need a read:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
for (const [lat, lon] of points) {
|
|
73
|
+
const { placeId, boundingBox } =
|
|
74
|
+
reader.lookupCached(lat, lon) ?? (await reader.lookup(lat, lon));
|
|
75
|
+
if (placeId === null) continue; // nothing here
|
|
76
|
+
const { level, country, region, city } = reader.placeIds(placeId); // sync, no names
|
|
77
|
+
// ...
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`lookup()` is `query()` without the names. It returns the place's id,
|
|
82
|
+
which `place(id)` resolves to names and `placeIds(id)` resolves
|
|
83
|
+
synchronously to the ids of the place and its ancestors. Two results with
|
|
84
|
+
the same `placeId` are the same place, so per-place work (a colour, a
|
|
85
|
+
label) can be keyed on it. A map tile renderer combines all of this: it
|
|
86
|
+
uses `boundingBox` to fill every pixel a result covers, and looks up names
|
|
87
|
+
once per place rather than once per pixel.
|
|
88
|
+
|
|
89
|
+
## Names: `localName` and `internationalName`
|
|
90
|
+
|
|
91
|
+
Every place has a name in its own language and script, and usually an
|
|
92
|
+
international (English) one:
|
|
93
|
+
|
|
94
|
+
| | `localName` | `internationalName` | `names` |
|
|
95
|
+
|---|---|---|---|
|
|
96
|
+
| Tokyo | 東京都 | Tokyo | `["東京都", "Tokyo"]` |
|
|
97
|
+
| The Hague | Den Haag | The Hague | `["Den Haag", "The Hague"]` |
|
|
98
|
+
| Amsterdam | Amsterdam | Amsterdam | `["Amsterdam"]` |
|
|
99
|
+
|
|
100
|
+
`internationalName` falls back to `localName` when there's no separate
|
|
101
|
+
international name. When a name is stored once and shown to everyone,
|
|
102
|
+
whatever their language, use `internationalName`. **Don't** index into
|
|
103
|
+
`names`: `names[0]` is the local name, so it gives non-Latin script for
|
|
104
|
+
every place in Japan, Korea, China, Russia and many other countries.
|
|
105
|
+
`names` lists every name, alternative spellings included. It's what
|
|
106
|
+
`search()` matches against.
|
|
107
|
+
|
|
108
|
+
## Search, and a coordinate per place
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
for await (const place of reader.search("munchen", { sort: true, limit: 20 })) {
|
|
112
|
+
place.internationalName; // "Munich"
|
|
113
|
+
place.country.internationalName; // "Germany"
|
|
114
|
+
const position = await reader.locate(place.id); // { lat, lon, boundingBox, area } or null
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Search is case- and diacritic-insensitive ("munchen" finds München). It
|
|
119
|
+
matches any part of any name, alternative names included. With
|
|
120
|
+
`sort: true`, an exact name beats a whole-word match, which beats a plain
|
|
121
|
+
substring match, and among equal matches a country comes first. Filter
|
|
122
|
+
with `level`, `country` or `region` (place ids).
|
|
123
|
+
|
|
124
|
+
**Equal matches are ranked by area when a locate index is attached**
|
|
125
|
+
(`areaTieBreak: "auto"`, the default): "berlin" puts Berlin, Germany
|
|
126
|
+
ahead of the towns called Berlin in the United States, and a province
|
|
127
|
+
still comes right after its own same-named city. Without a locate index
|
|
128
|
+
they fall back to city before region, then id order, which is close to
|
|
129
|
+
arbitrary. So attaching a locate index changes the *order* of sorted
|
|
130
|
+
results, never which places match. Pass `areaTieBreak: true` to insist on
|
|
131
|
+
area order (it rejects without a locate index) or `false` to opt out.
|
|
132
|
+
Area is a size signal, not fame: a large rural Portland parish in Jamaica
|
|
133
|
+
still outranks Portland, Oregon.
|
|
134
|
+
|
|
135
|
+
A comma narrows by the places a match lies in: `"Paris, Texas"`,
|
|
136
|
+
`"Springfield, Illinois, United States"`. Each part after a comma must
|
|
137
|
+
match a different, further-up ancestor (region, then country). The whole
|
|
138
|
+
text is still matched as a name too, for the places whose names contain
|
|
139
|
+
a comma.
|
|
140
|
+
|
|
141
|
+
`locate(id)` returns a point inside the place: `query()` at that point
|
|
142
|
+
resolves to the place or something inside it. It **can return `null` for
|
|
143
|
+
a real place that `search()` finds**: a place smaller than the file's
|
|
144
|
+
finest cell (a tiny island, an enclave, a boundary sliver) has no cells
|
|
145
|
+
of its own. That's about 0.15% of a worldwide file. Leave those out of
|
|
146
|
+
results that need a position. Don't default them to a coordinate.
|
|
147
|
+
|
|
148
|
+
`area` is the place's approximate size in square degrees (within about
|
|
149
|
+
9%), everything under it included. To zoom a Web Mercator map to it,
|
|
150
|
+
`Math.log2(360 / Math.sqrt(area))` is the zoom at which a square of that
|
|
151
|
+
area spans one 256-pixel tile; subtract a little to leave a margin.
|
|
152
|
+
|
|
153
|
+
`search()` and `places()` are async iterators. Collect them with
|
|
154
|
+
`for await`, or with `Array.fromAsync()` where your runtime has it.
|
|
155
|
+
|
|
156
|
+
## Ship the index files too
|
|
157
|
+
|
|
158
|
+
Two sidecar files make search and locate fast. On a server they're
|
|
159
|
+
effectively **required**:
|
|
160
|
+
|
|
161
|
+
| File | Without it |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `world.search-index.bin` | every `search()` scans every name: ~430 ms on a worldwide file |
|
|
164
|
+
| `world.locate-index.bin` | every `locate()` decodes the whole file: ~1.2 s on a worldwide file |
|
|
165
|
+
|
|
166
|
+
`openFile("world.bin")` picks up `world.search-index.bin` and
|
|
167
|
+
`world.locate-index.bin` next to it, the first time a `search()` or
|
|
168
|
+
`locate()` can use them. An index that doesn't belong to the file (built
|
|
169
|
+
from an older build of it, say) makes those calls reject with a
|
|
170
|
+
`CGeoDataIndexMismatchError` naming the index file. The reader never
|
|
171
|
+
falls back to a slow scan silently, so rebuild the index or delete it.
|
|
172
|
+
Elsewhere, pass the indexes yourself:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
const reader = await CGeoDataReader.open(bytes, {
|
|
176
|
+
searchIndex: await (await fetch("/data/world.search-index.bin")).arrayBuffer(),
|
|
177
|
+
locateIndex: await (await fetch("/data/world.locate-index.bin")).arrayBuffer(),
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`cgeodata build-locate-index world.bin --output world.locate-index.bin`
|
|
182
|
+
and `cgeodata build-search-index ...` build them (see [CLI](#cli)).
|
|
183
|
+
**Build the locate index first.** The search index stores the top
|
|
184
|
+
results of 1-2 character searches ranked the same way the reader will
|
|
185
|
+
rank them. With a locate index next to the `.bin`, it ranks them by area,
|
|
186
|
+
matching the reader's default. Built without one, sorted 1-2 character
|
|
187
|
+
searches can't use it once a locate index is present. They still return
|
|
188
|
+
the same results, just more slowly (~0.6 s on a worldwide file).
|
|
189
|
+
|
|
190
|
+
Only the `.bin` needs distributing: both indexes are a pure function of
|
|
191
|
+
it and can be built where it's used.
|
|
192
|
+
|
|
193
|
+
## Memory and the event loop
|
|
194
|
+
|
|
195
|
+
- **Cache**: decoded chunks and names are kept in a least-recently-used
|
|
196
|
+
cache, by default up to 64 MiB (`open(input, { cacheBytes })`). A
|
|
197
|
+
worldwide file decodes to about 690 MB in all and its largest chunk to
|
|
198
|
+
about 1.9 MB. Lookups in a few regions fit easily. Lookups scattered over
|
|
199
|
+
a whole continent re-decode chunks unless you give the cache more room.
|
|
200
|
+
`0` turns caching off.
|
|
201
|
+
- **Event loop**: every read is asynchronous, and decodes take turns, one
|
|
202
|
+
per macrotask, even when many lookups run at once. The longest the
|
|
203
|
+
reader holds the event loop is one chunk's decode: under 1 ms for most
|
|
204
|
+
chunks, about 3 ms for the largest chunk of a worldwide file (8 ms
|
|
205
|
+
for a version 0 file without WebAssembly SIMD).
|
|
206
|
+
- **Decoding**: the WebAssembly decoder (version 0 files only) keeps one
|
|
207
|
+
block of memory, as big as the largest chunk it has decoded (about
|
|
208
|
+
1.8 MB for a worldwide file), while decodes follow each other, and
|
|
209
|
+
frees it once none has run for a second. An idle reader holds only its cache.
|
|
210
|
+
- **Synchronous methods** answer from memory only: `lookupCached()`,
|
|
211
|
+
`placeIds()`, `chunkIndexAt()`, `subtreeIdRanges()`, `placeCounts`,
|
|
212
|
+
`header`.
|
|
213
|
+
- **Closing**: `await reader.close()` closes the file. A reader that's
|
|
214
|
+
garbage collected without being closed has its file closed then, rather
|
|
215
|
+
than leaking it.
|
|
216
|
+
|
|
217
|
+
## Errors
|
|
218
|
+
|
|
219
|
+
Problems with the data reject with one of these, all exported from the
|
|
220
|
+
package root:
|
|
221
|
+
|
|
222
|
+
| Class | Meaning |
|
|
223
|
+
|---|---|
|
|
224
|
+
| `CGeoDataFormatError` | the `.bin` or an index file is malformed or truncated. Download it again. |
|
|
225
|
+
| `CGeoDataVersionError` | (a `CGeoDataFormatError`) the file uses a format version this reader doesn't know (`error.version`). Upgrade the reader. |
|
|
226
|
+
| `CGeoDataUnsupportedError` | (a `CGeoDataFormatError`) a valid file this reader won't read: `max_depth` past 16. Rebuild it with a bigger chunk grid. |
|
|
227
|
+
| `CGeoDataIndexMismatchError` | a valid index, but built from a different `.bin`. Rebuild or delete it. |
|
|
228
|
+
| `CGeoDataError` | base class of all of the above; also a read that returned fewer bytes than the file's size promised. |
|
|
229
|
+
|
|
230
|
+
Invalid arguments (a latitude outside [-90, 90), an id the file doesn't
|
|
231
|
+
have) are plain `RangeError`s.
|
|
232
|
+
|
|
233
|
+
## What the data does and doesn't contain
|
|
234
|
+
|
|
235
|
+
Check these before migrating from another geocoder:
|
|
236
|
+
|
|
237
|
+
- **Three tiers: country, region, city.** There is no second
|
|
238
|
+
administrative level (GeoNames' `admin2`). A schema with a field for it
|
|
239
|
+
needs changing, not filling.
|
|
240
|
+
- **What "city" means varies by country.** The reader returns whichever
|
|
241
|
+
OpenStreetMap level covers that country best: municipalities in the
|
|
242
|
+
Netherlands, counties in most of the US, communes in France, localities
|
|
243
|
+
in Australia. See `countries.md` in the project root for each country.
|
|
244
|
+
- **Country names come from Natural Earth, in English.** `localName` and
|
|
245
|
+
`internationalName` are the same for countries. Some spellings differ
|
|
246
|
+
from GeoNames: "United States of America" (GeoNames: "United States"),
|
|
247
|
+
"North Macedonia" ("Macedonia"), "Macao" ("Macau"). Data you stored
|
|
248
|
+
earlier under the GeoNames spelling won't match new lookups until it's
|
|
249
|
+
looked up again.
|
|
250
|
+
- **Search ranks by size, not by importance.** With a locate index,
|
|
251
|
+
equally good matches are ordered by area ("berlin" puts the German
|
|
252
|
+
capital first); without one, the order among them is close to
|
|
253
|
+
arbitrary. Area isn't fame, though: "portland" still puts a Jamaican
|
|
254
|
+
parish before Portland, Oregon. Show enough results, and show each
|
|
255
|
+
one's region and country.
|
|
256
|
+
- **Attribution**: the data is derived from OpenStreetMap (ODbL), GeoNames
|
|
257
|
+
and Natural Earth. `reader.header.attribution` holds the notice the file
|
|
258
|
+
was built with. Show it wherever you show the results.
|
|
259
|
+
|
|
260
|
+
## Entry points
|
|
261
|
+
|
|
262
|
+
| Import | Contents |
|
|
263
|
+
|---|---|
|
|
264
|
+
| `@ferrybig/cgeodata-reader` | `CGeoDataReader`, its result types, the error classes, `BytesSource` / `BlobSource` / `ByteSource`, `streamInflater`. Works everywhere. |
|
|
265
|
+
| `@ferrybig/cgeodata-reader/node` | `openFile()`, `FileSource`, `nodeInflater` (zlib, faster than `DecompressionStream` on Node.js), `searchIndexPathFor()` / `locateIndexPathFor()`. |
|
|
266
|
+
| `@ferrybig/cgeodata-reader/indexes` | `SearchIndex` / `LocateIndex`, to open an index once and share it between readers, and `buildSearchIndex()` / `buildLocateIndex()`. |
|
|
267
|
+
| `@ferrybig/cgeodata-reader/format` | The format's low-level codecs and parsers, for tools that write or inspect files. These follow the spec, not a stable API. |
|
|
268
|
+
|
|
269
|
+
The package is ESM, with `.d.ts` types included. `dist/` is prebuilt, so
|
|
270
|
+
you don't compile this package's sources.
|
|
271
|
+
|
|
272
|
+
## CLI
|
|
273
|
+
|
|
274
|
+
The package installs `cgeodata` (also as `cgeodata-reader`), a command-line
|
|
275
|
+
front end to the same API. It needs a data file; the demo serves the
|
|
276
|
+
worldwide one, plus the two sidecar indexes that make `search` and
|
|
277
|
+
`--locate` fast (they are picked up automatically when they sit next to the
|
|
278
|
+
`.bin`):
|
|
279
|
+
|
|
280
|
+
```sh
|
|
281
|
+
wget https://geodata.ferrybig.nl/download/world.bin \
|
|
282
|
+
https://geodata.ferrybig.nl/download/world.search-index.bin \
|
|
283
|
+
https://geodata.ferrybig.nl/download/world.locate-index.bin
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Then run it without installing anything:
|
|
287
|
+
|
|
288
|
+
```sh
|
|
289
|
+
npx @ferrybig/cgeodata-reader lookup world.bin 52.37,4.9
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
or, installed, as `cgeodata`:
|
|
293
|
+
|
|
294
|
+
```sh
|
|
295
|
+
cgeodata lookup world.bin 52.37,4.9 # or points on stdin, one per line
|
|
296
|
+
cgeodata search world.bin amsterdam --sort --locate
|
|
297
|
+
cgeodata show world.bin 192858 # a place id from any other command
|
|
298
|
+
cgeodata info world.bin # header, counts, sizes
|
|
299
|
+
cgeodata build-locate-index world.bin --output world.locate-index.bin
|
|
300
|
+
cgeodata build-search-index world.bin --output world.search-index.bin # after the locate index
|
|
301
|
+
cgeodata help [<command>]
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## API reference
|
|
305
|
+
|
|
306
|
+
Every exported symbol has a doc comment, and your editor shows them.
|
|
307
|
+
`npm run docs` in this package's source generates an HTML reference
|
|
308
|
+
(TypeDoc) into `docs/`. The file format, including the exact lookup
|
|
309
|
+
procedure, is in [binary-format.md](binary-format.md).
|
|
310
|
+
|
|
311
|
+
## License
|
|
312
|
+
|
|
313
|
+
MIT for the code. A data file carries the license and attribution of the
|
|
314
|
+
data it was built from (see above).
|