@ferrybig/cgeodata-reader
    Preparing search index...

    A CGEODATA file opened for reading: answers which country, region and city a point lies in (query, lookup), what a place is called and where it sits in the hierarchy (place, placeIds), which places match a name (search), and where a place is (locate).

    Opening reads only the header, directory and place counts; each lookup reads and decodes the one chunk its point lies in, which then stays cached (OpenOptions.cacheBytes).

    // Node.js
    import { openFile } from "@ferrybig/cgeodata-reader/node";
    const reader = await openFile("world.bin");

    // Anywhere else
    import { CGeoDataReader } from "@ferrybig/cgeodata-reader";
    const reader = await CGeoDataReader.open(await (await fetch(url)).arrayBuffer());

    const { city, region, country } = await reader.query(52.0799, 4.3113);
    city?.internationalName; // "The Hague"
    Index

    The file's header (Section 5), including its attribution.

    The file's directory (Section 6): where each chunk is.

    • get placeCounts(): PlaceCounts

      How many countries, regions and cities the file has.

      Returns PlaceCounts

    • Opens a CGEODATA file: reads and validates its header, directory and the uncompressed part of its place table (Sections 5-7.2), and nothing else -- chunks and names are read on first use. The reader takes ownership of input, if it's a ByteSource: close() closes it, as does a failed open.

      Parameters

      Returns Promise<CGeoDataReader>

    • Closes the source, and any index this reader opened itself.

      Returns Promise<void>

    • The country, region and city a point lies in: Section 9's lookup procedure (lookup()) with the names resolved (place()). Rejects with a RangeError for a latitude outside [-90, 90) or a longitude outside [-180, 180).

      Parameters

      • lat: number
      • lon: number

      Returns Promise<QueryResult>

      const result = await reader.query(35.6762, 139.6503);
      result.city?.localName; // "杉並区"
      result.city?.internationalName; // "Suginami"
      result.country?.internationalName; // "Japan"
    • Section 9's lookup procedure: the place a point lies in, as an id, and the cell the answer holds for. Reads and decodes the point's chunk if it isn't cached; resolve the id with place() or placeIds().

      Parameters

      • lat: number
      • lon: number

      Returns Promise<PlaceIdResult>

    • lookup() answered from memory only: the same result when the point's chunk is already decoded, undefined when answering would need a read. For callers doing many lookups close together, which mostly hit the same few chunks:

      const result = reader.lookupCached(lat, lon) ?? (await reader.lookup(lat, lon));
      

      waits only for the lookups that actually read something.

      Parameters

      • lat: number
      • lon: number

      Returns PlaceIdResult | undefined

    • The index of the chunk a point lies in (Section 6.1).

      Parameters

      • lat: number
      • lon: number

      Returns number

    • The names of one place by global id, without resolving its ancestors. Cheaper than place() when only the names are needed.

      Parameters

      • id: number

      Returns Promise<string[]>

    • FNV-1a 64-bit hash of the place table bytes, as defined by binary-format.md Section B.6. Used to bind a search index to its source.

      Returns Promise<bigint>

    • FNV-1a 64-bit hash of everything before the first chunk -- header, directory and place table -- as defined by binary-format.md Section C.4. Binds a locate index to its source: the chunk lengths it covers change whenever the chunks do.

      Returns Promise<bigint>

    • Attaches a text search index (Appendix B, built by buildSearchIndex / cgeodata build-search-index) so search() only checks the places the index says could match instead of every place. Results are identical with or without it -- only the speed differs.

      Takes the index's file (as for open(); read on demand) or an open SearchIndex (which the caller keeps ownership of), or undefined to search without one. Replaces OpenOptions.searchIndex. Rejects with a CGeoDataIndexMismatchError if it was built from a different file.

      Parameters

      Returns Promise<void>

    • Whether search() will use an index. Reads (and validates) the one from OpenOptions.searchIndex if that hasn't happened yet.

      Returns Promise<boolean>

    • Attaches a locate index (Appendix C, built by buildLocateIndex / cgeodata build-locate-index) so locate() reads one entry instead of scanning every chunk. Results are identical with or without it -- only the speed differs -- except that its areas also let a sorted search() rank equal matches by size (SearchOptions.areaTieBreak).

      Takes the index's file (as for open(); read on demand) or an open LocateIndex (which the caller keeps ownership of), or undefined to locate without one. Replaces OpenOptions.locateIndex. Rejects with a CGeoDataIndexMismatchError if it was built from a different file.

      Parameters

      Returns Promise<void>

    • Whether locate() will use an index. Reads (and validates) the one from OpenOptions.locateIndex if that hasn't happened yet.

      Returns Promise<boolean>

    • Resolves one place id (Section 7.5) to its names and ancestors.

      Parameters

      • id: number

      Returns Promise<Place>

    • place() without the names: the place's level and the global ids of it and its ancestors, from the place table's counts alone, so no name block is read -- and so synchronous.

      Parameters

      • id: number

      Returns PlaceIds

    • How many places sit directly under id: a country's regions, a region's cities, always 0 for a city. From the place table's counts alone, like placeIds(), so no name block is read and it is synchronous: childCount(region) === 0 finds a region with no city mapped to it.

      Parameters

      • id: number

      Returns number

    • Walks the place hierarchy in id order (countries, then regions, then cities), optionally restricted to one level and/or one ancestor. Each level's ids for a given parent are contiguous (Section 7.5), so a filtered walk only touches the ids it yields.

      Parameters

      Returns AsyncIterableIterator<Place>

    • Diacritic- and case-insensitive substring match against every name of every place (all levels unless filtered) -- Section B.5's "matches": the folded form (foldDiacritics) of a name contains the folded form of the query. Without a search index this is a plain linear scan over the name blocks; with one (useSearchIndex()), only the places the index can't rule out are compared. Same results either way.

      A text with commas also reads as a place and the places it lies in (Appendix B.13): "Paris, Texas" finds a place named "Paris, Texas" and every "Paris" whose region or country matches "Texas"; "Springfield, Illinois, United States" narrows by both. Each part after a comma has to match a different ancestor, nearest first.

      Parameters

      Returns AsyncIterableIterator<Place>

      for await (const place of reader.search("munchen", { sort: true, limit: 20 }))
      console.log(place.internationalName, place.country.internationalName);
      // Munich Germany
      // Grafing bei München Germany
      // ...
    • The area byte (binary-format.md Section C.3) of every place, by id -- the size of the place and everything under it, on a log scale, a quarter of a doubling per step -- read from the attached locate index (useLocateIndex()), or undefined without one. What search({ sort: true }) breaks ties with (SearchOptions.areaTieBreak). Read once and then kept; the array is shared, so don't modify it.

      Returns Promise<Uint8Array<ArrayBufferLike> | undefined>

    • The parsed records of one chunk (Section 8), or undefined when the chunk is empty. Cached like lookups, unless cache is false: a one-pass walk over every chunk should skip the cache, so it doesn't evict everything lookups had cached for chunks it only visits once.

      Parameters

      • index: number
      • __namedParameters: { cache?: boolean } = {}

      Returns Promise<ChunkRecords | undefined>

    • The lat/lon extent of a chunk (Section 6.1).

      Parameters

      • index: number

      Returns BoundingBox

    • A representative lat/lon for a place id: the centre of the place's representative cell (binary-format.md Section C.3), a cell near the middle of the place's area -- its own cells plus those of every place under it -- that is guaranteed to lie in that area, so query() at the returned point resolves to the place or one of its descendants.

      Parameters

      • id: number

      Returns Promise<Location | null>

      const position = await reader.locate(place.id);
      if (position) map.setView([position.lat, position.lon]);

      Null if neither the place nor anything under it has any cells -- and that happens to real places, not only to broken files: a place smaller than the file's finest cell (a tiny island, an enclave, a boundary sliver) loses every cell to its neighbours. On a worldwide build that's about 0.15% of places, every one of them still found by search(); drop them from results that need a position rather than defaulting to a coordinate.

      With a locate index (useLocateIndex()) this reads one entry; without one it scans every chunk. Same result either way.

    • The ids of place id and of every place under it, as half-open ranges: [id, id + 1] first, then for a country its regions and their cities, for a region its cities. Each level's children are contiguous (Section 7.1), so this is at most three ranges.

      Parameters

      • id: number

      Returns [number, number][]

    • One chunk's records with their cells and places resolved -- what a lookup in that chunk can answer with, laid out for inspection.

      Parameters

      • index: number

      Returns AsyncIterableIterator<ChunkRecord>