![]() |
City Generator 1.0.1
Procedural city generation for C++23
|
This guide covers every feature of the city-generator library in detail. For a quick overview, see the README. For the full API reference, run doxygen Doxyfile from the repository root and open doc/api/html/index.html.
dasmig/citygen.hpp and dasmig/random.hpp into your include path.resources/ folder (containing full/ and/or lite/ subdirectories) so it is accessible at runtime.-std=c++23.The library ships two dataset tiers:
| Tier | Enum | Cities | Source |
|---|---|---|---|
| lite | dasmig::dataset::lite | ~25k (pop ≥ 15,000) | cities15000.zip |
| full | dasmig::dataset::full | ~200k (pop ≥ 500) | cities500.zip |
Resources are stored as resources/lite/cities.tsv and resources/full/cities.tsv.
On first access the singleton constructor probes these base paths:
| Priority | Base path |
|---|---|
| 1 | resources/ |
| 2 | ../resources/ |
| 3 | city-generator/resources/ |
It loads lite/cities.tsv if found, otherwise falls back to full/cities.tsv.
If your resources are elsewhere, call load() with a direct path:
Calling load() multiple times is safe — each call adds to the existing data.
Every generated city includes all 16 GeoNames fields:
| Field | Type | Description |
|---|---|---|
geonameid | uint32_t | GeoNames primary key |
name | string | UTF-8 city name |
asciiname | string | ASCII transliteration |
latitude | double | WGS84 decimal degrees |
longitude | double | WGS84 decimal degrees |
feature_code | string | PPL, PPLA, PPLC, etc. |
country_code | string | ISO-3166 two-letter |
cc2 | string | Alternate country codes |
admin1_code | string | State/province |
admin2_code | string | County/district |
admin3_code | string | Township/commune |
admin4_code | string | Sub-district |
population | uint64_t | City population |
elevation | int16_t | Meters (-9999 if unknown) |
dem | int16_t | Digital elevation model |
timezone | string | IANA timezone ID |
Pass an ISO-3166 two-letter country code to restrict generation:
Country-filtered selection is also population-weighted within the country. Throws std::invalid_argument if no cities match the code.
By default, cities are selected with probability proportional to their population. A city with 1,000,000 inhabitants is ~1000× more likely to be selected than one with 1,000. Cities with zero population are assigned a minimum weight of 1, ensuring they can still appear.
Switch to equal-probability selection where every loaded city has the same chance of being picked, regardless of population:
The weighted() setter returns *this for chaining:
Query the current mode with:
Pass an explicit seed to get_city() to produce the same city every time:
Every city records its seed. Retrieve it with city::seed() and pass it back to reproduce the exact same result:
Seed the generator's engine for a reproducible sequence:
Note: To replay a country-filtered city, pass both the country code and the seed:
gen.get_city("BR", c.seed()). Using justc.seed()without the country filter will select from the full dataset and likely produce a different city.
Construct independent generators with their own data and engine:
The shipped datasets are generated from GeoNames using the included Python script:
This produces resources/full/cities.tsv and resources/lite/cities.tsv.
The GeoNames data is licensed under CC BY 4.0. See LICENSE_DATA.txt.
Each cg instance is independent. The static instance() singleton uses a local static for safe initialization.
| Operation | Thread-safe? |
|---|---|
instance() | Yes (static local) |
get_city() on different instances | Yes |
get_city() on the same instance | No — requires external synchronization |
load() | No — must not be called concurrently with get_city() on the same instance |
Call load() once during initialization before spawning threads. For concurrent generation, give each thread its own cg instance.
| Exception | Condition |
|---|---|
std::runtime_error | get_city() called with no data loaded |
std::invalid_argument | get_city(country) with unknown country code |