|
Address Generator 2.1.2
Postal addresses with real streets by place, laid out in each country's format for C++23
|
This guide covers every feature of the address-generator library. For a quick overview, see the README. For the data and how it is built, see Data and Method. For the full API reference, run doxygen Doxyfile from the repository root and open doc/api/html/index.html.
Copy dasmig/addressgen.hpp, dasmig/core.hpp and the dasmig/core/ directory into your include path, keeping them together. Supply resources/ next to the program, or define DASMIG_ADDRESS_EMBED to compile the lite tier in (see Compiled-in Data).
instance() loads the lite tier on first use (or full if there is no lite), from the compiled-in copy or from the first of resources/, ../resources, address-generator/resources and ../address-generator/resources that holds lite/places.tsv.
A tier directory holds six TSV files (see Data and Method); only places.tsv is required. Each load replaces the data and every layer loaded before.
The full tier has no streets.tsv. Its streets are one file per country, streets/<CC>.tsv, and streets.manifest.tsv lists them (country, row count, content hash). Loading reads the manifest only; a country's file is read the first time a call needs it, once, whichever thread gets there first.
std::runtime_error) rather than silently different data.data_hash() and every result's signature cover the manifest and the layers, not the rows read so far, so a seed replays the same whether or not a country was read before.extend() are applied to a country's file when it is read, in the order they were added; adding a layer makes the next call read the file again with the layer on top. A layer in replace mode leaves a country with only the layer's rows.has_streets(code) answers from the manifest and does not read a file.An address converts to std::string (the one-line form) and streams with operator<<.
weighted = false, each country is equally likely. An unknown country throws std::invalid_argument.persona-generator passes its city). The name is matched without regard to case; region (a name or a code) tells places of one name apart, and without it the most populous wins. A place the data lacks needs country and is used as given: its street is composed and its postcode comes from the country's pattern.address_script::local (the default) uses the country's own layout; latin uses the Latin-script layout for the 14 territories that have one (AE, AM, CN, EG, HK, JP, KP, KR, MO, RU, TH, TW, UA, VN) and changes nothing elsewhere. It also writes a street named in another script by its Latin name (OpenStreetMap's name:en and similar) when the data has one, in every country; otherwise the street keeps its local name. Place names are the Latin names GeoNames gives, in either script.A Japanese address is written large to small, with the postcode mark first, the prefecture and municipality on one line and the street last. The house number is a block number, chome-ban-go (8-16-11); address::number is its first part. The data has no ward or municipality names below the place, so the municipality line is the place itself; when the prefecture has the place's own name (Tokyo), it is written once.
Both take the same country, place and weighted fields as an address and draw from the same streams, so for the same fields and seed get_street returns that address's street and get_postcode its postcode. Each has the _batch and for_each_ forms of the standard (get_street_batch, for_each_postcode, ...); a postcode batch is a std::vector<std::string>.
Each part draws from a stream split from the call seed, so a part added later leaves the others unchanged: "country", "place", "street", "number", "unit" and "postcode".
Rue de la Paix or Hauptstraße. real_street tells which.Apt 4, Flat 3, 3º B).{A} street and number, {C} city, {S} region, {Z} postcode), the number before or after the street as the country writes it, empty parts dropped.get_address(request, seed) gives the same result for the same request, seed and data, on every platform, without advancing the instance engine.get_address(request) draws a fresh seed from the instance engine, initially seeded from system entropy. There are no generator-level seed() or unseed() controls; for deterministic sequences, keep the seeds.v.seed() is the seed a result was generated from: get_address(dasmig::address_request{}, v.seed()) replays it with the same request and data.The shipped data is a base layer. Files with the same columns as the shipped tables can be loaded on top of it to add places, streets or postcodes, change them or remove them, without editing the shipped files. Each table has its own key (see Data and Method).
dasmig::core::tsv_error with the file, line and column, and the data stays as it was; a directory is applied whole or not at all.layers(table) lists what is loaded for a table: source, mode, rows added, replaced and removed, and warnings such as unknown columns.load(...) replaces the base and drops every layer.disabled.Every result records the seed and the data it came from:
to_json() gives the result as JSON, the form the WebAssembly layer and the game consume:
Seeds are strings because JavaScript numbers cannot hold every 64-bit value. to_json(writer) writes into an existing core::json_writer.
Define DASMIG_ADDRESS_EMBED before including the header to compile the lite tier into the program. load(dataset::lite) and instance() then use it and need no files. The compiled-in data has the same content and hash as the files.
dasmig/addressgen_data.hpp is generated from resources/lite by make embed; CI checks it is up to date.
Each adg instance is independent. Calls to the seeded get_address(request, seed) and the batch functions are const and safe from several threads on the same instance, provided nothing loads or extends data meanwhile. Reading a full country's street file on first use is part of such a call and is guarded, as is load_streets(). Calls that draw from the instance engine (get_address(request)), load and extend require external synchronisation (e.g. a std::mutex).
Every seeded getter has batch forms: get_address_batch returns a std::vector, and for_each_address streams the items to a sink in index order without holding the batch.
i is exactly get_address(request, core::item_seed(batch_seed, first + i)). A range can be generated directly (first = 1000 starts at item 1000), two batches that follow each other equal one, and the output does not depend on the number of threads.core::batch_options has count, first, threads and unique. The request describes one item; the options describe the batch..unique = core::unique_rule::by<address>(key) regenerates an item whose key already appeared in the batch, with a deterministic retry seed, and throws std::runtime_error naming the item when the attempts run out. A key such as a.formatted() makes a batch of distinct addresses.core::jsonl_writer writes one to_json() per line, core::csv_writer flattens each result's JSON into dotted columns. Any callable (std::uint64_t index, address&) works.| Exception | When |
|---|---|
std::runtime_error | Any generation call before data is loaded; a full country's street file that is missing or does not match streets.manifest.tsv; a composed street is needed and no street_types.tsv and street_names.tsv rows exist for the country or for * |
std::invalid_argument | A request names a country with no places, or a place the data lacks without a country; extend(path) of a file with an unknown name |
dasmig::core::tsv_error | A malformed table file or layer (message gives file, line and column); the data is unchanged |