@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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +313 -2
  3. package/binary-format.md +2366 -0
  4. package/dist/cache.d.ts +24 -0
  5. package/dist/cache.js +60 -0
  6. package/dist/cellBounds.d.ts +19 -0
  7. package/dist/cellBounds.js +36 -0
  8. package/dist/chunk.d.ts +34 -0
  9. package/dist/chunk.js +269 -0
  10. package/dist/chunkDecoder.d.ts +39 -0
  11. package/dist/chunkDecoder.js +116 -0
  12. package/dist/chunkDecoderWasm.d.ts +2 -0
  13. package/dist/chunkDecoderWasm.js +4 -0
  14. package/dist/cli/buildLocateIndex.d.ts +2 -0
  15. package/dist/cli/buildLocateIndex.js +46 -0
  16. package/dist/cli/buildSearchIndex.d.ts +2 -0
  17. package/dist/cli/buildSearchIndex.js +90 -0
  18. package/dist/cli/chunk.d.ts +2 -0
  19. package/dist/cli/chunk.js +67 -0
  20. package/dist/cli/info.d.ts +2 -0
  21. package/dist/cli/info.js +87 -0
  22. package/dist/cli/list.d.ts +2 -0
  23. package/dist/cli/list.js +71 -0
  24. package/dist/cli/locate.d.ts +2 -0
  25. package/dist/cli/locate.js +64 -0
  26. package/dist/cli/lookup.d.ts +2 -0
  27. package/dist/cli/lookup.js +128 -0
  28. package/dist/cli/search.d.ts +2 -0
  29. package/dist/cli/search.js +119 -0
  30. package/dist/cli/shared.d.ts +50 -0
  31. package/dist/cli/shared.js +140 -0
  32. package/dist/cli/show.d.ts +2 -0
  33. package/dist/cli/show.js +38 -0
  34. package/dist/cli.d.ts +2 -0
  35. package/dist/cli.js +107 -0
  36. package/dist/container.d.ts +17 -0
  37. package/dist/container.js +68 -0
  38. package/dist/directory.d.ts +21 -0
  39. package/dist/directory.js +31 -0
  40. package/dist/errors.d.ts +52 -0
  41. package/dist/errors.js +56 -0
  42. package/dist/format.d.ts +21 -0
  43. package/dist/format.js +22 -0
  44. package/dist/header.d.ts +29 -0
  45. package/dist/header.js +69 -0
  46. package/dist/index.d.ts +20 -0
  47. package/dist/index.js +18 -0
  48. package/dist/indexes.d.ts +11 -0
  49. package/dist/indexes.js +12 -0
  50. package/dist/inflate.d.ts +21 -0
  51. package/dist/inflate.js +61 -0
  52. package/dist/locateIndex.d.ts +142 -0
  53. package/dist/locateIndex.js +561 -0
  54. package/dist/morton.d.ts +28 -0
  55. package/dist/morton.js +51 -0
  56. package/dist/node.d.ts +65 -0
  57. package/dist/node.js +137 -0
  58. package/dist/placeTable.d.ts +69 -0
  59. package/dist/placeTable.js +177 -0
  60. package/dist/reader.d.ts +534 -0
  61. package/dist/reader.js +1128 -0
  62. package/dist/relevance.d.ts +184 -0
  63. package/dist/relevance.js +266 -0
  64. package/dist/searchIndex.d.ts +148 -0
  65. package/dist/searchIndex.js +786 -0
  66. package/dist/source.d.ts +49 -0
  67. package/dist/source.js +50 -0
  68. package/dist/turns.d.ts +19 -0
  69. package/dist/turns.js +40 -0
  70. package/dist/varint.d.ts +52 -0
  71. package/dist/varint.js +137 -0
  72. package/package.json +76 -5
@@ -0,0 +1,2366 @@
1
+ # CGEODATA: A Compact Binary Format for Reverse Geocoding
2
+
3
+ Version 1 (draft)
4
+
5
+ ## Abstract
6
+
7
+ This document specifies CGEODATA, a binary file format that maps a WGS84
8
+ coordinate to the country, region, and city that contain it, and that
9
+ carries a searchable list of those places. The format stores a quadtree
10
+ partition of the world as its leaf cells in Morton order, split into
11
+ independently compressed chunks so that a lookup reads only a small part of the file.
12
+ The place list is stored as delta-coded hierarchy counts plus
13
+ keyframe-compressed name blocks, so that names can be recovered by index
14
+ without decompressing the whole list.
15
+
16
+ ## Table of Contents
17
+
18
+ 1. [Introduction](#1-introduction)
19
+ 2. [Conventions and Terminology](#2-conventions-and-terminology)
20
+ 3. [Primitive Types](#3-primitive-types)
21
+ 4. [File Layout](#4-file-layout)
22
+ 5. [Header](#5-header)
23
+ 6. [Directory](#6-directory)
24
+ 7. [Place Table](#7-place-table)
25
+ 8. [Chunks](#8-chunks)
26
+ 9. [Lookup Procedure](#9-lookup-procedure)
27
+ 10. [Encoder Considerations](#10-encoder-considerations)
28
+ 11. [Robustness and Security Considerations](#11-robustness-and-security-considerations)
29
+ 12. [Open Issues](#12-open-issues)
30
+ - [Appendix A. Worked Example](#appendix-a-worked-example)
31
+ - [Appendix B. Text Search Index](#appendix-b-text-search-index)
32
+ - [Appendix C. Place Location Index](#appendix-c-place-location-index)
33
+ - [Appendix D. References](#appendix-d-references)
34
+
35
+ ## 1. Introduction
36
+
37
+ ### 1.1. Motivation
38
+
39
+ Reverse geocoding for photo management (see the project's [README](https://github.com/ferrybig/loc-to-city#readme))
40
+ is commonly implemented by storing one labelled point per place and
41
+ returning the nearest label. Cities are not points, and near any border
42
+ the nearest label is frequently wrong. Storing full polygons is accurate
43
+ but large and requires polygon arithmetic at query time.
44
+
45
+ CGEODATA stores the result of point-in-polygon classification instead of
46
+ the polygons: the world is recursively subdivided into cells until each
47
+ cell is attributed to a single place, and the resulting leaf cells are
48
+ stored in Morton order. A lookup is then one binary search.
49
+
50
+ ### 1.2. Design Goals
51
+
52
+ The format is designed with the following goals, in rough priority order:
53
+
54
+ 1. **Small on disk.** The place names and the spatial index together
55
+ should be far smaller than a worldwide nearest-point table.
56
+ 2. **Partial reads.** A reader MUST be able to answer a lookup by reading
57
+ and decompressing only a small, bounded part of the file, without
58
+ loading the entire file into memory. The file is expected to be large
59
+ at worldwide scale.
60
+ 3. **Fast lookups** after a modest one-time load of the directory.
61
+ 4. **Adequate accuracy for geotagging.** The target cell resolution is on
62
+ the order of a few hundred metres; the format itself supports finer
63
+ resolution at the cost of size.
64
+ 5. **Searchable place names.** The place list supports listing and text
65
+ search over countries, regions, and cities, and resolving a place to
66
+ its parents. An optional sidecar index
67
+ ([Appendix B](#appendix-b-text-search-index)) speeds up text search
68
+ without changing the file, and another
69
+ ([Appendix C](#appendix-c-place-location-index)) gives each place a
70
+ representative coordinate.
71
+ 6. **Implementable with no dependencies in JavaScript** (Node.js and
72
+ browsers) as well as other languages: only zlib, integer arithmetic,
73
+ and binary search are required.
74
+
75
+ ### 1.3. Non-Goals
76
+
77
+ - The format does not store polygon geometry and cannot answer
78
+ distance-to-border or area queries.
79
+ - The file itself does not store a representative coordinate per place.
80
+ One is defined by the chunks ([Section C.3](#c3-representative-cell))
81
+ and can be precomputed into an optional sidecar file
82
+ ([Appendix C](#appendix-c-place-location-index)).
83
+ - The format does not attempt exact answers at borders; cells that remain
84
+ ambiguous at the maximum depth are attributed to one place by the
85
+ encoder.
86
+
87
+ ## 2. Conventions and Terminology
88
+
89
+ ### 2.1. Requirements Language
90
+
91
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
92
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
93
+ "OPTIONAL" in this document are to be interpreted as described in BCP 14
94
+ [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as
95
+ shown here.
96
+
97
+ ### 2.2. Terms
98
+
99
+ - **Byte**: an octet, 8 bits.
100
+ - **Writer**, **encoder**: software that produces a CGEODATA file.
101
+ - **Reader**, **decoder**: software that consumes a CGEODATA file.
102
+ - **Place**: a country, region, or city. Every place has an
103
+ **identifier** (ID), which is its zero-based position in the place
104
+ table ([Section 7](#7-place-table)).
105
+ - **Chunk grid**: the fixed rectangular grid of `W × H` chunks that
106
+ covers the world ([Section 6.1](#61-chunk-grid)).
107
+ - **Chunk**: one cell of the chunk grid, together with its stored
108
+ spatial index ([Section 8](#8-chunks)). The stored form is one
109
+ independently compressed block.
110
+ - **Cell**: a rectangular area produced by quadtree subdivision of a
111
+ chunk. A cell at depth `d` is `2^d` times smaller than the chunk in each
112
+ direction.
113
+ - **Record**: one leaf cell stored in a chunk, attributing that cell to
114
+ one place (or to no place).
115
+ - **Morton code** (also Z-order code): the integer obtained by
116
+ interleaving the bits of a cell's row and column, so that quadtree
117
+ order becomes numeric order ([Section 8.1](#81-cell-grid-and-morton-codes)).
118
+ - **Unknown**: the lookup result for a coordinate that no record
119
+ attributes to a place.
120
+
121
+ ### 2.3. Coordinate System
122
+
123
+ All coordinates are WGS84 latitude and longitude in decimal degrees. A
124
+ valid latitude satisfies `-90 <= lat < 90`. A valid longitude satisfies
125
+ `-180 <= lon < 180`. Readers MUST reject coordinates outside these ranges.
126
+ Readers do not normalise longitude; a caller that wishes to accept
127
+ `lon = 180` or values outside the range MUST wrap them itself before the
128
+ lookup.
129
+
130
+ All intervals in this document are half-open: `[a, b)` includes `a` and
131
+ excludes `b`. The world grid is equirectangular: cells are equal in
132
+ degrees, not in metres.
133
+
134
+ ## 3. Primitive Types
135
+
136
+ ### 3.1. Fixed-Width Integers
137
+
138
+ `u8` is one byte. `u16`, `u24`, `u32`, and `u64` are unsigned integers of
139
+ 2, 3, 4, and 8 bytes, stored least significant byte first
140
+ (little-endian). `u64` is used only for checksums; a reader never needs
141
+ its value as a number and can compare it byte by byte, which sidesteps
142
+ the 53-bit integer limit of JavaScript.
143
+
144
+ ### 3.2. Variable-Length Integers (varint)
145
+
146
+ A `varint` is an unsigned integer encoded as in unsigned LEB128 (the
147
+ "base 128 varint" of Protocol Buffers):
148
+
149
+ - Each byte carries 7 bits of the value in its low bits.
150
+ - The high bit (`0x80`) is a continuation flag: `1` means another byte
151
+ follows, `0` marks the last byte.
152
+ - Groups are stored least significant first. The decoder accumulates
153
+ `byte & 0x7F` shifted left by `7 × i` for the `i`-th byte, and stops
154
+ after the first byte whose high bit is clear.
155
+
156
+ Every varint in this format MUST be less than `2^53`, so that it can be
157
+ represented exactly as an IEEE 754 double. An encoding therefore never
158
+ exceeds 8 bytes; readers MUST reject a varint longer than 8 bytes.
159
+ Writers MUST use the shortest encoding (no trailing `0x80 0x00`
160
+ padding). Readers MAY reject non-minimal encodings.
161
+
162
+ Examples: `0` is `00`; `127` is `7f`; `128` is `80 01`; `300` is
163
+ `ac 02`; `8192` is `80 40`.
164
+
165
+ ### 3.3. Delta-Coded Lists
166
+
167
+ Several lists in this format are stored delta-coded: each stored varint is
168
+ the difference between an element and the element before it, with an
169
+ implicit previous element of `0` before the first. A delta-coded list is
170
+ therefore always non-decreasing; where this document says "strictly
171
+ increasing", every stored delta after the first MUST be at least `1`.
172
+
173
+ ### 3.4. Strings
174
+
175
+ Strings are UTF-8 [RFC3629]. The header's attribution string is length
176
+ prefixed. Place names are `NUL` (`0x00`) terminated inside their
177
+ compressed block and MUST NOT contain `NUL`.
178
+
179
+ ### 3.5. Compressed Blocks
180
+
181
+ A compressed block is one complete zlib stream [RFC1950] containing
182
+ DEFLATE data [RFC1951]. Every compressed block in this format is
183
+ self-contained: it can be inflated without any other block. Readers
184
+ locate a block by the byte lengths recorded in the file, not by scanning
185
+ for stream boundaries.
186
+
187
+ ## 4. File Layout
188
+
189
+ A CGEODATA file consists of four consecutive sections, with no padding or
190
+ alignment between or within them:
191
+
192
+ ```
193
+ +---------------------------+
194
+ | Header (Sec. 5) | fixed fields, uncompressed
195
+ +---------------------------+
196
+ | Directory (Sec. 6) | place table length + chunk length table
197
+ +---------------------------+
198
+ | Place table (Sec. 7) | hierarchy counts + compressed name blocks
199
+ +---------------------------+
200
+ | Chunk 0 (Sec. 8) | one compressed block, or absent if empty
201
+ | Chunk 1 |
202
+ | ... |
203
+ | Chunk W*H - 1 |
204
+ +---------------------------+
205
+ ```
206
+
207
+ The header and directory are small and are expected to be read once and
208
+ kept in memory. The place table's uncompressed prefix is also small. The
209
+ compressed name blocks and the chunks are read on demand.
210
+
211
+ The file has no trailer. The end of the last chunk is the end of the
212
+ file.
213
+
214
+ Derived sidecar files, such as the text search index of
215
+ [Appendix B](#appendix-b-text-search-index) and the place location index
216
+ of [Appendix C](#appendix-c-place-location-index), are separate files that
217
+ share the signature and the version field's kind mechanism
218
+ ([Section 5](#5-header)). They are never embedded in a CGEODATA file,
219
+ and a CGEODATA file never refers to them.
220
+
221
+ ## 5. Header
222
+
223
+ | Field | Type | Description |
224
+ |----------------------|------------|-------------|
225
+ | `signature` | 8 bytes | The ASCII bytes `CGEODATA` (`43 47 45 4f 44 41 54 41`). |
226
+ | `version` | varint | File kind and format version, `kind × 16 + version`. A CGEODATA file is kind `0`; this document specifies version `1`, so the field is `1`. See below. |
227
+ | `attribution_length` | varint | Byte length of `attribution`. |
228
+ | `attribution` | bytes | UTF-8 text, MAY contain Markdown, intended for display to humans, for example `[OpenStreetMap](https://openstreetmap.org/copyright)`. MAY be empty. |
229
+ | `width_exp` | u8 | The chunk grid has `W = 2^width_exp` columns. |
230
+ | `height_exp` | u8 | The chunk grid has `H = 2^height_exp` rows. |
231
+ | `max_depth` | u8 | The maximum quadtree depth `D` inside a chunk. Every Morton code in the file is `2 × D` bits wide. |
232
+ | `keyframe_exp` | u8 | Name block size. `0` means the whole name list is a single block. Any other value `k` means each block holds `I = 2^k` names. See [Section 7.3](#73-name-blocks). |
233
+
234
+ The `version` field carries two numbers: its low 4 bits are the format
235
+ version within a kind, and the remaining high bits are the **kind**,
236
+ which distinguishes the different files that share the `CGEODATA`
237
+ signature. The kinds are:
238
+
239
+ | Kind | Value of `version` | File |
240
+ |------|--------------------|------|
241
+ | `0` | `0` to `15` | A CGEODATA file as described by Sections 4 to 9. |
242
+ | `1` | `16` to `31` | A text search index derived from a kind `0` file ([Appendix B](#appendix-b-text-search-index)). |
243
+ | `2` | `32` to `47` | A place location index derived from a kind `0` file ([Appendix C](#appendix-c-place-location-index)). |
244
+
245
+ Other kinds are reserved. Kinds `1` and up are sidecar files: each is
246
+ derived from one kind `0` file, is regenerated rather than edited, and
247
+ has its own layout after the `version` field. Whether a file is a
248
+ sidecar is fixed by its kind, not by its name or location.
249
+
250
+ Readers MUST reject a file whose signature does not match, whose kind
251
+ is not the kind they expect, or whose version they do not implement.
252
+ Version `0` of any kind is a pre-release version: files written under
253
+ this document carry no compatibility promise once the version number
254
+ changes.
255
+
256
+ Version `1` of kind `0` differs from version `0` only in the layout of a
257
+ chunk: version `0` stored every record's Morton code, which version `1`
258
+ derives from the depths ([Section 8.6](#86-version-0-chunks)). A reader
259
+ MAY read both.
260
+
261
+ Constraints:
262
+
263
+ - `max_depth` MUST be at most `26`, so that a Morton code (`2 × D` bits)
264
+ fits in 52 bits and is an exact integer in JavaScript.
265
+ - `width_exp` and `height_exp` MUST each be at most `15`. Larger values
266
+ are reserved.
267
+
268
+ Choosing the grid sizes is discussed in
269
+ [Section 10](#10-encoder-considerations).
270
+
271
+ ## 6. Directory
272
+
273
+ ### 6.1. Chunk Grid
274
+
275
+ The chunk grid divides the world into `W` columns and `H` rows of equal
276
+ angular size. Column `c` (`0 <= c < W`) covers longitudes
277
+ `[-180 + c × 360 / W, -180 + (c + 1) × 360 / W)`. Row `r` (`0 <= r < H`)
278
+ covers latitudes `[-90 + r × 180 / H, -90 + (r + 1) × 180 / H)`. Row `0`
279
+ is the southernmost row; the origin of the grid is the south-west corner
280
+ of the world at `(lat -90, lon -180)`.
281
+
282
+ Chunks are numbered in row-major order: chunk index `i = r × W + c`.
283
+ This is the order of both the chunk length table and the stored chunks.
284
+
285
+ For example, with `width_exp = 6` (`W = 64`) each column is `5.625°`
286
+ wide: column `0` covers `[-180, -174.375)`, column `1` covers
287
+ `[-174.375, -168.75)`, and so on.
288
+
289
+ ### 6.2. Layout
290
+
291
+ Immediately after the header:
292
+
293
+ | Field | Type | Description |
294
+ |----------------------|-------------------|-------------|
295
+ | `place_table_length` | varint | Byte length of the entire place table ([Section 7](#7-place-table)), including its uncompressed prefix. |
296
+ | `chunk_lengths` | `W × H` varints | Entry `i` is the byte length of the compressed block for chunk `i`. `0` means chunk `i` is empty: no bytes are stored for it, and every coordinate inside it resolves to unknown. |
297
+
298
+ Entries are plain lengths, not deltas and not offsets.
299
+
300
+ ### 6.3. Locating a Chunk
301
+
302
+ Let `chunks_start` be the file offset of the first byte after the place
303
+ table, that is, the offset of the end of `chunk_lengths` plus
304
+ `place_table_length`. Then the compressed block for chunk `i` occupies
305
+
306
+ ```
307
+ offset(i) = chunks_start + sum(chunk_lengths[0 .. i-1])
308
+ length(i) = chunk_lengths[i]
309
+ ```
310
+
311
+ Readers SHOULD compute the prefix sums once when loading the directory,
312
+ so that locating a chunk is an array index. The directory alone is
313
+ enough to locate any chunk; no chunk needs to be decompressed to find
314
+ another.
315
+
316
+ The last chunk's length is recorded like any other. Because each chunk
317
+ is a self-contained zlib stream, a reader could inflate it without
318
+ knowing its length, but the recorded length lets a reader bound the read
319
+ and verify that the file is complete.
320
+
321
+ ## 7. Place Table
322
+
323
+ The place table is the list of all countries, regions, and cities the
324
+ file knows about, their names, and the parent of each region and city.
325
+ It is used to turn the identifier found by a spatial lookup into names,
326
+ and to support text search and listing.
327
+
328
+ ### 7.1. Identifiers and Hierarchy
329
+
330
+ The hierarchy has exactly three levels: countries contain regions, and
331
+ regions contain cities. Places are numbered in one shared identifier
332
+ space:
333
+
334
+ - IDs `0 .. C-1` are the `C` countries.
335
+ - IDs `C .. C+R-1` are the `R` regions, grouped by country: all regions
336
+ of country `0` first, then all regions of country `1`, and so on.
337
+ - IDs `C+R .. N-1` are the cities, grouped by region in the same way.
338
+ `N = C + R + (number of cities)` is the total number of places.
339
+
340
+ An identifier denotes exactly one place; a value used for a country is
341
+ never also used for a region or city. Chunk palettes
342
+ ([Section 8.3](#83-palette)) MAY reference any place, at any level: a
343
+ cell whose most specific known place is a region resolves to that
344
+ region.
345
+
346
+ The grouping order is what makes the hierarchy cheap to store: instead
347
+ of a parent pointer per place, the table stores only how many children
348
+ each parent has, and readers recover parents from prefix sums. A useful
349
+ side effect is that a search that stops after a limited number of hits
350
+ naturally reports countries before regions before cities.
351
+
352
+ ### 7.2. Layout
353
+
354
+ | Field | Type | Description |
355
+ |---------------------|----------------------|-------------|
356
+ | `country_count` | varint | `C`, the number of countries. |
357
+ | `region_counts` | `C` varints | Entry `j` is the number of regions belonging to country `j`. `R` is the sum of all entries. |
358
+ | `city_counts` | `R` varints | Entry `j` is the number of cities belonging to region `j` (region index, not ID). The number of cities is the sum of all entries. |
359
+ | `block_lengths` | `K - 1` varints | Entry `b` is the byte length of name block `b`, for `0 <= b < K-1`. `K` is defined in Section 7.3. |
360
+ | `name_blocks` | `K` compressed blocks | The name list, in ID order, split into blocks as described in Section 7.3. |
361
+
362
+ The four count fields are uncompressed and are expected to be read once
363
+ and kept in memory. A country's region count or a region's city count
364
+ MAY be `0`.
365
+
366
+ ### 7.3. Name Blocks
367
+
368
+ The name list is the concatenation, in ID order, of every place's name
369
+ entry ([Section 7.4](#74-name-entries)), each followed by a `NUL` byte.
370
+ It is split into `K` blocks of `I` consecutive entries each (the last
371
+ block MAY be shorter), where
372
+
373
+ ```
374
+ I = 2^keyframe_exp (if keyframe_exp != 0)
375
+ K = ceil(N / I) (if keyframe_exp != 0)
376
+ K = 1 (if keyframe_exp == 0)
377
+ ```
378
+
379
+ A file with no places (`N = 0`) has `K = 0` and no name blocks.
380
+
381
+ Block `b` holds the entries with IDs `b × I .. min((b + 1) × I, N) - 1`.
382
+ A block boundary MAY fall anywhere, including between a country and a
383
+ region. Each block is compressed separately as one compressed block
384
+ ([Section 3.5](#35-compressed-blocks)).
385
+
386
+ Block `b` starts at
387
+
388
+ ```
389
+ names_start + sum(block_lengths[0 .. b-1])
390
+ ```
391
+
392
+ where `names_start` is the offset of the first byte after
393
+ `block_lengths`. The length of the last block is not stored; it extends
394
+ to the end of the place table, so its length is `place_table_length`
395
+ minus the number of bytes consumed by everything before it.
396
+
397
+ The blocks act as keyframes for random access: to recover the name of
398
+ place `id`, a reader inflates only block `floor(id / I)` and skips
399
+ `id mod I` `NUL`-terminated entries. A smaller `I` gives cheaper random
400
+ access; a larger `I` gives better compression, since zlib finds more
401
+ repetition in a larger block, and a shorter `block_lengths` list.
402
+
403
+ `keyframe_exp = 7` (blocks of 128 names) is RECOMMENDED. Measured on the
404
+ worldwide file (about 319,000 places), with a reader that opens the file
405
+ afresh for every operation so that no name block is cached, which is
406
+ the situation of a reader that is queried only occasionally:
407
+
408
+ | `keyframe_exp` | Place table | Resolving one place | Scanning all names |
409
+ |----------------|-------------|---------------------|--------------------|
410
+ | `13` | 1.64 MB | 4.9 ms | 134 ms |
411
+ | `11` | 1.70 MB | 1.7 ms | 142 ms |
412
+ | `9` | 1.82 MB | 0.9 ms | 141 ms |
413
+ | `7` | 2.05 MB | 0.7 ms | 152 ms |
414
+ | `5` | 2.53 MB | 1.4 ms | 210 ms |
415
+
416
+ "Resolving one place" is a place and its parents' names, as every
417
+ lookup ([Section 9](#9-lookup-procedure)) that finds a place needs.
418
+ Name access is random in nearly every use: a lookup needs up to three
419
+ names from anywhere in the table, and a search confirms candidates
420
+ spread across it ([Section B.9](#b9-query-procedure)), so every
421
+ operation other than a full scan pays for one block per distinct name
422
+ it touches, and a smaller block is directly cheaper. Below `7` the
423
+ fixed costs take over: a reader parses `K - 1` block lengths when it
424
+ opens the file, and each block carries its own zlib framing, so both
425
+ opening the file and a full scan get slower. The place table grows by
426
+ a quarter from `13` to `7`, which is small beside the chunks at any
427
+ useful `max_depth`.
428
+
429
+ ### 7.4. Name Entries
430
+
431
+ A name entry is one or more names separated by `|` (`0x7C`), interpreted
432
+ by position:
433
+
434
+ 1. The first field is the local name, as the place is called in its own
435
+ language, for example `Nederland`. It MUST NOT be empty.
436
+ 2. The second field is the international (English) name, for example
437
+ `The Netherlands`. It MAY be empty or absent, meaning the
438
+ international name is the same as the local name.
439
+ 3. Any further fields are alternative names or spellings, useful for
440
+ search, for example the historical or transliterated names.
441
+
442
+ Writers SHOULD omit trailing empty fields, so a place with a single name
443
+ is stored as `Amsterdam`, not `Amsterdam|`. Fields MUST NOT contain `|`
444
+ or `NUL`; there is no escape mechanism. A reader that displays a place
445
+ SHOULD show the local name, and a search SHOULD match against every
446
+ field.
447
+
448
+ Examples:
449
+
450
+ ```
451
+ Nederland|The Netherlands
452
+ Den Haag|The Hague|'s-Gravenhage
453
+ Amsterdam
454
+ ```
455
+
456
+ ### 7.5. Resolving an Identifier
457
+
458
+ Given the counts of Section 7.2, a reader classifies `id` as follows:
459
+
460
+ - If `id < C`, it is country `id`.
461
+ - Otherwise, if `id < C + R`, it is the region with region index
462
+ `j = id - C`. Its country is the largest `c` such that
463
+ `sum(region_counts[0 .. c-1]) <= j`.
464
+ - Otherwise it is the city with city index `j = id - C - R`. Its region
465
+ index is the largest `r` such that `sum(city_counts[0 .. r-1]) <= j`,
466
+ and its country follows from the region as above.
467
+
468
+ Readers SHOULD precompute the two prefix-sum arrays at load time, after
469
+ which both steps are a binary search. Readers MUST reject an `id` that
470
+ is not less than `N`.
471
+
472
+ ## 8. Chunks
473
+
474
+ Each non-empty chunk stores the spatial index for its area as a single
475
+ compressed block ([Section 3.5](#35-compressed-blocks)). This section
476
+ describes the inflated content.
477
+
478
+ ### 8.1. Cell Grid and Morton Codes
479
+
480
+ Inside a chunk, a quadtree of depth `D = max_depth` subdivides the chunk
481
+ into `2^D × 2^D` cells of equal angular size. A cell is addressed by its
482
+ column `x` and row `y`, each in `[0, 2^D)`, counted from the chunk's
483
+ south-west corner: `x` increases eastward and `y` increases northward.
484
+
485
+ The full-precision Morton code of cell `(x, y)` is the `2 × D`-bit
486
+ integer obtained by interleaving the bits of `y` and `x`, most
487
+ significant first, with the `y` bit placed above the `x` bit in every
488
+ pair:
489
+
490
+ ```
491
+ code = 0
492
+ for i from D-1 down to 0:
493
+ code = (code << 1) | ((y >> i) & 1)
494
+ code = (code << 1) | ((x >> i) & 1)
495
+ ```
496
+
497
+ Equivalently, reading the code two bits at a time from the top selects a
498
+ quadrant at each level: `00` south-west, `01` south-east, `10`
499
+ north-west, `11` north-east.
500
+
501
+ A cell at depth `d < D` is the union of the `4^(D-d)` full-precision
502
+ cells whose codes share its top `2 × d` bits. Its **stored code** is the
503
+ smallest of those codes: the `2 × d` path bits followed by
504
+ `2 × (D - d)` zero bits. Consequently the stored code of a cell is the
505
+ minimum full-precision code of any point inside it, which is the
506
+ invariant the lookup in [Section 9](#9-lookup-procedure) depends on.
507
+
508
+ Codes are chunk-local. Cell `(0, 0)` is the chunk's own south-west
509
+ corner, not the world's. This keeps codes small.
510
+
511
+ For `D = 2` the sixteen full-precision codes are laid out as follows
512
+ (north is up):
513
+
514
+ ```
515
+ y=3 | 10 11 14 15
516
+ y=2 | 8 9 12 13
517
+ y=1 | 2 3 6 7
518
+ y=0 | 0 1 4 5
519
+ +----------------
520
+ x=0 x=1 x=2 x=3
521
+ ```
522
+
523
+ ### 8.2. Layout
524
+
525
+ | Field | Type | Description |
526
+ |-------------------|----------------------------|-------------|
527
+ | `palette_count` | varint | `P`, the number of palette entries. |
528
+ | `palette` | `P` varints, delta-coded | Place IDs referenced by this chunk, strictly increasing ([Section 8.3](#83-palette)). |
529
+ | `record_count` | varint | `M`, the number of records. |
530
+ | `depths` | `M` u8 | Depth `d` of each record, in Morton order. |
531
+ | `palette_indices` | `M` × `S` bytes | Palette index of each record, in Morton order, as unsigned little-endian integers of `S` bytes. |
532
+
533
+ The two per-record arrays are stored one after the other rather than
534
+ interleaved per record. Grouping same-typed values together lets zlib
535
+ find far more repetition; on the prototype data this columnar layout
536
+ plus delta coding reduced the index about twelvefold compared with
537
+ fixed 8-byte records.
538
+
539
+ A record's Morton code is not stored: the records tile the chunk in
540
+ Morton order, so each one starts where the one before it ends
541
+ ([Section 8.4](#84-records)). Both arrays are fixed width, so the length
542
+ of the block follows from `P` and `M`.
543
+
544
+ The inflated block MUST contain exactly these fields and nothing else:
545
+ after `palette_indices` the block ends.
546
+
547
+ ### 8.3. Palette
548
+
549
+ The palette lists every place ID referenced by the chunk's records, in
550
+ strictly increasing order, delta-coded ([Section 3.3](#33-delta-coded-lists)).
551
+ The first stored value is the first ID itself (its delta from `0`), so an
552
+ ID of `0`, the first country, can be referenced.
553
+
554
+ Records refer to the palette by a **local index** in `[0, P]`:
555
+
556
+ - Index `0` means "no place". A record with index `0` resolves to
557
+ unknown, exactly as if no record covered the cell.
558
+ - Index `k` for `1 <= k <= P` refers to `palette[k - 1]`.
559
+
560
+ The index width `S` is derived from `P`:
561
+
562
+ | `P` | `S` |
563
+ |-------------------------|---------|
564
+ | `P <= 255` | 1 byte |
565
+ | `256 <= P <= 65535` | 2 bytes |
566
+ | `65536 <= P <= 2^24-1` | 3 bytes |
567
+ | otherwise | 4 bytes |
568
+
569
+ Most chunks are expected to reference a few dozen places at most and to
570
+ use 1-byte indices.
571
+
572
+ Every palette entry MUST be a valid place ID, that is, less than `N`.
573
+
574
+ ### 8.4. Records
575
+
576
+ Record `j` is the triple `(codes[j], depths[j], palette_indices[j])`.
577
+ It states that the cell at depth `depths[j]` whose stored code is
578
+ `codes[j]` is attributed to the place selected by `palette_indices[j]`.
579
+
580
+ A cell denoted by `(code, d)` covers the full-precision code range
581
+ `[code, code + 4^(D-d))`. The records cover the chunk from its first
582
+ full-precision cell on, without gaps, so each code is where the record
583
+ before it ends:
584
+
585
+ ```
586
+ codes[0] = 0
587
+ codes[j+1] = codes[j] + 4^(D - depths[j])
588
+ ```
589
+
590
+ An empty area between two places is covered by records with palette
591
+ index `0`. The records MAY end before the chunk does: the cells from
592
+ `codes[M-1] + 4^(D - depths[M-1])` up to `4^D` have no record and resolve
593
+ to unknown. A chunk with no place at all need not be stored
594
+ ([Section 6.2](#62-layout)).
595
+
596
+ ### 8.5. Invariants
597
+
598
+ A chunk MUST satisfy all of the following. Readers MAY verify them at
599
+ load time and MUST NOT rely on undefined behaviour if they do not.
600
+
601
+ 1. `0 <= depths[j] <= D` for every record.
602
+ 2. The low `2 × (D - depths[j])` bits of `codes[j]` are zero (the code
603
+ is a stored code as defined in Section 8.1).
604
+ 3. The records end within the chunk:
605
+ `codes[M-1] + 4^(D - depths[M-1]) <= 4^D`.
606
+ 4. Every palette index is at most `P`.
607
+
608
+ Codes are then strictly increasing, every code is less than `4^D`, and
609
+ cells do not overlap, by construction.
610
+
611
+ ### 8.6. Version 0 Chunks
612
+
613
+ In a version `0` file ([Section 5](#5-header)), a chunk stores every
614
+ record's code, between `record_count` and `depths`:
615
+
616
+ | Field | Type | Description |
617
+ |-------------------|----------------------------|-------------|
618
+ | `palette_count` | varint | As in Section 8.2. |
619
+ | `palette` | `P` varints, delta-coded | As in Section 8.2. |
620
+ | `record_count` | varint | As in Section 8.2. |
621
+ | `codes_length` | varint | Byte length of `codes`. |
622
+ | `codes` | `M` varints, delta-coded | Stored Morton codes, strictly increasing. |
623
+ | `depths` | `M` u8 | As in Section 8.2. |
624
+ | `palette_indices` | `M` × `S` bytes | As in Section 8.2. |
625
+
626
+ The records need not tile the chunk: a writer could leave out any empty
627
+ cell, not only those after the last record, and a coordinate in such a
628
+ gap resolves to unknown. Invariants 1, 2 and 4 of
629
+ [Section 8.5](#85-invariants) apply, and invariant 3 is replaced by:
630
+
631
+ - Codes are strictly increasing, `codes[j] < codes[j+1]`, and cells do
632
+ not overlap, `codes[j] + 4^(D - depths[j]) <= codes[j+1]`.
633
+ - Every code is less than `4^D`.
634
+
635
+ Version `1` dropped the codes because they were nearly all redundant.
636
+ A writer's records hardly ever leave a gap, and where none is left, a
637
+ code delta is the size of the record before it, `4^(D - depths[j])`.
638
+ zlib, which sees the codes and the depths as separate columns, did not
639
+ find that out. In the worldwide file, 1.66 million gaps among 106.4
640
+ million records took 3.77 million index-`0` records (3.5% more records)
641
+ to fill, and the chunks went from 62.2 MB to 45.6 MB (-26.7%), with the
642
+ same cell for every place.
643
+
644
+ ## 9. Lookup Procedure
645
+
646
+ To resolve a coordinate `(lat, lon)` to a place ID, a reader performs the
647
+ following steps. `W`, `H`, and `D` come from the header.
648
+
649
+ 1. **Validate.** If `lat` or `lon` is outside the ranges of
650
+ [Section 2.3](#23-coordinate-system), fail.
651
+ 2. **Compute global cell coordinates.** Let `NX = W × 2^D` and
652
+ `NY = H × 2^D` be the total number of full-precision cells across the
653
+ world in each direction.
654
+
655
+ ```
656
+ gx = floor((lon + 180) / 360 × NX)
657
+ gy = floor((lat + 90) / 180 × NY)
658
+ ```
659
+
660
+ Because floating-point rounding can push a value just below the upper
661
+ bound onto it, readers MUST clamp `gx` to `[0, NX-1]` and `gy` to
662
+ `[0, NY-1]`.
663
+ 3. **Split into chunk and local cell.** `c = gx >> D`, `r = gy >> D`,
664
+ `x = gx & (2^D - 1)`, `y = gy & (2^D - 1)`, and the chunk index is
665
+ `i = r × W + c`. Computing `gx` and `gy` once and splitting them, rather
666
+ than computing the chunk and the local cell with separate divisions,
667
+ avoids inconsistent rounding between the two.
668
+ 4. **Fetch the chunk.** If `chunk_lengths[i]` is `0`, the result is
669
+ unknown. Otherwise read `length(i)` bytes at `offset(i)`
670
+ ([Section 6.3](#63-locating-a-chunk)) and inflate them, and compute
671
+ the records' codes ([Section 8.4](#84-records)). Readers SHOULD cache
672
+ inflated chunks with their codes.
673
+ 5. **Compute the query code** `q` from `(x, y)` with the procedure of
674
+ [Section 8.1](#81-cell-grid-and-morton-codes).
675
+ 6. **Binary search.** Find the largest `j` such that `codes[j] <= q`
676
+ (`bisect_right(codes, q) - 1` in Python terms). If no such `j` exists,
677
+ the result is unknown.
678
+ 7. **Verify containment.** Let `shift = 2 × (D - depths[j])`. If
679
+ `(q >> shift) != (codes[j] >> shift)`, the point lies after the last
680
+ record (or, in a version `0` file, in a gap between records) and the
681
+ result is unknown.
682
+ 8. **Resolve the palette index.** Let `k = palette_indices[j]`. If `k` is
683
+ `0`, the result is unknown. Otherwise the result is `palette[k - 1]`.
684
+
685
+ Step 6 is correct because of the invariants of
686
+ [Section 8.5](#85-invariants): every stored code is the minimum code of
687
+ its cell, cells do not overlap, and codes are sorted, so the cell
688
+ containing `q`, if any, is the one with the largest stored code not
689
+ exceeding `q`. Step 7 is required because the records may end before the
690
+ chunk does.
691
+
692
+ To present the result, the reader resolves the ID to its parents
693
+ ([Section 7.5](#75-resolving-an-identifier)) and recovers the names from
694
+ the appropriate name blocks ([Section 7.3](#73-name-blocks)).
695
+
696
+ ## 10. Encoder Considerations
697
+
698
+ This section is informative.
699
+
700
+ **Chunk grid size.** More chunks mean that each lookup inflates a smaller
701
+ block and that codes are shorter, at the cost of a larger directory and
702
+ some per-chunk overhead (zlib framing and a palette per chunk). Chunks
703
+ over open ocean cost only their `0` length entry. `width_exp = height_exp + 1`
704
+ gives chunks that are square in degrees; any other ratio gives
705
+ rectangular chunks and cells, which is permitted.
706
+
707
+ **Maximum depth.** `D` together with the chunk grid fixes the finest
708
+ resolution: a full-precision cell is `360 / (W × 2^D)` degrees of
709
+ longitude by `180 / (H × 2^D)` degrees of latitude. The prototype found
710
+ that forcing every ambiguous cell down to a 10 m floor produced far more
711
+ records than the size budget allowed, while stopping at roughly 300 m
712
+ and attributing the remaining ambiguous cells to the candidate with the
713
+ largest share of the cell's area gave good results for geotagging.
714
+
715
+ A deep `D` makes lookups slower, not just files smaller: a chunk is one
716
+ zlib block, which a lookup inflates whole, and it spans up to `4^D`
717
+ cells -- already over four billion at `D = 16`. Finer cells should come
718
+ from a bigger chunk grid instead. The reference reader therefore
719
+ rejects files with `D` above `16`, where every Morton code still fits in
720
+ an unsigned 32-bit integer (`4^16 = 2^32`); a writer SHOULD stay at or
721
+ below it. With a `128 × 128` chunk grid, `D = 16` is a cell about 5 m
722
+ wide at the equator. Deeper files are valid, but only suit transfer or
723
+ archiving, where a decoder can stream through each chunk.
724
+
725
+ **Building records.** The intended construction is recursive: starting
726
+ from the chunk as a depth-`0` cell, stop and emit a record when no place
727
+ touches the cell (with palette index `0`), or when exactly one place
728
+ fully contains it; otherwise subdivide, and at depth `D` pick one
729
+ candidate. When several places fully contain a cell (nested
730
+ administrative levels), prefer the smallest.
731
+
732
+ **Empty cells.** An empty area between two records is best written as
733
+ the fewest index-`0` records that cover it: each the largest cell that
734
+ starts where the one before it ends and does not reach past the area.
735
+ Four empty siblings make one empty parent, as they would in the
736
+ recursive construction. Empty cells after the last record need no
737
+ record at all.
738
+
739
+ **Keyframe interval.** See [Section 7.3](#73-name-blocks).
740
+
741
+ **Compression.** zlib was chosen over zstd or brotli because it is a
742
+ zero-dependency built-in in Node.js and browsers. The columnar and
743
+ delta-coded layout captures most of the gain regardless of the
744
+ compressor; on the prototype, zstd and brotli were only 10–20% smaller.
745
+ Writers SHOULD use the highest zlib compression level; compression is a
746
+ one-time cost.
747
+
748
+ ## 11. Robustness and Security Considerations
749
+
750
+ A CGEODATA file may come from an untrusted source. Readers MUST NOT
751
+ assume that a file is well formed.
752
+
753
+ - Readers MUST verify the signature, kind, and version before
754
+ interpreting any other field.
755
+ - Readers MUST check that `place_table_length`, every chunk offset, and
756
+ every chunk length lie within the file before reading.
757
+ - Readers MUST bound the inflated size of every compressed block. A
758
+ chunk's inflated size is at most a small multiple of what its counts
759
+ imply; a reader SHOULD inflate with an explicit output limit and reject
760
+ a block that exceeds it, to defend against decompression bombs.
761
+ - Readers MUST check that the inflated chunk contains exactly the bytes
762
+ its counts require, that every depth is at most `D`, that the records
763
+ end within the chunk, that every palette index is at most `P`, and
764
+ that every palette entry is less than `N`. Readers MAY additionally
765
+ verify the alignment invariant of [Section 8.5](#85-invariants), and
766
+ in a version `0` file the ordering and non-overlap invariants of
767
+ [Section 8.6](#86-version-0-chunks).
768
+ - Readers MUST treat the attribution text and place names as untrusted
769
+ text. Where they are rendered as Markdown or HTML, they MUST be
770
+ sanitised.
771
+ - Varints MUST be rejected when longer than 8 bytes, so that a malformed
772
+ file cannot cause unbounded loops or values beyond `2^53`.
773
+
774
+ ## 12. Open Issues
775
+
776
+ The following are known gaps or unresolved decisions in version `1`.
777
+
778
+ 1. **Latitude scaling.** Because the grid is equirectangular, cells
779
+ shrink in east-west extent towards the poles. A per-row scaling
780
+ factor, or fewer columns at high latitudes, would keep cells closer to
781
+ square on the ground. Not addressed in this version.
782
+ 2. **Representative points are derived, not stored.** A place search
783
+ that returns a location for each hit (as Immich's `searchPlaces`
784
+ does) needs a representative point per place. The place table has no
785
+ such field; the point is derived from the chunks
786
+ ([Section C.3](#c3-representative-cell)) and cached in a sidecar file
787
+ of kind `2`. It is therefore limited to what the cells show: it cannot
788
+ be an administrative centre or a label position from the source data.
789
+ A future version could let the encoder supply one.
790
+ 3. **No exact-fallback marker.** The prototype's approach C reserves a
791
+ sentinel for cells that should be resolved by exact polygon
792
+ arithmetic. This format only supports build-time attribution
793
+ (approach D). A reserved palette index could be added later.
794
+ 4. **Fixed three-level hierarchy.** Countries whose cities sit directly
795
+ under the country need a placeholder region.
796
+ 5. **Global versus per-chunk depth.** `D` is global. A per-chunk depth
797
+ would let sparse areas stop at coarser cells; whether that is worth
798
+ the complexity has not been measured.
799
+ 6. **Name escaping.** `|` cannot appear in a name. No real place name
800
+ affected by this is known, but there is no escape mechanism.
801
+
802
+ ## Appendix A. Worked Example
803
+
804
+ This appendix gives a complete, valid, synthetic file of 157 bytes and
805
+ walks through several lookups. Its geography is fictional. The file uses
806
+ a `2 × 2` chunk grid, a quadtree depth of `2`, and name blocks of `4`
807
+ entries.
808
+
809
+ ### A.1. Header (17 bytes)
810
+
811
+ ```
812
+ 43 47 45 4f 44 41 54 41 signature "CGEODATA"
813
+ 01 version 1
814
+ 03 4f 53 4d attribution_length 3, attribution "OSM"
815
+ 01 width_exp 1 -> W = 2
816
+ 01 height_exp 1 -> H = 2
817
+ 02 max_depth 2 -> D = 2, codes are 4 bits
818
+ 02 keyframe_exp 2 -> I = 4 names per block
819
+ ```
820
+
821
+ ### A.2. Directory (5 bytes)
822
+
823
+ ```
824
+ 6d place_table_length 109
825
+ 00 00 00 1a chunk_lengths: chunk 0, 1, 2 empty; chunk 3 is 26 bytes
826
+ ```
827
+
828
+ Chunk `3` is row `1`, column `1`: latitudes `[0, 90)`, longitudes
829
+ `[0, 180)`. Its block starts at offset `17 + 5 + 109 = 131` and ends at
830
+ `157`, the end of the file.
831
+
832
+ ### A.3. Place Table (109 bytes)
833
+
834
+ Uncompressed prefix (6 bytes):
835
+
836
+ ```
837
+ 02 country_count C = 2
838
+ 01 01 region_counts: country 0 has 1 region, country 1 has 1 -> R = 2
839
+ 02 01 city_counts: region 0 has 2 cities, region 1 has 1 -> 3 cities, N = 7
840
+ 43 block_lengths: block 0 is 67 bytes (K = ceil(7/4) = 2, so K-1 = 1 entry)
841
+ ```
842
+
843
+ The identifier space is:
844
+
845
+ | ID | Kind | Parent | Name entry |
846
+ |----|---------|--------|----------------------------|
847
+ | 0 | country | | `Nederland\|The Netherlands` |
848
+ | 1 | country | | `België\|Belgium` |
849
+ | 2 | region | 0 | `Noord-Holland` |
850
+ | 3 | region | 1 | `Antwerpen\|Antwerp` |
851
+ | 4 | city | 2 | `Amsterdam` |
852
+ | 5 | city | 2 | `Zaandam` |
853
+ | 6 | city | 3 | `Antwerpen\|Antwerp` |
854
+
855
+ Block `0` (IDs 0–3) inflates to
856
+ `Nederland|The Netherlands\0België|Belgium\0Noord-Holland\0Antwerpen|Antwerp\0`
857
+ and is stored as these 67 bytes:
858
+
859
+ ```
860
+ 78 da f3 4b 4d 49 2d ca 49 cc 4b a9 09 c9 48 55 f0 4b 2d c9 80 70 8b 19
861
+ 9c 52 73 d2 33 0f af ae 01 d3 a5 b9 0c 7e f9 f9 45 29 ba 1e f9 39 20 69
862
+ 06 c7 bc 92 f2 d4 a2 82 d4 bc 1a 28 8b 01 00 2e 70 1c 57
863
+ ```
864
+
865
+ Block `1` (IDs 4–6) inflates to `Amsterdam\0Zaandam\0Antwerpen|Antwerp\0`
866
+ and is stored as the remaining `109 - 6 - 67 = 36` bytes:
867
+
868
+ ```
869
+ 78 da 73 cc 2d 2e 49 2d 4a 49 cc 65 88 4a 4c cc 03 d1 8e 79 25 e5 a9 45
870
+ 05 a9 79 35 50 16 03 00 f7 a7 0d 6c
871
+ ```
872
+
873
+ To find the name of ID `5`, a reader inflates block `floor(5 / 4) = 1`
874
+ and skips `5 mod 4 = 1` entry, arriving at `Zaandam`.
875
+
876
+ ### A.4. Chunk 3 (26 bytes compressed)
877
+
878
+ Stored bytes:
879
+
880
+ ```
881
+ 78 da 63 62 62 66 67 64 62 62 62 62 64 62 64 62 60 60 60 64 02 00 01 8b
882
+ 00 21
883
+ ```
884
+
885
+ Inflated content (18 bytes):
886
+
887
+ ```
888
+ 02 palette_count P = 2 -> S = 1
889
+ 02 03 palette deltas 2, 3 -> IDs 2 (Noord-Holland), 5 (Zaandam)
890
+ 07 record_count M = 7
891
+ 01 02 02 02 02 01 02 depths
892
+ 01 02 00 00 00 01 02 palette_indices -> ID 2, ID 5, none, none, none, ID 2, ID 5
893
+ ```
894
+
895
+ Each record starts where the one before it ends
896
+ ([Section 8.4](#84-records)). A depth-1 record spans `4^(2-1) = 4`
897
+ codes and a depth-2 record `1`, so the codes are:
898
+
899
+ | Record | Depth | Code | Palette index |
900
+ |--------|-------|-------------------|---------------|
901
+ | 0 | 1 | `0` | `1`, ID 2 |
902
+ | 1 | 2 | `0 + 4 = 4` | `2`, ID 5 |
903
+ | 2 | 2 | `4 + 1 = 5` | `0`, none |
904
+ | 3 | 2 | `5 + 1 = 6` | `0`, none |
905
+ | 4 | 2 | `6 + 1 = 7` | `0`, none |
906
+ | 5 | 1 | `7 + 1 = 8` | `1`, ID 2 |
907
+ | 6 | 2 | `8 + 4 = 12` | `2`, ID 5 |
908
+
909
+ The last record ends at `12 + 1 = 13`. The seven records cover the
910
+ chunk's `4 × 4` cell grid as follows (north is up; `A` = Noord-Holland,
911
+ `B` = Zaandam, `-` = index 0, `.` = no record):
912
+
913
+ ```
914
+ y=3 | A A . .
915
+ y=2 | A A B .
916
+ y=1 | A A - -
917
+ y=0 | A A B -
918
+ +-----------------
919
+ x=0 x=1 x=2 x=3
920
+ ```
921
+
922
+ Record `0` is `(code 0, depth 1)`, the depth-1 cell covering codes
923
+ `0..3`. Record `5` is `(code 8, depth 1)`, covering codes `8..11`. The
924
+ other five are full-precision cells. Codes `5` to `7` are empty and
925
+ covered by index-`0` records; codes `13` to `15`, after the last record,
926
+ have no record. Records `3` and `4` could not be one depth-1 record:
927
+ codes `6` and `7` are half of the depth-1 cell `4..7`, whose other half
928
+ holds Zaandam and record `2`.
929
+
930
+ ### A.5. Lookups
931
+
932
+ `NX = NY = 2 × 2^2 = 8`.
933
+
934
+ **`(lat 20, lon 100)`.** `gx = floor(280/360 × 8) = 6`,
935
+ `gy = floor(110/180 × 8) = 4`. Chunk `c = 1, r = 1`, index `3`; local
936
+ cell `x = 2, y = 0`, code `q = 4`. The largest stored code `<= 4` is
937
+ record `1` (`code 4, depth 2`). `shift = 0`, and `4 == 4`, so the point
938
+ is inside. Palette index `2` selects ID `5`: **Zaandam**, in
939
+ Noord-Holland, Nederland.
940
+
941
+ **`(lat 40, lon 30)`.** `gx = floor(210/360 × 8) = 4`,
942
+ `gy = floor(130/180 × 8) = 5`. Chunk `3`; local cell `x = 0, y = 1`,
943
+ `q = 2`. The largest stored code `<= 2` is record `0` (`code 0,
944
+ depth 1`). `shift = 2 × (2 - 1) = 2`, and `2 >> 2 == 0 >> 2`, so the
945
+ point is inside that depth-1 cell. Palette index `1` selects ID `2`:
946
+ **Noord-Holland** (a region-level result).
947
+
948
+ **`(lat 30, lon 100)`.** `gx = 6`, `gy = floor(120/180 × 8) = 5`;
949
+ chunk `3`, local `x = 2, y = 1`, `q = 6`. The largest code `<= 6` is
950
+ record `3` (`code 6, depth 2`), which matches exactly, but its palette
951
+ index is `0`: **unknown**.
952
+
953
+ **`(lat 20, lon 170)`.** `gx = 7`, `gy = 4`; local `x = 3, y = 0`,
954
+ `q = 5`. Record `2` matches exactly, and its palette index is `0` too:
955
+ **unknown**.
956
+
957
+ **`(lat 60, lon 170)`.** `gx = 7`, `gy = floor(150/180 × 8) = 6`;
958
+ chunk `3`, local `x = 3, y = 2`, `q = 13`. The largest code `<= 13` is
959
+ record `6` (`code 12, depth 2`). `shift = 0` and `13 != 12`, so the
960
+ point lies after the last record: **unknown**.
961
+
962
+ **`(lat -10, lon 10)`.** `gx = 4`, `gy = floor(80/180 × 8) = 3`; chunk
963
+ `c = 1, r = 0`, index `1`. Its length is `0`: **unknown**, with no
964
+ decompression performed.
965
+
966
+ ## Appendix B. Text Search Index
967
+
968
+ This appendix specifies an OPTIONAL sidecar file that speeds up substring
969
+ search over place names. It is a separate file, derived entirely from the
970
+ place table ([Section 7](#7-place-table)) of one CGEODATA file, and it
971
+ carries file kind `1` ([Section 5](#5-header)).
972
+
973
+ ### B.1. Purpose and Properties
974
+
975
+ A text search without an index reads every name in the place table and
976
+ compares it with the query: a linear scan over every name block, which
977
+ at worldwide scale means inflating the names of over 300,000 places. The
978
+ index described here lets a reader avoid most of that, in two ways:
979
+
980
+ - **Trigram lists** ([Section B.3](#b3-trigrams-and-lists)) record, for
981
+ every run of three symbols, which places contain it in one of their
982
+ names. A query of three or more symbols reads only the lists of its own
983
+ trigrams, and only the places present in all of them can match.
984
+ - **A short-query table** ([Section B.6](#b6-short-query-table)) answers
985
+ queries of one or two characters, which have no trigram and match a
986
+ large part of the table, by storing the first places of their
987
+ relevance-ordered result directly.
988
+
989
+ The index has the following properties:
990
+
991
+ - **It covers every place.** Countries, regions, and cities are all
992
+ indexed, so a query never has to scan any part of the place table
993
+ linearly while the index applies.
994
+ - **The trigram lists only over-approximate.** Every step that maps
995
+ names to lists is many-to-one, so the places they leave over are a
996
+ superset of the true matches ([Section B.9](#b9-query-procedure)). A
997
+ reader confirms every candidate by comparing its names, exactly as it
998
+ would without the index, and MUST produce the same results with and
999
+ without it.
1000
+ - **It is a pure function of its source.** Two writers given the
1001
+ same source, the same parameters, and the same relevance rule
1002
+ ([Section B.7](#b7-file-layout)) MUST produce byte-identical files.
1003
+ The file carries no timestamp and nothing else that depends on
1004
+ the writer, and nothing in it is compressed, since deflate output is
1005
+ not guaranteed to be identical between zlib implementations. It is
1006
+ bound to its source by a checksum ([Section B.8](#b8-source-checksum)):
1007
+ over the source's place table, or for a short-query table in area
1008
+ order, over everything up to the first chunk.
1009
+ - **It is read in parts.** The header records the length of every
1010
+ section, so a reader reads only the header up front, and afterwards
1011
+ only what a query needs: the list directory and a few lists for a long
1012
+ query, or the table directory and one table block for a short one.
1013
+ - **It is disposable.** Because it is derived, it can be rebuilt from
1014
+ its source at any time.
1015
+
1016
+ ### B.2. Symbol Folding
1017
+
1018
+ Names and queries are reduced in two stages, both used elsewhere in this
1019
+ appendix: first to their **folded form**, a plain string that is
1020
+ diacritic- and case-insensitive; then, for trigram purposes only, further
1021
+ to a sequence of **symbols**, non-negative integers that also collapse
1022
+ every ASCII digit together and every other ASCII punctuation/space
1023
+ character together. The folded form is what [Section B.5](#b5-relevance-order)
1024
+ and [Section B.6](#b6-short-query-table) compare and rank with; the
1025
+ symbol sequence is what trigrams ([Section B.3](#b3-trigrams-and-lists))
1026
+ are taken from. Keeping them distinct matters: the symbol sequence's
1027
+ digit/punctuation bucketing is the right call for keeping the number of
1028
+ trigram lists bounded, but it would make `5th` match `9th` as a
1029
+ substring, which is too loose for deciding whether, or how well, text
1030
+ actually matches a query.
1031
+
1032
+ The folded form is produced by:
1033
+
1034
+ 1. Decompose the text into Unicode Normalization Form D [UAX15].
1035
+ 2. Remove every code point in the Combining Diacritical Marks block,
1036
+ `U+0300` to `U+036F` inclusive.
1037
+ 3. Convert the result to lowercase with the Unicode default case
1038
+ conversion (the locale-independent `toLowercase()` of [UNICODE]
1039
+ Section 3.13).
1040
+
1041
+ Step 1 followed by step 2 turns `é` into `e`. Step 3 turns `E` into `e`.
1042
+ `ß`, `ø`, and `ł`, which have no decomposition, and every letter of a
1043
+ non-Latin script, pass through unchanged apart from case.
1044
+
1045
+ The symbol sequence continues from the folded form with a fourth step,
1046
+ mapping each remaining code point `c` to one symbol:
1047
+
1048
+ | Code points | Symbol |
1049
+ |---------------------------------|--------|
1050
+ | `a` to `z` (`U+0061`–`U+007A`) | `0` to `25` in alphabetical order |
1051
+ | `0` to `9` (`U+0030`–`U+0039`) | `27`, the digit symbol |
1052
+ | any other code point below `U+0080` | `26`, the separator symbol |
1053
+ | any code point from `U+0080` up | `c` itself |
1054
+
1055
+ Step 4 puts all ASCII digits in one bucket and all ASCII spaces and
1056
+ punctuation in another, and keeps every other character distinct: `ß`,
1057
+ `ø`, and `ł` and every letter of a non-Latin script are symbols of their
1058
+ own, same as in the folded form. Symbols `0` to `27` are the **Latin
1059
+ symbols**; every other symbol is at least `128`.
1060
+
1061
+ Both reductions are lossy, but one code point never becomes more than one
1062
+ folded character, and one folded character never becomes more than one
1063
+ symbol, so any text that contains another as a substring contains it as a
1064
+ substring after folding, and after taking symbols, too
1065
+ ([Section B.9](#b9-query-procedure)). Step 4's bucketing means the symbol
1066
+ sequence is a *further*, coarser folding of the folded form -- so a
1067
+ comparison against the folded form alone (Sections B.5, B.6) is stricter
1068
+ than, and therefore safe to confirm trigram-index candidates with, a
1069
+ comparison against the symbol sequence ([Section B.9](#b9-query-procedure)
1070
+ explains why that direction is the one that matters). Neither stage uses
1071
+ any Unicode character property beyond normalization and case conversion,
1072
+ so that a writer and a reader built against different versions of the
1073
+ Unicode Character Database fold the same text the same way. Writers and
1074
+ readers MUST use exactly this procedure for both the folded form and the
1075
+ symbol sequence.
1076
+
1077
+ ### B.3. Trigrams and Lists
1078
+
1079
+ A **trigram** is a run of three consecutive symbols. The trigram set of
1080
+ a place is the union, over every field of its name entry
1081
+ ([Section 7.4](#74-name-entries)), of the trigrams of that field's
1082
+ symbol sequence. Each field is folded separately; no trigram spans the
1083
+ `|` between two fields. A field shorter than three symbols contributes
1084
+ nothing.
1085
+
1086
+ The index has one list per possible **list index**. There are
1087
+
1088
+ ```
1089
+ L = 28^3 + 2^hash_exp = 21952 + 2^hash_exp
1090
+ ```
1091
+
1092
+ list indices. A trigram `(s0, s1, s2)` whose symbols are all Latin has
1093
+ the list index
1094
+
1095
+ ```
1096
+ l = s0 × 784 + s1 × 28 + s2 (0 <= l < 21952)
1097
+ ```
1098
+
1099
+ so every Latin trigram has a list of its own. Any other trigram is
1100
+ hashed into one of `2^hash_exp` shared lists: let `bytes` be the nine
1101
+ bytes formed by writing `s0`, `s1`, and `s2` each as a `u24`
1102
+ ([Section 3.1](#31-fixed-width-integers)), let `h` be the 32-bit
1103
+ FNV-1a hash [FNV] of `bytes`, and fold `h` to `hash_exp` bits:
1104
+
1105
+ ```
1106
+ h = 0x811c9dc5
1107
+ for each byte b of bytes, in order:
1108
+ h = h xor b
1109
+ h = (h × 0x01000193) mod 2^32
1110
+ l = 21952 + (((h >> hash_exp) xor h) mod 2^hash_exp)
1111
+ ```
1112
+
1113
+ Symbols are code points or smaller, so each fits in a `u24`. Test
1114
+ vectors for the 32-bit hash: the empty input hashes to `811c9dc5`, the
1115
+ single byte `a` to `e40c292c`, and the ASCII text `foobar` to
1116
+ `bf9cf968`. Some trigram list indices with `hash_exp = 15`:
1117
+
1118
+ | Text | Trigram | `bytes` | `h` | `l` |
1119
+ |----------|----------------------------|------------------------------|------------|---------|
1120
+ | `łód` | `(0x142, 14, 3)` | `42 01 00 0e 00 00 03 00 00` | `0702848f` | `24650` |
1121
+ | `東京都` | `(0x6771, 0x4eac, 0x90fd)` | `71 67 00 ac 4e 00 fd 90 00` | `574bcbf2` | `47909` |
1122
+ | `straße` | `(0, 0xdf, 4)`, from `aße` | `00 00 00 df 00 00 04 00 00` | `e5f8c954` | `22629` |
1123
+
1124
+ List `l` holds every place whose trigram set contains a trigram with
1125
+ list index `l`. Two hashed trigrams may share a list; that only admits
1126
+ more candidates, never fewer.
1127
+
1128
+ ### B.4. List Encoding
1129
+
1130
+ Each list is a strictly increasing set of place IDs, stored in one of
1131
+ two encodings:
1132
+
1133
+ - **ID list** (encoding `0`): the IDs as a delta-coded list
1134
+ ([Section 3.3](#33-delta-coded-lists)), with no count. The list ends
1135
+ where its stored length ends.
1136
+ - **Bitmap** (encoding `1`): `ceil(N / 8)` bytes, one bit per place of
1137
+ the source, where `N` is the number of places. The bit of place `id`
1138
+ is in byte `id >> 3`, at bit position `id & 7`, least significant bit
1139
+ first. Bits for positions `N` and beyond in the last byte MUST be `0`.
1140
+
1141
+ Writers MUST use the ID list when its encoded length is at most
1142
+ `ceil(N / 8)`, and the bitmap otherwise. The choice is then fixed by the
1143
+ set, which keeps the file a pure function of its source. A list that
1144
+ would be empty is not stored at all ([Section B.7](#b7-file-layout)).
1145
+
1146
+ ### B.5. Relevance Order
1147
+
1148
+ A reader that returns search results ordered by relevance, rather than
1149
+ by ID, SHOULD use this order or the area order of
1150
+ [Section B.5.1](#b51-area-order), and the short-query table
1151
+ ([Section B.6](#b6-short-query-table)) MUST be built with the one its
1152
+ `relevance_rule` names ([Section B.7](#b7-file-layout)).
1153
+
1154
+ A place **matches** a query when the folded form
1155
+ ([Section B.2](#b2-symbol-folding)) of at least one field of its name
1156
+ entry contains the folded form of the query as a substring. Folding
1157
+ before comparing means a query need not be typed with the right
1158
+ diacritics: `zurich` matches a field spelled `Zürich`.
1159
+
1160
+ The **tier** of a field that contains the query, against the folded
1161
+ form of both, is:
1162
+
1163
+ - `40` when the field's folded form equals the query's;
1164
+ - otherwise `20` when the query's **tokens** occur in the field's tokens
1165
+ as a contiguous run, where the tokens of a text are its folded form
1166
+ split at every code point whose General Category is not a letter (`L*`)
1167
+ or a number (`N*`), with empty tokens dropped;
1168
+ - otherwise `10`.
1169
+
1170
+ A field that does not contain the query folded has tier `0` and, with
1171
+ it, **quality** `0`: nothing below distinguishes a non-match. A field
1172
+ that does contain it gets `1` added to its folded tier -- an **exact
1173
+ spelling bonus** -- when the *unfolded* comparison (plain lowercase, no
1174
+ decomposition or diacritic stripping) reaches that same tier against the
1175
+ *unfolded* query, i.e. when folding was not actually needed to reach it.
1176
+ The unfolded comparison can never reach a *better* tier than the folded
1177
+ one -- folding only ever adds matches -- so "reaches at least the same
1178
+ tier" is exactly "the query's own spelling, as given, was already
1179
+ enough". Unlike the folded tier itself, and unlike
1180
+ [Section B.9](#b9-query-procedure)'s confirmation step, which compares
1181
+ folded forms, the bonus is evaluated with no normalization beyond
1182
+ lowercasing: it decides only how well a field matches, never whether it
1183
+ matches at all, so it can be stricter than the comparison the index's
1184
+ guarantee rests on without ever losing a match. A field's **quality** is
1185
+ its folded tier plus this bonus, so quality is one of `0`, `10`, `11`,
1186
+ `20`, `21`, `40`, `41`.
1187
+
1188
+ The quality of a place is the highest quality of its fields, and its
1189
+ **score** is its quality plus a level bonus: `3` for a country, `2` for
1190
+ a city, `1` for a region. Results are ordered by descending score,
1191
+ places with equal scores by descending context quality
1192
+ ([Section B.13](#b13-qualified-queries)), which is `0` for a query
1193
+ without contexts, and then by ascending ID.
1194
+
1195
+ The level bonus only ever breaks a tie between two places of equal
1196
+ quality: its spread (`3 - 1 = 2`) is kept well under the gap between any
1197
+ two quality values that differ in folded tier (`11` to `20` is `9`, `21`
1198
+ to `40` is `19`), so a worse tier can never outscore a better one no
1199
+ matter which levels are involved, and the exact spelling bonus can never
1200
+ promote a field past the next tier either. A country outranks a
1201
+ same-quality city or region specifically because a query that exactly
1202
+ names a country is a rarer, more deliberate kind of search than one
1203
+ that merely happens to match one of the many more numerous cities or
1204
+ regions sharing the text -- a plain reversal of city and region was
1205
+ deliberately not used instead, since it would equally rank, say, a
1206
+ *province* named Antwerpen above the *city* of Antwerp on an identical
1207
+ exact match, which is backwards for the far more common case of
1208
+ searching a city's own name; a city therefore still outranks a region of
1209
+ equal quality, exactly as if countries did not exist in the formula.
1210
+
1211
+ For example, the query `amsterdam` gives the city `Amsterdam` quality
1212
+ `41` (folded tier `40`, an exact match that needed no folding) and score
1213
+ `43`, and the city `Nieuw-Amsterdam` quality `21` (folded tier `20`; its
1214
+ tokens are `nieuw`, `amsterdam`) and score `23`. The query `belgie`
1215
+ (typed without the diaeresis) gives the country `België` folded tier
1216
+ `40` (an exact match once both are folded to `belgie`) but quality `40`,
1217
+ not `41`: the unfolded comparison, `belgië` against `belgie`, does not
1218
+ reach an exact match (or even a substring match) on its own, so the
1219
+ spelling bonus does not apply; querying `belgië` instead reaches quality
1220
+ `41`. The query ` amsterdam` with a leading space matches neither: token
1221
+ matching only ranks places that already match.
1222
+
1223
+ Only the ranking depends on Unicode character properties, never whether
1224
+ a place matches, so a reader and writer that disagree about a character's
1225
+ category can disagree about order but not about the set of results.
1226
+
1227
+ #### B.5.1. Area Order
1228
+
1229
+ Among places of equal score, the order above falls back to IDs, which
1230
+ carry no meaning: the query `berlin` would list a dozen towns in the
1231
+ United States before the city-state of Berlin. A reader that knows the
1232
+ area of every place -- the area bytes of
1233
+ [Section C.3](#c3-representative-cell), which a place location index
1234
+ stores -- MAY instead order results by area, so that among equally good
1235
+ matches a large place comes first. Whether to is the application's
1236
+ choice, and a reader SHOULD let it make it. A reader MUST NOT use this
1237
+ order without the areas, since a reader that has them and one that does
1238
+ not would then disagree.
1239
+
1240
+ The **rank area** of a country or a city is its area byte. The rank
1241
+ area of a region is also its area byte, unless one or more of its cities
1242
+ has a local name (field `0`, [Section 7.4](#74-name-entries)) whose
1243
+ folded form ([Section B.2](#b2-symbol-folding)) equals that of the
1244
+ region's own local name: then it is the largest area byte among those
1245
+ cities. A province would otherwise come before its own capital of the
1246
+ same name (the province of Utrecht before the city of Utrecht) for no
1247
+ reason but its size, which Section B.5's level bonus exists to prevent;
1248
+ with this rule it comes right after it, while a city-state with no city
1249
+ of its own name (Berlin, Vienna) still ranks by its own area.
1250
+
1251
+ In area order, places are compared on the following keys in turn, each
1252
+ one only deciding between places equal on all keys before it:
1253
+
1254
+ 1. the folded tier of their quality ([Section B.5](#b5-relevance-order),
1255
+ quality without the exact spelling bonus), higher first;
1256
+ 2. their context quality ([Section B.13](#b13-qualified-queries)),
1257
+ higher first;
1258
+ 3. countries before regions and cities;
1259
+ 4. their rank area, higher first;
1260
+ 5. the exact spelling bonus, present first;
1261
+ 6. cities before regions;
1262
+ 7. their ID, lower first.
1263
+
1264
+ The exact spelling bonus comes after the area, unlike in Section B.5's
1265
+ score, or the query `zurich` would rank a village spelled `Zurich` ahead
1266
+ of `Zürich` whatever their sizes. Countries keep their precedence for
1267
+ the reason Section B.5 gives.
1268
+
1269
+ For example, the query `dam` against the file of
1270
+ [Appendix A](#appendix-a-worked-example) matches `Amsterdam` and
1271
+ `Zaandam`, both with quality `11` and score `13`. Section B.5 orders them
1272
+ by ID, `Amsterdam` first. In area order `Zaandam`, with area byte `5`
1273
+ ([Section C.8](#c8-worked-example)), comes first, and `Amsterdam`, which
1274
+ has no cells and so area byte `0`, second.
1275
+
1276
+ Area order matches the same places as Section B.5 and only orders them
1277
+ differently.
1278
+
1279
+ ### B.6. Short-Query Table
1280
+
1281
+ A query of one or two characters has no trigram, and matches so many
1282
+ places that comparing every name is the only way to rank them. The
1283
+ short-query table instead stores, for every such query that has any
1284
+ match, the first `top_count` places in relevance order
1285
+ ([Section B.5](#b5-relevance-order)).
1286
+
1287
+ The table's **keys** are every string of one or two consecutive code
1288
+ points that occurs in the folded form ([Section B.2](#b2-symbol-folding))
1289
+ of some field of some name entry. The table MUST contain every key and
1290
+ no other. The **entry** of a key is the list of the places that match
1291
+ it, in the relevance order the file's `relevance_rule` names
1292
+ ([Section B.5](#b5-relevance-order) or
1293
+ [Section B.5.1](#b51-area-order), scoring the key itself as the query),
1294
+ truncated to its first `top_count` places. A
1295
+ key always has at least one match, so an entry is never empty; it has
1296
+ fewer than `top_count` places only when fewer places match. A query
1297
+ whose folded form is one or two code points long and is not a key
1298
+ matches no place at all.
1299
+
1300
+ Because a key is already in folded form, scoring it as a query can never
1301
+ need Section B.5's exact spelling bonus to be anything other than
1302
+ whatever the key's own, already-folded spelling gives it -- a key never
1303
+ carries a diacritic of its own to fold away. A *query* that does,
1304
+ though, may need a different bonus than its key would: `e` and `é` both
1305
+ fold to the key `e`, but a field spelled with an actual `é` reaches the
1306
+ exact spelling bonus against the query `é` and not against the query
1307
+ `e`, which the table cannot represent with one entry per key. A reader
1308
+ therefore MUST NOT answer a query from this table unless the query's own
1309
+ folded form already equals its unfolded (plain lowercase) form, i.e. the
1310
+ query itself has no diacritic to fold -- see [Section B.9](#b9-query-procedure)'s
1311
+ step 1.
1312
+
1313
+ Entries are sorted by the UTF-8 bytes of their key, compared bytewise
1314
+ (which equals comparing code points), and grouped into **table blocks**
1315
+ of `2^top_block_exp` consecutive entries; the last block MAY be shorter.
1316
+ A table block is the concatenation of its entries, each stored as:
1317
+
1318
+ | Field | Type | Description |
1319
+ |---------|---------------|-------------|
1320
+ | `key` | bytes, `NUL` | The key in UTF-8, followed by a `NUL` byte. |
1321
+ | `count` | varint | The number of places in the entry, from `1` to `top_count`. |
1322
+ | `ids` | `count` varints | The places, in relevance order. Not delta-coded: relevance order is not ID order. |
1323
+
1324
+ The **table directory** lists, for each block, the key of its first
1325
+ entry, so that a reader finds the block of a key by binary search over
1326
+ the directory and then reads that block alone:
1327
+
1328
+ | Field | Type | Description |
1329
+ |-----------------|---------------|-------------|
1330
+ | `key_count` | varint | `Q`, the number of keys. There are `ceil(Q / 2^top_block_exp)` blocks. |
1331
+ | for each block: `first_key` | bytes, `NUL` | The key of the block's first entry, in UTF-8, followed by `NUL`. |
1332
+ | for each block: `block_length` | varint | The byte length of the block. |
1333
+
1334
+ Blocks are stored consecutively in block order, so block `b` starts at
1335
+ the sum of the lengths of blocks `0` to `b - 1`.
1336
+
1337
+ ### B.7. File Layout
1338
+
1339
+ The file is a header followed by four sections, with no padding and no
1340
+ trailer.
1341
+
1342
+ | Field | Type | Description |
1343
+ |-------------------------|---------|-------------|
1344
+ | `signature` | 8 bytes | The ASCII bytes `CGEODATA`, as in [Section 5](#5-header). |
1345
+ | `version` | varint | Kind and version as defined in Section 5. This appendix specifies kind `1`, version `3`, so the value is `19` (`13`). Version `2` had no `relevance_rule` field, its short-query table always in Section B.5's order; a reader MUST reject it. Version `1` built the short-query table from the unfolded lowercase form and a different level bonus ordering (city highest, not country); a version-1 file is not just an older layout but a different, incompatible relevance order, and nothing in its bytes records which rule built it, since `source_checksum` covers only the place table, not the relevance rule -- a reader MUST reject it rather than answer queries with it. |
1346
+ | `source_checksum` | u64 | Checksum of the source file's place table ([Section B.8](#b8-source-checksum)). |
1347
+ | `place_count` | varint | `N`, the number of places in the source. |
1348
+ | `hash_exp` | u8 | The number of hashed lists is `2^hash_exp` ([Section B.3](#b3-trigrams-and-lists)). MUST be from `1` to `24`. |
1349
+ | `top_count` | u8 | The number of places per short-query entry ([Section B.6](#b6-short-query-table)). `0` means the file has no short-query table. |
1350
+ | `top_block_exp` | u8 | Each table block holds `2^top_block_exp` entries. MUST be at most `24`. |
1351
+ | `relevance_rule` | u8 | The order of the short-query table ([Section B.6](#b6-short-query-table)): `0` for [Section B.5](#b5-relevance-order)'s, `1` for the area order of [Section B.5.1](#b51-area-order). Other values are reserved. Also decides what `source_checksum` covers ([Section B.8](#b8-source-checksum)). |
1352
+ | `list_directory_length` | varint | Byte length of `list_directory`. |
1353
+ | `lists_length` | varint | Byte length of `lists`. |
1354
+ | `table_directory_length`| varint | Byte length of `table_directory`. |
1355
+ | `list_directory` | bytes | See below. |
1356
+ | `lists` | bytes | The stored lists, in increasing list index, each in its encoding ([Section B.4](#b4-list-encoding)). |
1357
+ | `table_directory` | bytes | The table directory of Section B.6. Empty when `top_count` is `0`. |
1358
+ | `table_blocks` | bytes | The table blocks of Section B.6, in block order. They run to the end of the file. Empty when `top_count` is `0`. |
1359
+
1360
+ The list directory records which lists are stored and how long each
1361
+ one is:
1362
+
1363
+ | Field | Type | Description |
1364
+ |------------|-----------------------|-------------|
1365
+ | `presence` | `ceil(L / 8)` bytes | Bit `l` is `1` if list `l` is stored, that is, if it is not empty. Bit `l` is in byte `l >> 3`, at bit position `l & 7`, least significant bit first. Bits for `L` and beyond in the last byte MUST be `0`. |
1366
+ | `entries` | one varint per stored list | In increasing list index: `length × 2 + encoding`, where `length` is the byte length of the stored list and `encoding` is `0` for an ID list and `1` for a bitmap. |
1367
+
1368
+ List `l`, if stored, starts at the sum of the lengths of the stored
1369
+ lists with a smaller index. A reader computes all offsets with one pass
1370
+ over `entries`. A list that is not stored is empty: no place has a
1371
+ trigram with that list index.
1372
+
1373
+ Version `0` of kind `1` (value `16`) was an earlier bigram layout of
1374
+ this index. Readers MUST reject it.
1375
+
1376
+ ### B.8. Source Checksum
1377
+
1378
+ `source_checksum` binds an index to the place table it was built from.
1379
+ It is the 64-bit FNV-1a hash [FNV] of exactly the `place_table_length`
1380
+ bytes of the source file's place table ([Section 7.2](#72-layout)),
1381
+ starting at the first byte after `chunk_lengths`
1382
+ ([Section 6.2](#62-layout)):
1383
+
1384
+ ```
1385
+ h = 0xcbf29ce484222325
1386
+ for each byte b of the place table, in file order:
1387
+ h = h xor b
1388
+ h = (h × 0x100000001b3) mod 2^64
1389
+ ```
1390
+
1391
+ The result is stored as a `u64`, least significant byte first. Test
1392
+ vectors: the empty input hashes to `cbf29ce484222325`, the single byte
1393
+ `a` to `af63dc4c8601ec8c`, and the ASCII text `foobar` to
1394
+ `85944171f73967e8`.
1395
+
1396
+ Only the place table is covered, not the whole source file, for two
1397
+ reasons. The index depends on nothing else, so a source rebuilt with
1398
+ different chunk parameters, or with some chunks left empty, keeps the
1399
+ same index valid. And verifying the checksum then reads only the place
1400
+ table, which stays small (about 2 MiB for the worldwide file at the
1401
+ recommended `keyframe_exp` of [Section 7.3](#73-name-blocks)), rather
1402
+ than every chunk.
1403
+
1404
+ A short-query table in area order (`relevance_rule = 1`) depends on the
1405
+ areas of [Section C.3](#c3-representative-cell) as well, which depend on
1406
+ the chunks. For such an index, `source_checksum` is instead the checksum
1407
+ of [Section C.4](#c4-file-layout): of the source file from its first
1408
+ byte up to the first chunk, which covers the chunk lengths.
1409
+
1410
+ Readers SHOULD compute the checksum of the source's place table (or
1411
+ prefix) when they load an index, and a reader that does so MUST reject
1412
+ an index whose `source_checksum` differs. A reader MAY skip the check where the
1413
+ build pipeline guarantees the pairing, but it then has no protection
1414
+ against a stale index, which silently misses places whose names were
1415
+ added or changed after the index was built. The checksum detects
1416
+ accidental mismatches; it is not a defence against a deliberately
1417
+ crafted index ([Section B.10](#b10-robustness)).
1418
+
1419
+ FNV-1a was chosen over a cryptographic hash because it is a dozen lines
1420
+ of integer arithmetic in any language and needs no library. In
1421
+ JavaScript, where 64-bit integers are not native, the multiplication is
1422
+ convenient to implement on two 32-bit halves: because the prime is
1423
+ `2^40 + 0x1b3`, `h × prime` is `(h << 40) + h × 0x1b3`, and `h << 40`
1424
+ only ever contributes `low32(h) << 8` to the high half.
1425
+
1426
+ ### B.9. Query Procedure
1427
+
1428
+ To search for a query text:
1429
+
1430
+ 1. **Short queries.** Let `q` be the folded form of the query
1431
+ ([Section B.2](#b2-symbol-folding)). If `q` is one or two code points
1432
+ long, the search asks for results in the relevance order that
1433
+ `relevance_rule` names, with a limit of at most `top_count` and no
1434
+ restriction by level or parent, `top_count` is not `0`, the query
1435
+ has no comma ([Section B.13](#b13-qualified-queries)), and `q`
1436
+ already equals the plain lowercase form of the query (i.e. the query
1437
+ has no diacritic of its own to fold --
1438
+ [Section B.6](#b6-short-query-table) explains why this matters), then
1439
+ look `q` up in the short-query table: find the last block whose
1440
+ `first_key` is at most `q`, read that block, and scan it for `q`. The
1441
+ result is the first `limit` places of its entry, or nothing if `q` is
1442
+ not a key. Stop.
1443
+ 2. **Fold the query** further, with step 4 of
1444
+ [Section B.2](#b2-symbol-folding), into a symbol sequence
1445
+ `s_0 .. s_{m-1}`. If `m < 3`, the query has no trigram and the lists
1446
+ cannot help: compare every name without the index, and stop.
1447
+ 3. **Collect the distinct list indices** of the query's trigrams
1448
+ ([Section B.3](#b3-trigrams-and-lists)). If any of them is not
1449
+ stored, no place can match: the result is empty.
1450
+ 4. **Intersect the lists.** The candidates are the places present in
1451
+ every one of them. A reader SHOULD start from the shortest stored
1452
+ list and MAY stop as soon as the intersection is empty. An ID list
1453
+ is intersected by merging two sorted sequences; a bitmap by testing
1454
+ one bit per remaining candidate.
1455
+ 5. **Confirm each candidate** by comparing its names with the query, as
1456
+ the reader would without the index, and report the confirmed ones.
1457
+ Candidates come out in increasing ID order, which is the order of a
1458
+ search without relevance ranking.
1459
+
1460
+ A qualified query ([Section B.13](#b13-qualified-queries)) takes steps 2
1461
+ to 4 once for the head of each of its readings: the candidates of the
1462
+ query are the union of the candidates of its heads, and if any head has
1463
+ no trigram, every name is compared.
1464
+
1465
+ The lists never lose a match, under the following condition. Say a
1466
+ reader matches a name against a query when the name's symbol sequence
1467
+ contains the query's symbol sequence as a (contiguous) subsequence --
1468
+ the comparison the trigram lists are actually built from. Then a
1469
+ matching name contains every trigram of the query's symbol sequence, so
1470
+ the place is present in every list of step 4 and is examined in step 5.
1471
+ A reader whose comparison is stricter than this inherits the guarantee,
1472
+ since everything it accepts is also accepted by the symbol-sequence
1473
+ comparison. The folded-form substring match of
1474
+ [Section B.5](#b5-relevance-order) is one such stricter comparison: step
1475
+ 4 of Section B.2 maps each folded-form code point to one symbol, so a
1476
+ folded-form substring relationship survives into the symbol sequence
1477
+ unchanged, the same argument Section B.2 makes for why neither folding
1478
+ stage ever loses a match the other would find. A reader whose comparison
1479
+ is looser, for example one that tolerates a missing or transposed
1480
+ character, or that matches across the `|` between fields, does not
1481
+ inherit the guarantee, and MUST NOT use the index for such queries. In
1482
+ particular, a reader that ranks by tokens MUST only rank places that
1483
+ match as a folded substring, as Section B.5 requires; ranking a place
1484
+ whose tokens match but whose names do not contain the query as a folded
1485
+ substring would make results depend on the index. The exotic cases in
1486
+ which lowercasing and decomposition do not commute are not known to
1487
+ affect any place name.
1488
+
1489
+ The short-query table of step 1 is exact rather than an
1490
+ over-approximation: its entries are the answer. A reader SHOULD still
1491
+ confirm that each place of an entry matches `q`, which costs at most
1492
+ `top_count` name lookups, and drop any that do not.
1493
+
1494
+ ### B.10. Robustness
1495
+
1496
+ A search index file may come from an untrusted source, or may be stale.
1497
+ Readers MUST NOT assume that it is well formed or that it matches its
1498
+ source.
1499
+
1500
+ - Readers MUST verify the signature, kind, and version before
1501
+ interpreting any other field, and MUST reject `hash_exp` outside `1`
1502
+ to `24`, `top_block_exp` above `24`, and a `relevance_rule` other than
1503
+ `0` or `1`.
1504
+ - Readers MUST check that the header's section lengths add up to at
1505
+ most the file length, and that `list_directory_length` equals
1506
+ `ceil(L / 8)` plus the length of `entries`, where the number of
1507
+ entries is the number of `1` bits in `presence`.
1508
+ - Readers MUST check that the lengths in `entries` add up to
1509
+ `lists_length`, that every bitmap is exactly `ceil(N / 8)` bytes, and
1510
+ that every block length in the table directory lies within
1511
+ `table_blocks`.
1512
+ - Readers MUST check that `place_count` equals `N`, the number of places
1513
+ in the source, and MUST reject any place ID in a list or table entry
1514
+ that is not less than `N`, and any ID list that is not strictly
1515
+ increasing.
1516
+ - Readers MUST confirm every trigram candidate by comparing its names.
1517
+ Given this, a malformed or mismatched list can cause missed results
1518
+ and wasted work, but never a wrong result. A malformed short-query
1519
+ table can reorder or omit results; confirming its places, as
1520
+ Section B.9 recommends, keeps it from adding a wrong one.
1521
+ - Nothing in the file is compressed, so there is no decompression to
1522
+ bound. The allocations a query needs are bounded by the lengths of the
1523
+ sections and lists it reads, which are bounded by the file length.
1524
+
1525
+ ### B.11. Choosing Parameters
1526
+
1527
+ This section is informative. The measurements were taken on the
1528
+ worldwide file (239 countries, 5,469 regions, 313,732 cities), with a
1529
+ reader that opens the source afresh for every query, as a search box
1530
+ queried only occasionally would: nothing is cached except by the
1531
+ operating system. Each query asks for the top five places in relevance
1532
+ order and resolves them to their parents.
1533
+
1534
+ **Hashed lists.** With `hash_exp = 15` the file holds 32,768 hashed
1535
+ lists beside the 21,952 Latin ones; the presence bitmap is 6.7 KiB, and
1536
+ a query of a script other than Latin leaves about as few candidates as
1537
+ a Latin one of the same length. With the 28-symbol folding of the
1538
+ earlier bigram version, which mapped every non-Latin letter to the
1539
+ separator, a three-letter Cyrillic, Arabic, or Chinese query left every
1540
+ place with a non-Latin name, over 36,000 cities, as candidates.
1541
+ `hash_exp = 15` is RECOMMENDED.
1542
+
1543
+ **Size.** The worldwide index is 4.2 MiB: 3.8 MiB of lists (36,317
1544
+ stored), a 49 KiB list directory, and 362 KiB of short-query table.
1545
+ Every trigram list turned out smaller as an ID list than as a bitmap:
1546
+ an ID list costs about a byte per place, so a bitmap only wins for a
1547
+ trigram shared by more than about one place in eight, which the
1548
+ worldwide data does not have, but a small file (such as the example
1549
+ below) does. The index is built in under three seconds.
1550
+
1551
+ **Speed.** How long a query takes depends far more on the source's
1552
+ `keyframe_exp` ([Section 7.3](#73-name-blocks)) than on the index,
1553
+ because confirming a candidate means inflating the name block it is in.
1554
+ Mean times for random substrings of real city names, with and without
1555
+ the index; every query opens the source and the index afresh, including
1556
+ verifying the checksum (about 10 ms of each indexed query):
1557
+
1558
+ | `keyframe_exp` | Index | 1 character | 2 characters | 3 characters | 5 characters | 8 characters | 95th percentile |
1559
+ |----------------|-------|-------------|--------------|--------------|--------------|--------------|-----------------|
1560
+ | `13` | yes | 15 ms | 13 ms | 67 ms | 24 ms | 15 ms | 88 ms |
1561
+ | `13` | no | 155 ms | 127 ms | 124 ms | 123 ms | 122 ms | 169 ms |
1562
+ | `7` | yes | 11 ms | 10 ms | 26 ms | 13 ms | 12 ms | 34 ms |
1563
+ | `7` | no | 181 ms | 141 ms | 138 ms | 136 ms | 135 ms | 200 ms |
1564
+
1565
+ The one- and two-character rows are answered by the short-query table.
1566
+ Intersecting the lists takes well under a millisecond in every case; the
1567
+ rest of a three-character query is confirming its candidates, of which
1568
+ there are thousands for the commonest trigrams.
1569
+
1570
+ **Short-query table.** With `top_count = 5` the worldwide table has
1571
+ 25,497 keys and takes 365 KiB. It replaces the whole-table
1572
+ scan that one- and two-character queries otherwise need, for the
1573
+ common case of a search box that shows the top few matches.
1574
+ `top_count = 5` and `top_block_exp = 6` (blocks of 64 keys, roughly
1575
+ 1 KiB each) are RECOMMENDED. A reader asked for more results, or for
1576
+ results restricted by level or parent, falls back to the scan for these
1577
+ queries.
1578
+
1579
+ **Why trigrams.** Bigrams (the earlier version of this index) are
1580
+ common enough that a query leaves several times more candidates than
1581
+ with trigrams, and with bigrams pooled into groups of places to keep
1582
+ the file small, more again: for Latin queries of three or more
1583
+ characters with on average 530 true matches among the cities, the
1584
+ earlier layout left 3,500 cities as candidates, where trigrams leave
1585
+ 650. Longer n-grams would narrow queries of four or more characters
1586
+ further, but these are already fast, and would serve no query of
1587
+ three.
1588
+
1589
+ ### B.12. Worked Example
1590
+
1591
+ This example indexes the source file of
1592
+ [Appendix A](#appendix-a-worked-example) with `hash_exp = 4`,
1593
+ `top_count = 2`, and `top_block_exp = 4`: small parameters, so that
1594
+ the example is short but still has several table blocks. Its
1595
+ short-query table is in Section B.5's order (`relevance_rule = 0`). The
1596
+ seven places and their name fields are:
1597
+
1598
+ | ID | Level | Fields |
1599
+ |----|---------|-------------------------------|
1600
+ | 0 | country | `Nederland`, `The Netherlands` |
1601
+ | 1 | country | `België`, `Belgium` |
1602
+ | 2 | region | `Noord-Holland` |
1603
+ | 3 | region | `Antwerpen`, `Antwerp` |
1604
+ | 4 | city | `Amsterdam` |
1605
+ | 5 | city | `Zaandam` |
1606
+ | 6 | city | `Antwerpen`, `Antwerp` |
1607
+
1608
+ `L = 21952 + 2^4 = 21968`, so `presence` is `2746` bytes. `N = 7`, so a
1609
+ bitmap is `1` byte, and any list of two or more places is stored as a
1610
+ bitmap, since its ID list would take two bytes or more. The file is
1611
+ `3202` bytes: a 26-byte header, a `2793`-byte list directory, `47` bytes
1612
+ of lists, a `16`-byte table directory, and `320` bytes of table blocks.
1613
+
1614
+ #### B.12.1. Header (26 bytes)
1615
+
1616
+ ```
1617
+ 43 47 45 4f 44 41 54 41 signature "CGEODATA"
1618
+ 13 version 19 -> kind 1 (text search index), version 3
1619
+ 9f e6 00 b9 37 18 dc 77 source_checksum 0x77dc1837b900e69f
1620
+ 07 place_count 7
1621
+ 04 hash_exp 4 -> 16 hashed lists, L = 21968
1622
+ 02 top_count 2
1623
+ 04 top_block_exp 4 -> 16 entries per table block
1624
+ 00 relevance_rule 0 -> Section B.5's order
1625
+ e9 15 list_directory_length 2793
1626
+ 2f lists_length 47
1627
+ 10 table_directory_length 16
1628
+ ```
1629
+
1630
+ The checksum is the FNV-1a 64 hash of the 109 bytes of the place table
1631
+ in [Section A.3](#a3-place-table-109-bytes), that is the bytes at file
1632
+ offsets `22` to `130` of the Appendix A file, from the `02` of
1633
+ `country_count` to the `6c` that ends block `1`.
1634
+
1635
+ #### B.12.2. Lists
1636
+
1637
+ Every name folds to Latin symbols only (`België` becomes `belgie`), so
1638
+ no hashed list is used. The 47 stored lists, in list index order, are
1639
+ below; `-` stands for the separator symbol `26`. "Bit" is the `presence`
1640
+ byte and bit that mark the list as stored, "Entry" is its varint in
1641
+ `entries`, and "Stored" is the list itself.
1642
+
1643
+ | Trigram | `l` | Bit | Entry | Stored | Places |
1644
+ |---------|-------|--------------|-------|-------------|--------|
1645
+ | `aan` | 13 | 1: `20` | `02` | IDs `05` | 5 |
1646
+ | `ams` | 354 | 44: `04` | `02` | IDs `04` | 4 |
1647
+ | `and` | 367 | 45: `80` | `03` | bitmap `25` | 0, 2, 5 |
1648
+ | `ant` | 383 | 47: `80` | `03` | bitmap `48` | 3, 6 |
1649
+ | `bel` | 907 | 113: `08` | `02` | IDs `01` | 1 |
1650
+ | `dam` | 2364 | 295: `10` | `03` | bitmap `30` | 4, 5 |
1651
+ | `der` | 2481 | 310: `02` | `02` | IDs `00` | 0 |
1652
+ | `d-h` | 3087 | 385: `80` | `02` | IDs `02` | 2 |
1653
+ | `ede` | 3224 | 403: `01` | `02` | IDs `00` | 0 |
1654
+ | `elg` | 3450 | 431: `04` | `02` | IDs `01` | 1 |
1655
+ | `erd` | 3615 | 451: `80` | `02` | IDs `04` | 4 |
1656
+ | `erl` | 3623 | 452: `80` | `02` | IDs `00` | 0 |
1657
+ | `erp` | 3627 | 453: `08` | `03` | bitmap `48` | 3, 6 |
1658
+ | `eth` | 3675 | 459: `08` | `02` | IDs `00` | 0 |
1659
+ | `e-n` | 3877 | 484: `20` | `02` | IDs `00` | 0 |
1660
+ | `gie` | 4932 | 616: `10` | `02` | IDs `01` | 1 |
1661
+ | `giu` | 4948 | 618: `10` | `02` | IDs `01` | 1 |
1662
+ | `her` | 5617 | 702: `02` | `02` | IDs `00` | 0 |
1663
+ | `he-` | 5626 | 703: `04` | `02` | IDs `00` | 0 |
1664
+ | `hol` | 5891 | 736: `08` | `02` | IDs `02` | 2 |
1665
+ | `ium` | 6844 | 855: `10` | `02` | IDs `01` | 1 |
1666
+ | `lan` | 8637 | 1079: `20` | `03` | bitmap `05` | 0, 2 |
1667
+ | `lgi` | 8800 | 1100: `01` | `02` | IDs `01` | 1 |
1668
+ | `lla` | 8932 | 1116: `10` | `02` | IDs `02` | 2 |
1669
+ | `mst` | 9931 | 1241: `08` | `02` | IDs `04` | 4 |
1670
+ | `nda` | 10276 | 1284: `10` | `02` | IDs `05` | 5 |
1671
+ | `nds` | 10294 | 1286: `40` | `02` | IDs `00` | 0 |
1672
+ | `ned` | 10307 | 1288: `08` | `02` | IDs `00` | 0 |
1673
+ | `net` | 10323 | 1290: `08` | `02` | IDs `00` | 0 |
1674
+ | `noo` | 10598 | 1324: `40` | `02` | IDs `02` | 2 |
1675
+ | `ntw` | 10746 | 1343: `04` | `03` | bitmap `48` | 3, 6 |
1676
+ | `oll` | 11295 | 1411: `80` | `02` | IDs `02` | 2 |
1677
+ | `oor` | 11385 | 1423: `02` | `02` | IDs `02` | 2 |
1678
+ | `ord` | 11455 | 1431: `80` | `02` | IDs `02` | 2 |
1679
+ | `pen` | 11885 | 1485: `20` | `03` | bitmap `48` | 3, 6 |
1680
+ | `rda` | 13412 | 1676: `10` | `02` | IDs `04` | 4 |
1681
+ | `rd-` | 13438 | 1679: `40` | `02` | IDs `02` | 2 |
1682
+ | `rla` | 13636 | 1704: `10` | `02` | IDs `00` | 0 |
1683
+ | `rpe` | 13752 | 1719: `01` | `03` | bitmap `48` | 3, 6 |
1684
+ | `ste` | 14648 | 1831: `01` | `02` | IDs `04` | 4 |
1685
+ | `ter` | 15025 | 1878: `02` | `02` | IDs `04` | 4 |
1686
+ | `the` | 15096 | 1887: `01` | `02` | IDs `00` | 0 |
1687
+ | `twe` | 15516 | 1939: `10` | `03` | bitmap `48` | 3, 6 |
1688
+ | `wer` | 17377 | 2172: `02` | `03` | bitmap `48` | 3, 6 |
1689
+ | `zaa` | 19600 | 2450: `01` | `02` | IDs `05` | 5 |
1690
+ | `-ho` | 20594 | 2574: `04` | `02` | IDs `02` | 2 |
1691
+ | `-ne` | 20752 | 2594: `01` | `02` | IDs `00` | 0 |
1692
+
1693
+ All other `presence` bytes are `00`. The entries and the lists are the
1694
+ "Entry" and "Stored" columns concatenated: `entries` is 47 bytes, one
1695
+ varint per list, and `lists` is 47 bytes, since every stored list here
1696
+ is one byte long. The ID list of a single place is its ID, since the
1697
+ first delta is taken from `0`. The place table's `The Netherlands` and
1698
+ `Noord-Holland` contribute the separator trigrams `e-n`, `he-`, `-ne`,
1699
+ `d-h`, `rd-`, and `-ho`.
1700
+
1701
+ #### B.12.3. Short-Query Table
1702
+
1703
+ The names' folded forms contain 63 distinct strings of one or two code
1704
+ points, so the table has four blocks: three of 16 entries and one of
1705
+ 15. The table directory (16 bytes) is:
1706
+
1707
+ ```
1708
+ 3f key_count 63 -> 4 blocks
1709
+ 20 00 50 block 0: first_key " ", block_length 80
1710
+ 65 20 00 51 block 1: first_key "e ", block_length 81
1711
+ 6c 67 00 52 block 2: first_key "lg", block_length 82
1712
+ 72 64 00 4d block 3: first_key "rd", block_length 77
1713
+ ```
1714
+
1715
+ The entries, key followed by its places in relevance order:
1716
+
1717
+ ```
1718
+ block 0 " " 0 " n" 0 "-" 2 "-h" 2 "a" 0 4 "aa" 5
1719
+ "am" 4 5 "an" 0 5 "b" 1 "be" 1 "d" 0 4 "d-" 2
1720
+ "da" 4 5 "de" 0 "ds" 0 "e" 0 1
1721
+ block 1 "e " 0 "ed" 0 "el" 1 "en" 6 3 "er" 0 4 "et" 0
1722
+ "g" 1 "gi" 1 "h" 0 2 "he" 0 "ho" 2 "i" 1
1723
+ "ie" 1 "iu" 1 "l" 0 1 "la" 0 2
1724
+ block 2 "lg" 1 "ll" 2 "m" 1 4 "ms" 4 "n" 0 5 "nd" 0 5
1725
+ "ne" 0 "no" 2 "nt" 6 3 "o" 2 "ol" 2 "oo" 2
1726
+ "or" 2 "p" 6 3 "pe" 6 3 "r" 0 4
1727
+ block 3 "rd" 4 2 "rl" 0 "rp" 6 3 "s" 0 4 "st" 4 "t" 0 4
1728
+ "te" 4 "th" 0 "tw" 6 3 "u" 1 "um" 1 "w" 6 3
1729
+ "we" 6 3 "z" 5 "za" 5
1730
+ ```
1731
+
1732
+ Block 0 is stored as these 80 bytes:
1733
+
1734
+ ```
1735
+ 20 00 01 00 20 6e 00 01 00 2d 00 01 02 2d 68 00 01 02 61 00 02 00 04
1736
+ 61 61 00 01 05 61 6d 00 02 04 05 61 6e 00 02 00 05 62 00 01 01
1737
+ 62 65 00 01 01 64 00 02 00 04 64 2d 00 01 02 64 61 00 02 04 05
1738
+ 64 65 00 01 00 64 73 00 01 00 65 00 02 00 01
1739
+ ```
1740
+
1741
+ Some entries worked through Section B.5 (scores aren't stored, only the
1742
+ ID order they produce, but are given here to show why):
1743
+
1744
+ - `en`: the region `Antwerpen` (ID 3) and the city `Antwerpen` (ID 6)
1745
+ both reach folded tier `10` (a substring match) and need no folding
1746
+ either, so both get the exact spelling bonus, quality `11`; the
1747
+ city's level bonus (`2`) still beats the region's (`1`), giving scores
1748
+ `13` and `12` -- the entry is `6 3`, not in ID order. (Country beating
1749
+ city and region at equal quality is the point of this version's level
1750
+ bonus; city still beats region here exactly as version 1's did, since
1751
+ neither place in this example is a country.)
1752
+ - `h`: the country `Nederland` / `The Netherlands` (ID 0, matching via
1753
+ the `The Netherlands` field) and the region `Noord-Holland` (ID 2)
1754
+ both reach quality `11` the same way `en` does; the country's level
1755
+ bonus (`3`) beats the region's (`1`), giving scores `14` and `12` --
1756
+ the entry is `0 2`. Under version 1's level bonus (city `4`, region
1757
+ `2`, country `1`) this came out the other way, `2 0` -- a region used
1758
+ to outrank a country of equal quality, which this version's bonus
1759
+ ordering specifically fixes.
1760
+ - `ie` and `iu` are both keys of the country `België` / `Belgium` (ID
1761
+ `1`) alone, reached in opposite ways. `ie` exists only in `België`'s
1762
+ folded form (`belgië` does not contain the literal substring `ie`,
1763
+ since its sixth letter is `ë` not `e`, and `belgium` does not contain
1764
+ it either), so it reaches quality `10`, no exact spelling bonus, score
1765
+ `13`; `iu` is a literal substring of `belgium` with no folding
1766
+ involved at all, quality `11`, score `14`. Both entries hold only ID
1767
+ `1`, so the difference isn't visible in the stored bytes, only in how
1768
+ each key was reached.
1769
+ - `nl` is not a key, so a search for `nl` matches nothing, without
1770
+ reading any block beyond the one that would hold it.
1771
+
1772
+ #### B.12.4. Queries
1773
+
1774
+ **`dam`.** Folds to symbols `3, 0, 12`: the one trigram `dam`, list
1775
+ `2364`, a bitmap `30`, places `4` and `5`. Both `Amsterdam` and
1776
+ `Zaandam` contain `dam`, so both are confirmed.
1777
+
1778
+ **`dams`.** Trigrams `dam` (list `2364`, places `4`, `5`) and `ams`
1779
+ (list `354`, place `4`). The intersection is place `4`, `Amsterdam`,
1780
+ whose names do not contain `dams`: it is rejected and the search
1781
+ reports nothing. The lists passed it because a trigram set discards
1782
+ where the trigrams occur: `Amsterdam` has both, just not overlapping as
1783
+ in `dams`.
1784
+
1785
+ **`antwerp`.** Trigrams `ant`, `ntw`, `twe`, `wer`, `erp`, all with
1786
+ bitmap `48` (places `3` and `6`). Both are confirmed, and in relevance
1787
+ order the city (quality `41`, an exact match of the field `Antwerp`
1788
+ needing no folding, score `41 + 2 = 43`) comes before the region
1789
+ (quality `41`, score `41 + 1 = 42`).
1790
+
1791
+ **`belgie`, sorted.** Typed without the diaeresis. Trigrams `bel`,
1792
+ `elg`, `lgi`, `gie`, all list `België`'s country alone (places `1`'s
1793
+ lists -- see B.12.2). `België`'s folded form, `belgie`, equals the
1794
+ query exactly: folded tier `40`. The *unfolded* comparison, `belgië`
1795
+ against `belgie`, does not reach even a substring match (`belgië`
1796
+ does not contain `belgie` literally -- nor does `Belgium`, whose sixth
1797
+ letter is `u`, not `e`), so the exact spelling bonus does not apply:
1798
+ quality stays `40`, score `43`. A reader with no folding at all (version
1799
+ 1, or plain case-insensitive matching) would have found nothing here,
1800
+ since neither field contains `belgie` literally. Querying `belgië`
1801
+ instead (typed correctly) matches `België`'s own field without any
1802
+ folding needed, reaching quality `41`, score `44` -- a query a
1803
+ non-folding reader would also have found, since it's byte-identical to
1804
+ the stored field after lowercasing alone. This reader answers both
1805
+ queries, by folding for the first and plain matching for the second,
1806
+ and ranks the correctly-spelled one slightly higher.
1807
+
1808
+ **`Zaan-dam`.** Folds to `z a a n - d a m`. The trigrams `an-`
1809
+ (list `390`), `n-d` (`10923`), and `-da` (`20468`) are not stored, so
1810
+ nothing can match and no list needs to be read at all: step 3 of
1811
+ Section B.9 ends the search. This is correct, since no name contains
1812
+ `zaan-dam`.
1813
+
1814
+ **`a`, sorted, limit 2.** One code point, and its folded form equals its
1815
+ plain lowercase form (step 1 of Section B.9 applies, per Section B.6's
1816
+ restriction): `a` is in block 0 (the last block whose first key, `" "`,
1817
+ is at most `a`), and its entry is `0 4`, the country `Nederland` (field
1818
+ `Nederland` matches `a` at quality `11`, score `11 + 3 = 14`) ahead of
1819
+ the city `Amsterdam` (quality `11`, score `11 + 2 = 13` -- `Zaandam`
1820
+ ties it but loses the ID tie-break). Under version 1's level bonus this
1821
+ entry was `4 5`, `Amsterdam` and `Zaandam`, since a country was worth
1822
+ less than either city. A search for `a` in ID order, or with a limit
1823
+ above 2, instead compares every name.
1824
+
1825
+ Built in area order instead (`relevance_rule = 1`, from the area bytes
1826
+ of [Section C.8](#c8-worked-example)), the entry of `a` is `0 2`: the
1827
+ country still comes first, then `Noord-Holland`, whose area byte `14`
1828
+ is larger than `Zaandam`'s `5`, while `Amsterdam` has none. That
1829
+ index's `source_checksum` is the one of Section C.8.1,
1830
+ `0xfc0348f51391f7a1`. A reader that ranks by Section B.5's order cannot
1831
+ use that table, and compares every name instead.
1832
+
1833
+ **`ë`, sorted, limit 1.** Its folded form, `e`, differs from its plain
1834
+ lowercase form, `ë` itself, so step 1 does NOT apply here even though
1835
+ `e` is a key (Section B.6) -- comparing against the key `e`'s own
1836
+ precomputed entry (`0 1`, from B.12.3 above) would silently drop the
1837
+ exact spelling bonus `België`'s own `belgië` field earns against the
1838
+ *literal* query `ë`: `belgië` contains `ë`, reaching folded tier `10`
1839
+ *and* the bonus (the raw comparison also reaches tier `10`, since
1840
+ `belgië` literally contains `ë`), quality `11`, score `11 + 3 = 14`.
1841
+ `Nederland` / `The Netherlands` reaches only folded tier `10` with no
1842
+ bonus (neither field contains a literal `ë`), quality `10`, score
1843
+ `10 + 3 = 13`. Scored this way, `België` (`14`) outranks `Nederland`
1844
+ (`13`) outright -- the key `e`'s own precomputed entry, built by
1845
+ scoring `e` (not `ë`) as the query, has them the other way around
1846
+ (`0 1`, `Nederland` first) precisely because `e` itself, being
1847
+ diacritic-free, never earns `België`'s field the bonus the way the
1848
+ literal query `ë` does. Trusting the key's entry here would report
1849
+ `Nederland` as the top match for `ë`, the wrong answer. The query
1850
+ instead falls through to step 2: it folds to one symbol (one short of a
1851
+ trigram), so the lists cannot help either, and every name is compared
1852
+ directly, correctly placing `België` first.
1853
+
1854
+ ### B.13. Qualified Queries
1855
+
1856
+ A search box is often typed into as `Paris, Texas`: a place, then a
1857
+ place it lies in. A reader MAY treat a query that contains a comma
1858
+ (`,`, U+002C) as a **qualified query**, which has several
1859
+ **readings**:
1860
+
1861
+ - the whole query as given, with no contexts, so that a place whose own
1862
+ name contains a comma, such as `Stormont, Dundas and Glengarry
1863
+ Counties`, is still found by typing its name;
1864
+ - for each comma in the query, a reading whose **head** is the text
1865
+ before that comma and whose **contexts** are the parts after it,
1866
+ divided at the remaining commas. White space (Unicode `White_Space`)
1867
+ is removed from both ends of the head and of every context, and
1868
+ contexts that are then empty are dropped, so that `Paris,`, typed on
1869
+ the way to `Paris, Texas`, reads as `Paris`. A reading whose head is
1870
+ empty is dropped, as is one identical to an earlier reading.
1871
+
1872
+ A place matches a reading when its name entry matches the head
1873
+ ([Section B.5](#b5-relevance-order)) and every context matches the name
1874
+ entry of one of the place's ancestors
1875
+ ([Section 7.1](#71-identifiers-and-hierarchy)), each context a further
1876
+ ancestor than the one before it, nearest first: for a city, the first
1877
+ context may match its region or its country, and a second only its
1878
+ country. A reading with more contexts than the place has ancestors does
1879
+ not match it. A place matches a qualified query when it matches any of
1880
+ its readings.
1881
+
1882
+ For ranking, a place's quality is that of the head against its own name
1883
+ entry, and its **context quality** is the sum of the qualities of the
1884
+ contexts against the ancestors they match, under the assignment of
1885
+ contexts to ancestors that gives the highest sum. Among the readings a
1886
+ place matches, the one that ranks first in the order being used counts.
1887
+
1888
+ Every head is matched as a query of its own, so the trigram lists never
1889
+ lose a match of a reading, and a reader uses them head by head
1890
+ ([Section B.9](#b9-query-procedure)). The short-query table is not used
1891
+ for a query with a comma.
1892
+
1893
+ For example, against the file of
1894
+ [Appendix A](#appendix-a-worked-example), `zaandam, noord` has the
1895
+ readings `zaandam, noord` (no contexts), which matches nothing, and
1896
+ `zaandam` with the context `noord`, which matches `Zaandam`, whose
1897
+ region `Noord-Holland` contains `noord`. `antwerp, antwerpen` matches
1898
+ the city `Antwerpen`, whose region is `Antwerpen`, but not the region
1899
+ `Antwerpen`, whose only ancestor is `België`. `zaandam, nederland,
1900
+ noord` matches nothing, since the contexts are nearest first.
1901
+
1902
+ ## Appendix C. Place Location Index
1903
+
1904
+ This appendix defines a **representative cell** for every place, a
1905
+ cell near the middle of the place's area whose centre serves as the
1906
+ place's coordinate, and an **area byte**, the size of that area on a
1907
+ logarithmic scale, and specifies an OPTIONAL sidecar file that stores
1908
+ both. The file is derived entirely from one CGEODATA file and carries
1909
+ file kind `2` ([Section 5](#5-header)).
1910
+
1911
+ ### C.1. Purpose and Properties
1912
+
1913
+ A place search that shows its results on a map, or hands them to an
1914
+ application that wants a coordinate per result, needs a point for each
1915
+ place. The file stores areas, not points, so the point has to be
1916
+ derived from the chunks: [Section C.3](#c3-representative-cell) defines
1917
+ it. Deriving it for one place means decoding every chunk that might
1918
+ hold one of the place's cells, which at worldwide scale is every chunk,
1919
+ about 1.5 seconds. The index described here stores the result for every
1920
+ place, so that finding it costs one read of a few bytes. The area tells
1921
+ an application how far to zoom a map in on the place, and lets a search
1922
+ rank large places before small ones of the same name
1923
+ ([Section B.5.1](#b51-area-order)); computing it is as costly as the
1924
+ cell.
1925
+
1926
+ The index has the following properties:
1927
+
1928
+ - **It is a cache, not a data source.** Every entry is the
1929
+ representative cell, and every area byte the area byte, of
1930
+ [Section C.3](#c3-representative-cell), which a reader can compute
1931
+ from the source file alone. A reader MUST produce the same result with
1932
+ and without the index. The one exception is search ranking: a reader
1933
+ that ranks by area ([Section B.5.1](#b51-area-order)) only while an
1934
+ index supplies the areas orders its search results differently
1935
+ without one, though it finds the same places.
1936
+ - **It is a pure function of its source.** Two writers given the same
1937
+ source file MUST produce byte-identical files. The file carries no
1938
+ timestamp and no parameter chosen by the writer. It is bound to its
1939
+ source by a checksum ([Section C.4](#c4-file-layout)).
1940
+ - **It is uncompressed and fixed-width.** Entry `id` is at a computed
1941
+ offset, so a lookup reads exactly one entry and a reader needs to keep
1942
+ nothing but the header in memory between lookups.
1943
+ - **It is disposable.** It can be rebuilt from its source at any time,
1944
+ and a reader that lacks it computes the same cells from the chunks.
1945
+
1946
+ ### C.2. Entries
1947
+
1948
+ An **entry** names one cell of the source file, or no cell at all. It
1949
+ is an unsigned integer. Let `B = width_exp + height_exp`, so that a
1950
+ chunk index fits in `B` bits. The entry for the cell of depth `d` whose
1951
+ chunk index is `i` ([Section 6.1](#61-chunk-grid)) and whose stored code
1952
+ is `code` ([Section 8.1](#81-cell-grid-and-morton-codes)) is
1953
+
1954
+ ```
1955
+ entry = 2^(B + 2d) + i × 4^d + (code >> 2(D - d))
1956
+ ```
1957
+
1958
+ that is, a single `1` bit (the **marker**), followed by the `B` bits of
1959
+ the chunk index, followed by the cell's `2d`-bit path from the top of
1960
+ the chunk's quadtree. The entry `0` has no marker and means that the
1961
+ place has no cells. A reader decodes a non-zero entry as follows:
1962
+
1963
+ 1. Let `t` be the position of the most significant `1` bit, so that
1964
+ `2^t <= entry < 2^(t+1)`.
1965
+ 2. `t - B` MUST be even, and between `0` and `2D` inclusive. Otherwise
1966
+ the entry is invalid. The depth is `d = (t - B) / 2`.
1967
+ 3. Let `rest = entry - 2^t`. The chunk index is `i = rest >> 2d`, and
1968
+ the stored code is `(rest mod 4^d) << 2(D - d)`.
1969
+
1970
+ Since `rest < 2^(B + 2d)`, the chunk index is always less than
1971
+ `W × H`. The largest entry, for a cell of depth `D`, has
1972
+ `B + 2D + 1` bits, so every entry of a file fits in
1973
+
1974
+ ```
1975
+ entry_width = ceil((width_exp + height_exp + 2 × max_depth + 1) / 8)
1976
+ ```
1977
+
1978
+ bytes, which is what the index uses for all of them. For a `64 × 64`
1979
+ chunk grid this is 4 bytes up to `max_depth = 9`, 5 bytes up to
1980
+ `max_depth = 13`, and 6 bytes up to `max_depth = 17`. A reader whose
1981
+ integers are exact only up to `2^53`, such as one in JavaScript, can
1982
+ handle entries of up to 6 bytes and MAY reject an index with wider
1983
+ ones. The marker is what lets one width serve cells of
1984
+ every depth, and lets `0` stand for no cell without reserving any
1985
+ valid cell's value.
1986
+
1987
+ Writers following this version store only full-precision cells
1988
+ (`d = D`), as [Section C.3](#c3-representative-cell) requires. Readers
1989
+ MUST nevertheless accept an entry of any depth, so that a later version
1990
+ can name coarser cells without a new layout.
1991
+
1992
+ ### C.3. Representative Cell
1993
+
1994
+ The **area** of a place is the set of records, across all chunks,
1995
+ attributed to that place or to a place under it: for a city, the
1996
+ records whose palette entry is the city; for a region, those of the
1997
+ region and of its cities; for a country, those of the country, its
1998
+ regions, and their cities ([Section 7.1](#71-identifiers-and-hierarchy)).
1999
+ Records with palette index `0` belong to no area. A place whose area is
2000
+ empty has no representative cell, and its entry is `0`.
2001
+
2002
+ Each record of the area is placed on the global grid of full-precision
2003
+ cells used by the lookup procedure ([Section 9](#9-lookup-procedure),
2004
+ step 2). For a record with stored code `code` and depth `d` in chunk
2005
+ `i = r × W + c`, let `(x, y)` be the cell whose full-precision code is
2006
+ `code`, the south-west corner cell of the record. Then the record
2007
+ covers the `s × s` square of global cells with south-west corner
2008
+ `(gx, gy)`, where
2009
+
2010
+ ```
2011
+ gx = c × 2^D + x gy = r × 2^D + y s = 2^(D - d)
2012
+ ```
2013
+
2014
+ and it has **weight** `s × s`, its area in full-precision cells. The
2015
+ representative cell is computed from these squares in four steps.
2016
+
2017
+ 1. **Centres.** The centre of each square, in units of half a cell, is
2018
+ `u = 2 × gx + s` horizontally and `v = 2 × gy + s` vertically. Both
2019
+ are integers.
2020
+ 2. **Weighted median per axis.** Let `T` be the total weight of the
2021
+ area. For the `u` axis, let `lo` be the smallest centre `u` for which
2022
+ the weight of all squares with centre at most `u` is at least `T / 2`.
2023
+ If that weight is exactly `T / 2`, let `hi` be the smallest centre
2024
+ greater than `lo`, which exists because the remaining weight is
2025
+ positive. Otherwise let `hi = lo`. The median lies at `(lo + hi) / 2`
2026
+ half-cells, in the full-precision cell column
2027
+
2028
+ ```
2029
+ fx = floor((lo + hi) / 4)
2030
+ ```
2031
+
2032
+ The same procedure on the `v` centres gives the row `fy`. With equal
2033
+ weights this is the familiar median: the middle value of an odd
2034
+ count, and the midpoint of the two middle values of an even one.
2035
+ 3. **Nearest cell of the area.** For every square, its cell nearest to
2036
+ `(fx, fy)` is `(clamp(fx, gx, gx + s - 1), clamp(fy, gy, gy + s - 1))`,
2037
+ at offset `(dx, dy)` from `(fx, fy)`. Its squared distance is
2038
+
2039
+ ```
2040
+ (a × dx)^2 + (b × dy)^2 where g = min(2 × H, W), a = 2 × H / g, b = W / g
2041
+ ```
2042
+
2043
+ which is proportional to the squared distance in degrees, a
2044
+ full-precision cell being `360 / (W × 2^D)` degrees wide and
2045
+ `180 / (H × 2^D)` high. The representative cell is the nearest of
2046
+ these cells. Among equally near cells, it is the one in the lower
2047
+ row, and among those, the one in the lower column. When `(fx, fy)`
2048
+ itself lies in the area, it is the only cell at distance `0`, and so
2049
+ it is the representative cell.
2050
+ 4. **Entry.** The representative cell `(px, py)` is the full-precision
2051
+ cell `x = px mod 2^D`, `y = py mod 2^D` of chunk
2052
+ `i = (py >> D) × W + (px >> D)`, and its entry is formed as in
2053
+ [Section C.2](#c2-entries) with `d = D`.
2054
+
2055
+ The representative cell always lies within one of the area's records,
2056
+ so a lookup ([Section 9](#9-lookup-procedure)) of any point in it, in
2057
+ particular its centre, resolves to the place or to a place under it.
2058
+ The result depends only on the set of records in the area, not on the
2059
+ order in which they are visited.
2060
+
2061
+ The **area byte** of a place is `0` when its area is empty. Otherwise it
2062
+ is the number of binary digits of `T^4`, where `T` is the total weight
2063
+ of step 2, but at most `255`:
2064
+
2065
+ ```
2066
+ area_byte = min(255, floor(log2(T^4)) + 1)
2067
+ ```
2068
+
2069
+ Each step is a quarter of a doubling: an area of `T` cells has area byte
2070
+ `v` when `2^((v - 1) / 4) <= T < 2^(v / 4)`, so a reader can recover the
2071
+ area to within about 9%. Implementations MUST compute it exactly, for
2072
+ example by counting the bits of `T^4` in integer arithmetic, since a
2073
+ floating-point logarithm can round the wrong way for an area just below
2074
+ a power of two. A `128 × 128` chunk grid with `max_depth = 10` has
2075
+ `2^34` full-precision cells, area byte `137`; the cap of `255` is only
2076
+ reached by an area of `2^63.5` cells or more.
2077
+
2078
+ Every quantity above is an integer, and implementations MUST compute
2079
+ them exactly. The largest is a squared distance, which is less than
2080
+ `(a × W × 2^D)^2 + (b × H × 2^D)^2`; an implementation whose integers
2081
+ are exact only up to `2^53`, such as one in JavaScript, SHOULD check
2082
+ this bound before computing any representative cell. It holds for a
2083
+ `64 × 64` chunk grid up to `max_depth = 19`.
2084
+
2085
+ Informally: the median locates the middle of the place along each axis
2086
+ independently, weighting every cell by its area. Unlike the mean, it is
2087
+ not dragged away by a distant exclave or island. Unlike the centre of
2088
+ the bounding box, it follows where the area actually is. When the
2089
+ median point falls outside the place, as it can for a crescent-shaped
2090
+ area or one made of several islands, step 3 moves it to the nearest
2091
+ cell that is inside. Two known imperfections follow from working on
2092
+ the grid: weights are areas in degrees, not on the ground, so cells at
2093
+ high latitude count for more than their true size, in the area byte as
2094
+ much as in the median; and the horizontal
2095
+ axis does not wrap, so the median of a place that straddles the 180th
2096
+ meridian lands on one side of it rather than in the middle.
2097
+
2098
+ ### C.4. File Layout
2099
+
2100
+ The file is one header followed by the entries and the area bytes,
2101
+ with no padding and no trailer.
2102
+
2103
+ | Field | Type | Description |
2104
+ |-------------------|------------------------------|-------------|
2105
+ | `signature` | 8 bytes | The ASCII bytes `CGEODATA`, as in [Section 5](#5-header). |
2106
+ | `version` | varint | Kind and version as defined in Section 5. This appendix specifies kind `2`, version `1`, so the value is `33` (`21`). Version `0` had no `areas`; readers MUST reject it. |
2107
+ | `source_checksum` | u64 | Checksum of the start of the source file, defined below. |
2108
+ | `entry_width` | u8 | The width in bytes of every entry. MUST equal the value given in [Section C.2](#c2-entries) for the source's header. |
2109
+ | `place_count` | varint | `N`, the number of places in the source ([Section 7.1](#71-identifiers-and-hierarchy)). |
2110
+ | `entries` | `N × entry_width` bytes | The entry of place `0`, then of place `1`, and so on, each an unsigned little-endian integer of `entry_width` bytes. |
2111
+ | `areas` | `N` bytes | The area byte ([Section C.3](#c3-representative-cell)) of place `0`, then of place `1`, and so on. |
2112
+
2113
+ The file length MUST be exactly the header length plus
2114
+ `N × (entry_width + 1)`. The entry of place `id` starts at byte
2115
+ `header_length + id × entry_width`, and its area byte is at
2116
+ `header_length + N × entry_width + id`. The area bytes follow the
2117
+ entries rather than being part of them so that a reader ranking a
2118
+ search by area reads all of them in one read.
2119
+
2120
+ `source_checksum` is the 64-bit FNV-1a hash
2121
+ ([Section B.8](#b8-source-checksum)) of the first bytes of the source
2122
+ file up to, but not including, the first chunk: the header, the
2123
+ directory, and the place table, that is file offsets `0` to
2124
+ `chunks_start - 1` ([Section 6.3](#63-locating-a-chunk)). It is stored
2125
+ least significant byte first.
2126
+
2127
+ The index depends on the chunks as well as on the place table, so the
2128
+ checksum of [Section B.8](#b8-source-checksum), which covers only the
2129
+ place table, would not do. Hashing the chunks themselves would make
2130
+ loading an index read the whole source file. The directory is a
2131
+ compromise: it holds the length of every compressed chunk, which
2132
+ changes whenever the chunk's content does, except in the unlikely case
2133
+ that a rebuilt chunk compresses to exactly its old length. Writers that
2134
+ rebuild a source SHOULD rebuild its index at the same time rather than
2135
+ rely on the checksum to catch the difference.
2136
+
2137
+ Readers SHOULD compute the checksum when they load an index, and a
2138
+ reader that does so MUST reject an index whose `source_checksum`
2139
+ differs from it. A reader that rejects an index falls back to computing
2140
+ representative cells from the chunks.
2141
+
2142
+ ### C.5. Lookup Procedure
2143
+
2144
+ To find the representative coordinate of place `id` with an index:
2145
+
2146
+ 1. **Validate.** `id` MUST be less than `N`.
2147
+ 2. **Read the entry.** Read `entry_width` bytes at
2148
+ `header_length + id × entry_width` and interpret them as an unsigned
2149
+ little-endian integer.
2150
+ 3. **Decode** it as in [Section C.2](#c2-entries). An entry of `0`
2151
+ means the place has no representative cell, and no coordinate.
2152
+ 4. **Compute the coordinate.** The cell's extent follows from its chunk
2153
+ ([Section 6.1](#61-chunk-grid)) and its path within the chunk
2154
+ ([Section 8.1](#81-cell-grid-and-morton-codes)), as for any record.
2155
+ The place's coordinate is the centre of that extent.
2156
+ 5. **Read the area**, if wanted: the byte at
2157
+ `header_length + N × entry_width + id`.
2158
+
2159
+ Without an index, a reader computes the same cell and area byte from the
2160
+ chunks by [Section C.3](#c3-representative-cell) and continues at
2161
+ step 4.
2162
+
2163
+ A place of area byte `v` covers about `2^((v - 0.5) / 4)` full-precision
2164
+ cells, each `360 / (W × 2^D)` degrees wide and `180 / (H × 2^D)` high.
2165
+ A square of the same area in degrees is a rough guide to how far to zoom
2166
+ a map in on the place: at zoom `z`, a Web Mercator map shows
2167
+ `360 / 2^z` degrees of longitude across one 256-pixel tile.
2168
+
2169
+ The extent of the cell tells a reader how precisely to present the
2170
+ coordinate: a full-precision cell of a worldwide file with
2171
+ `max_depth = 8` is about `0.02°` wide, so three decimals are
2172
+ meaningful and more are not.
2173
+
2174
+ ### C.6. Robustness
2175
+
2176
+ A locate index may come from an untrusted source, or may be stale.
2177
+ Readers MUST NOT assume that it is well formed or that it matches its
2178
+ source.
2179
+
2180
+ - Readers MUST verify the signature, kind, and version before
2181
+ interpreting any other field.
2182
+ - Readers MUST check that `entry_width` equals the value
2183
+ [Section C.2](#c2-entries) gives for the source, that `place_count`
2184
+ equals `N`, and that the file length is exactly the header length
2185
+ plus `N × (entry_width + 1)`, before reading any entry.
2186
+ - Every byte is a valid area byte, so there is nothing to check. A
2187
+ wrong one only misorders search results ranked by area or misjudges a
2188
+ zoom level.
2189
+ - Readers MUST reject an entry whose marker is not at a valid position
2190
+ ([Section C.2](#c2-entries), step 2). The decoding cannot produce a
2191
+ chunk index outside the grid, so no further bounds check is needed
2192
+ before computing the cell's extent.
2193
+ - Unlike a search index, a locate index contributes its result directly:
2194
+ a malformed or mismatched index yields a wrong coordinate rather than
2195
+ a missed one. A reader that cannot trust the pairing MAY confirm a
2196
+ result by looking up its coordinate and checking that it resolves to
2197
+ the place or to a place under it, at the cost of decoding one chunk.
2198
+
2199
+ ### C.7. Design Notes
2200
+
2201
+ This section is informative.
2202
+
2203
+ **Measurements.** On a worldwide file of 319,440 places with a
2204
+ `64 × 64` chunk grid and `max_depth = 8` (9.5 MiB), entries are 4 bytes
2205
+ and the index (of version `0`, without area bytes) is 1.2 MiB; the area
2206
+ bytes add one byte per place, 0.3 MiB. Building it decodes the chunks two or three
2207
+ times, since places are processed a batch of countries at a time to
2208
+ bound memory, and takes about 11 seconds. Without the index, one representative cell
2209
+ costs about 1.6 seconds, nearly all of it spent decoding chunks. With
2210
+ it, a lookup takes well under a millisecond, even with the file evicted
2211
+ from the operating system's cache, and a reader that is idle between
2212
+ lookups holds nothing but an open file. An older worldwide build at
2213
+ `max_depth = 11` (67 MiB, 255,555 places) needs 5-byte entries, for an
2214
+ index of 1.2 MiB built in 2.5 minutes. A `128 × 128` grid with
2215
+ `max_depth = 10` (71 MiB, 259,598 places) also needs 5-byte entries;
2216
+ with its area bytes the index is 1.5 MiB, built in about 90 seconds,
2217
+ of which totalling the areas takes a few.
2218
+
2219
+ **Alternatives measured.** Storing only the chunk of each place's cell
2220
+ (2 bytes per place for a `64 × 64` grid) and decoding that chunk at
2221
+ lookup time cost 4 to 6 ms per lookup, and about 50 ms for a burst of
2222
+ ten, the typical page of search results; the time goes into decoding
2223
+ the chunk, not reading it. With `max_depth = 11`, where chunks are
2224
+ about ten times larger, it would cost tens of milliseconds per lookup.
2225
+ Compressing the entries in blocks of 256 saved 30% of the size and
2226
+ doubled the lookup time, since the cells of neighbouring places are
2227
+ close but their low-order bits are not. Storing a coarser cell to save
2228
+ bytes and refining it from the chunk at lookup time has the costs of
2229
+ the chunk-only design for every place whose cell is deeper than the
2230
+ coarse cell. Fitting 4-byte entries at `max_depth = 11` means coarsening
2231
+ every cell deeper than depth 9, which was 16% of places, with chunk
2232
+ decodes of 40 to 140 ms each.
2233
+
2234
+ **Why the whole area.** The records attributed directly to a region or
2235
+ country are only the parts not covered by any of its children: often
2236
+ nothing at all, and otherwise leftover strips such as water or
2237
+ unincorporated land. On the worldwide file, 41% of regions have no
2238
+ records of their own. Including the records of every place under a
2239
+ place makes the coordinate of a region or country the middle of the
2240
+ region or country, as a user expects.
2241
+
2242
+ ### C.8. Worked Example
2243
+
2244
+ This example indexes the source file of
2245
+ [Appendix A](#appendix-a-worked-example). There, `B = 1 + 1 = 2` and
2246
+ `D = 2`, so entries have at most `2 + 4 + 1 = 7` bits and
2247
+ `entry_width = 1`. The file is `19 + 7 = 26` bytes.
2248
+
2249
+ #### C.8.1. File (33 bytes)
2250
+
2251
+ ```
2252
+ 43 47 45 4f 44 41 54 41 signature "CGEODATA"
2253
+ 21 version 33 -> kind 2 (place location index), version 1
2254
+ a1 f7 91 13 f5 48 03 fc source_checksum 0xfc0348f51391f7a1
2255
+ 01 entry_width 1
2256
+ 07 place_count 7
2257
+ 73 00 73 00 00 74 00 entries of places 0 to 6
2258
+ 0e 00 0e 00 00 05 00 areas of places 0 to 6
2259
+ ```
2260
+
2261
+ The checksum is the FNV-1a 64 hash of the first 131 bytes of the
2262
+ Appendix A file: the 17-byte header, the 5-byte directory, and the
2263
+ 109-byte place table, up to the first byte of chunk `3` at offset
2264
+ `131`.
2265
+
2266
+ #### C.8.2. Areas
2267
+
2268
+ All records are in chunk `3` (`c = 1`, `r = 1`), so `gx = 4 + x` and
2269
+ `gy = 4 + y`. The four records with a place are:
2270
+
2271
+ | Record | Code, depth | Place | `(gx, gy)` | `s` | Weight | `u` | `v` |
2272
+ |--------|-------------|------------------|------------|-----|--------|------|------|
2273
+ | 0 | 0, 1 | 2, Noord-Holland | (4, 4) | 2 | 4 | 10 | 10 |
2274
+ | 1 | 4, 2 | 5, Zaandam | (6, 4) | 1 | 1 | 13 | 9 |
2275
+ | 5 | 8, 1 | 2, Noord-Holland | (4, 6) | 2 | 4 | 10 | 14 |
2276
+ | 6 | 12, 2 | 5, Zaandam | (6, 6) | 1 | 1 | 13 | 13 |
2277
+
2278
+ Records `2` to `4` have palette index `0`. The area of Zaandam (`5`) is
2279
+ records `1` and `6`. The areas of Noord-Holland (`2`) and of Nederland
2280
+ (`0`), which contains it, are all four records. Amsterdam, België, and both
2281
+ Antwerpens have no records, so their entries are `00`.
2282
+
2283
+ #### C.8.3. Zaandam
2284
+
2285
+ `T = 2`. Both `u` centres are `13`, which already holds more than half
2286
+ the weight, so `lo = hi = 13` and `fx = floor(26 / 4) = 6`. The `v`
2287
+ centres are `9` and `13`, one weight each. The weight at or below `9`
2288
+ is exactly `T / 2`, so `lo = 9`, `hi = 13`, and
2289
+ `fy = floor(22 / 4) = 5`: the median falls midway between Zaandam's two
2290
+ cells.
2291
+
2292
+ Cell `(6, 5)` is local cell `(2, 1)` of chunk `3`, code `6`, which is
2293
+ record `3`, with no place. The nearest cells of the area are record
2294
+ `1`'s `(6, 4)` and record `6`'s `(6, 6)`, both one row away. With
2295
+ `g = min(4, 2) = 2`, `a = 2` and `b = 1`, both are at squared distance
2296
+ `1`. The tie goes to
2297
+ the lower row, `(6, 4)`: local cell `(2, 0)` of chunk `3`, code `4`.
2298
+ The entry is `2^(2 + 4) + 3 × 16 + 4 = 116`, `74`.
2299
+
2300
+ Its centre is `lat -90 + 4.5 × 22.5 = 11.25`,
2301
+ `lon -180 + 6.5 × 45 = 112.5`, which is the cell of the first lookup in
2302
+ [Section A.5](#a5-lookups), Zaandam.
2303
+
2304
+ #### C.8.4. Noord-Holland and Nederland
2305
+
2306
+ `T = 4 + 1 + 4 + 1 = 10`. The `u` centres are `10` with weight `8`
2307
+ and `13` with weight `2`: `10` holds more than half, so
2308
+ `fx = floor(20 / 4) = 5`. The `v` centres are `9` (weight `1`), `10`
2309
+ (`4`), `13` (`1`), and `14` (`4`). The weight at or below `10` is `5`,
2310
+ exactly half, so `lo = 10`, `hi = 13`, and `fy = floor(23 / 4) = 5`.
2311
+
2312
+ Cell `(5, 5)` is local cell `(1, 1)`, inside record `0`'s square, so it
2313
+ is the representative cell. Its code at depth `2` is `3`, and the
2314
+ entry is `64 + 48 + 3 = 115`, `73`. Its centre is `lat 33.75`,
2315
+ `lon 67.5`, where a lookup finds record `0`: Noord-Holland, which is
2316
+ the place itself for ID `2` and a place under it for ID `0`.
2317
+
2318
+ #### C.8.5. Decoding
2319
+
2320
+ Entry `74` is `116`. Its most significant bit is `t = 6`, and
2321
+ `t - B = 4` is even and at most `2D = 4`, so `d = 2`.
2322
+ `rest = 116 - 64 = 52`, the chunk index is `52 >> 4 = 3`, and the
2323
+ stored code is `52 mod 16 = 4`. An entry of `75` (`117`) would decode
2324
+ the same way to code `5`, a cell with no place: a well-formed entry is
2325
+ not necessarily a correct one, which is what the checksum is for. An
2326
+ entry of `20` (`32`) would be rejected, since `t - B = 3` is odd.
2327
+
2328
+ #### C.8.6. Area Bytes
2329
+
2330
+ Zaandam's area has `T = 2` (C.8.3). `T^4 = 16`, `10000` in binary, has
2331
+ five digits, so its area byte is `05`: `2^(4/4) <= 2 < 2^(5/4)`.
2332
+ Noord-Holland's and Nederland's have `T = 10` (C.8.4). `T^4 = 10000` lies
2333
+ between `2^13 = 8192` and `2^14 = 16384`, so it has fourteen binary
2334
+ digits and their area byte is `0e`. The four places without records
2335
+ have area byte `00`.
2336
+
2337
+ A full-precision cell of this file is `45°` wide and `22.5°` high,
2338
+ `1012.5` square degrees. Area byte `05` stands for about
2339
+ `2^(4.5 / 4) × 1012.5 ≈ 2209` square degrees, against Zaandam's true
2340
+ `2025`.
2341
+
2342
+ ## Appendix D. References
2343
+
2344
+ - [RFC1950] Deutsch, P. and J-L. Gailly, "ZLIB Compressed Data Format
2345
+ Specification version 3.3", May 1996.
2346
+ - [RFC1951] Deutsch, P., "DEFLATE Compressed Data Format Specification
2347
+ version 1.3", May 1996.
2348
+ - [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
2349
+ Requirement Levels", BCP 14, March 1997.
2350
+ - [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO 10646",
2351
+ STD 63, November 2003.
2352
+ - [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119
2353
+ Key Words", BCP 14, May 2017.
2354
+ - Protocol Buffers, "Encoding: Base 128 Varints",
2355
+ <https://protobuf.dev/programming-guides/encoding/#varints>.
2356
+ - Morton, G. M., "A computer oriented geodetic data base and a new
2357
+ technique in file sequencing", IBM, 1966.
2358
+ - [UAX15] Whistler, K., "Unicode Normalization Forms", Unicode Standard
2359
+ Annex #15, <https://unicode.org/reports/tr15/>.
2360
+ - [UNICODE] The Unicode Consortium, "The Unicode Standard", Section
2361
+ 3.13 "Default Case Algorithms", <https://www.unicode.org/versions/latest/>.
2362
+ - [FNV] Eastlake, D., Fowler, G., Noll, L., Vo, K-P., and T. Hansen,
2363
+ "The FNV Non-Cryptographic Hash Algorithm",
2364
+ draft-eastlake-fnv, <https://datatracker.ietf.org/doc/draft-eastlake-fnv/>.
2365
+ - [history.md](https://github.com/ferrybig/loc-to-city/blob/master/history.md): the prototype and measurements this format
2366
+ is derived from.