<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>genom - Rust</title>
<link rel="icon" href="favicon.ico">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<a href="/" class="logo-section">
<img src="../logo.webp" alt="Genom" class="logo-icon">
<div class="logo-info">
<div class="logo-title">genom</div>
<div class="logo-version">VERSION</div>
</div>
</a>
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-title">Crate genom</div>
<a href="#crate-overview" class="nav-link">Overview</a>
</div>
<div class="nav-section">
<div class="nav-title">Modules</div>
<a href="#mod-enrichment" class="nav-link">enrichment</a>
<a href="#mod-types" class="nav-link">types</a>
</div>
<div class="nav-section">
<div class="nav-title">Structs</div>
<a href="#struct-Geocoder" class="nav-link">Geocoder</a>
<a href="#struct-Place" class="nav-link">Place</a>
<a href="#struct-Location" class="nav-link">Location</a>
<a href="#struct-PlaceInput" class="nav-link">PlaceInput</a>
</div>
<div class="nav-section">
<div class="nav-title">Reference</div>
<a href="#binary-format" class="nav-link">Binary format</a>
</div>
<div class="nav-section">
<div class="nav-title">Functions</div>
<a href="#fn-lookup" class="nav-link">lookup</a>
<a href="#fn-enrich_place" class="nav-link">enrich_place</a>
</div>
</nav>
</aside>
<main class="content">
<div class="content-inner">
<section id="crate-overview" class="doc-section">
<h1 class="page-title">Crate genom</h1>
<div class="doc-block">
<p class="doc-summary">
Fast reverse geocoding library with enriched location data. Convert coordinates to detailed place information including timezone, currency, region, and more.
</p>
<h2>Features</h2>
<ul>
<li><strong>Simple API</strong> - Single function call: <code><a href="#fn-lookup">genom::lookup(lat, lon)</a></code></li>
<li><strong>Rich Data</strong> - Returns 16+ fields including timezone, currency, postal code, region</li>
<li><strong>Fast Lookups</strong> - Grid-based spatial indexing for sub-millisecond queries</li>
<li><strong>Zero Config</strong> - Database builds automatically on first install</li>
<li><strong>Thread-Safe</strong> - Global singleton with lazy initialization</li>
<li><strong>Compact</strong> - Efficient binary format with string interning</li>
</ul>
<h2>Quick Start</h2>
<div class="code-example">
<pre><span class="keyword">use</span> genom;
<span class="keyword">fn</span> <span class="fn-name">main</span>() {
<span class="comment">// Lookup coordinates</span>
<span class="keyword">if let</span> <span class="keyword">Some</span>(place) = genom::<span class="fn-name">lookup</span>(<span class="number">40.7128</span>, <span class="number">-74.0060</span>) {
<span class="macro">println!</span>(<span class="string">"{}, {}"</span>, place.city, place.country_name);
<span class="comment">// Output: New York, United States</span>
}
}</pre>
</div>
</div>
</section>
<section id="mod-enrichment" class="doc-section">
<h1 class="section-title">
<span class="item-kind">Module</span>
enrichment
</h1>
<div class="doc-block">
<p class="doc-summary">
Data enrichment module that adds computed fields to raw geographic data.
</p>
<p>
This module provides functionality to enrich basic place data with additional information such as:
</p>
<ul>
<li>Country names from ISO codes</li>
<li>Currency codes by country</li>
<li>Continent information</li>
<li>EU membership status</li>
<li>Timezone calculations (offset, abbreviation, DST status)</li>
</ul>
<p>
All enrichment data is stored in static lazy-initialized hash maps for efficient lookup.
</p>
</div>
</section>
<section id="mod-types" class="doc-section">
<h1 class="section-title">
<span class="item-kind">Module</span>
types
</h1>
<div class="doc-block">
<p class="doc-summary">
Core data structures for geographic information.
</p>
<p>
This module defines the fundamental types used throughout the library:
</p>
<ul>
<li><a href="#struct-Place"><code>Place</code></a> - Enriched output with complete geographic context</li>
<li><a href="#struct-Location"><code>Location</code></a> - Simple coordinate pair with distance calculations</li>
</ul>
</div>
</section>
<section id="struct-Geocoder" class="doc-section">
<h1 class="section-title">
<span class="item-kind">Struct</span>
Geocoder
</h1>
<div class="item-signature">
<pre><span class="keyword">pub struct</span> <span class="type-name">Geocoder</span> { <span class="comment">/* private fields */</span> }</pre>
</div>
<div class="doc-block">
<p class="doc-summary">
The core geocoding engine. Manages the spatial database and performs coordinate lookups.
</p>
<h2>Conceptual Role</h2>
<p>
<code>Geocoder</code> is the transport layer for all geographic queries. It handles:
</p>
<ul>
<li>Zero-copy parse of the embedded binary blob</li>
<li>Grid-based spatial indexing for O(1) lookups</li>
<li>Nearest-neighbor search across grid cells</li>
<li>String table resolution for compact storage</li>
</ul>
<h2>What This Type Does NOT Do</h2>
<ul>
<li>Data enrichment (handled by <a href="#mod-enrichment">enrichment</a> module)</li>
<li>Distance calculations (delegated to <a href="#struct-Location">Location</a> type)</li>
<li>Thread synchronization (uses <code>OnceLock</code> for initialization)</li>
</ul>
<h2>Invariants</h2>
<ul>
<li>After construction, the database is fully loaded and valid</li>
<li>Grid keys are consistent with coordinate quantization</li>
<li>String indices into the embedded blob are bounds-checked at parse time</li>
</ul>
<h2>Thread Safety</h2>
<p>
<code>Geocoder</code> is <code>Send</code> but not <code>Sync</code>. However, the global instance
accessed via <a href="#method-global"><code>Geocoder::global()</code></a> is safe to use from multiple threads
because all operations are read-only after initialization.
</p>
</div>
<h2 class="impl-title">Implementations</h2>
<div class="impl-block">
<h3 id="method-global" class="method-title">
<span class="item-kind">pub fn</span>
<span class="method-name">global</span>() → &'static Self
</h3>
<div class="doc-block">
<p class="doc-summary">
Returns a reference to the global geocoder singleton.
</p>
<h4>Initialization</h4>
<p>
First call zero-copy-parses the embedded binary blob and builds two
<code>FxHashMap</code> indexes. Subsequent calls return the cached
instance. Initialization is thread-safe via <code>OnceLock</code>.
</p>
<h4>Panics</h4>
<p>
Panics if database initialization fails (corrupted data, out of memory).
This is intentional - the library cannot function without a valid database.
</p>
<h4>Examples</h4>
<div class="code-example">
<pre><span class="keyword">use</span> genom::Geocoder;
<span class="keyword">let</span> geocoder = Geocoder::<span class="fn-name">global</span>();
<span class="keyword">let</span> place = geocoder.<span class="fn-name">lookup</span>(<span class="number">51.5074</span>, <span class="number">-0.1278</span>);</pre>
</div>
</div>
</div>
<div class="impl-block">
<h3 id="method-lookup" class="method-title">
<span class="item-kind">pub fn</span>
<span class="method-name">lookup</span>(&self, latitude: f64, longitude: f64) → Option<<a href="#struct-Place">Place</a>>
</h3>
<div class="doc-block">
<p class="doc-summary">
Finds the nearest place to the given coordinates.
</p>
<h4>Algorithm</h4>
<ol>
<li>Quantize coordinates into the 0.1° city grid cell</li>
<li>Expanding-ring search across neighboring cells until a candidate is found</li>
<li>Refine the postal code from the matching country's 0.01° postal grid</li>
<li>Enrich with country/currency/continent/timezone metadata</li>
</ol>
<h4>Returns</h4>
<p>
<code>Some(Place)</code> if a location is found within search radius, <code>None</code> otherwise.
Ocean coordinates typically return <code>None</code> unless near coastal cities.
</p>
<h4>Examples</h4>
<div class="code-example">
<pre><span class="keyword">use</span> genom::Geocoder;
<span class="keyword">let</span> geocoder = Geocoder::<span class="fn-name">global</span>();
<span class="comment">// Paris, France</span>
<span class="keyword">let</span> place = geocoder.<span class="fn-name">lookup</span>(<span class="number">48.8566</span>, <span class="number">2.3522</span>).<span class="fn-name">unwrap</span>();
<span class="macro">assert_eq!</span>(place.city, <span class="string">"Paris"</span>);
<span class="macro">assert_eq!</span>(place.country_code, <span class="string">"FR"</span>);</pre>
</div>
</div>
</div>
</section>
<section id="struct-Place" class="doc-section">
<h1 class="section-title">
<span class="item-kind">Struct</span>
Place
</h1>
<div class="item-signature">
<pre><span class="keyword">pub struct</span> <span class="type-name">Place</span> {
<span class="keyword">pub</span> city: <span class="type">String</span>,
<span class="keyword">pub</span> region: <span class="type">String</span>,
<span class="keyword">pub</span> region_code: <span class="type">String</span>,
<span class="keyword">pub</span> district: <span class="type">String</span>,
<span class="keyword">pub</span> country_code: <span class="type">String</span>,
<span class="keyword">pub</span> country_name: <span class="type">String</span>,
<span class="keyword">pub</span> postal_code: <span class="type">String</span>,
<span class="keyword">pub</span> timezone: <span class="type">String</span>,
<span class="keyword">pub</span> timezone_abbr: <span class="type">String</span>,
<span class="keyword">pub</span> utc_offset: <span class="type">i32</span>,
<span class="keyword">pub</span> utc_offset_str: <span class="type">String</span>,
<span class="keyword">pub</span> latitude: <span class="type">f64</span>,
<span class="keyword">pub</span> longitude: <span class="type">f64</span>,
<span class="keyword">pub</span> currency: <span class="type">String</span>,
<span class="keyword">pub</span> continent_code: <span class="type">String</span>,
<span class="keyword">pub</span> continent_name: <span class="type">String</span>,
<span class="keyword">pub</span> is_eu: <span class="type">bool</span>,
<span class="keyword">pub</span> dst_active: <span class="type">bool</span>,
}</pre>
</div>
<div class="doc-block">
<p class="doc-summary">
The enriched output type containing complete geographic context for a location.
</p>
<p>
This struct is returned by <a href="#fn-lookup"><code>lookup()</code></a> and contains 18 fields
providing comprehensive information about a geographic location.
</p>
</div>
<h2 class="fields-title">Fields</h2>
<div class="field-list">
<div class="field-item">
<h3 class="field-name">city: String</h3>
<div class="doc-block">
<p>City or locality name (e.g., "New York", "Tokyo", "Paris")</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">region: String</h3>
<div class="doc-block">
<p>State, province, or administrative region full name (e.g., "California", "Tokyo", "Île-de-France")</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">region_code: String</h3>
<div class="doc-block">
<p>ISO 3166-2 region code (e.g., "CA" for California, "13" for Tokyo)</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">district: String</h3>
<div class="doc-block">
<p>County, district, or sub-region (e.g., "Los Angeles County", "Chiyoda")</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">country_code: String</h3>
<div class="doc-block">
<p>ISO 3166-1 alpha-2 country code (e.g., "US", "JP", "FR")</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">country_name: String</h3>
<div class="doc-block">
<p>Full country name (e.g., "United States", "Japan", "France")</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">postal_code: String</h3>
<div class="doc-block">
<p>Postal or ZIP code (e.g., "10001", "100-0001", "75001")</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">timezone: String</h3>
<div class="doc-block">
<p>IANA timezone identifier (e.g., "America/New_York", "Asia/Tokyo", "Europe/Paris")</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">timezone_abbr: String</h3>
<div class="doc-block">
<p>Current timezone abbreviation (e.g., "EST", "JST", "CET"). Changes based on DST.</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">utc_offset: i32</h3>
<div class="doc-block">
<p>Current UTC offset in seconds (e.g., -18000 for UTC-5, 32400 for UTC+9)</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">utc_offset_str: String</h3>
<div class="doc-block">
<p>Formatted UTC offset string (e.g., "UTC-5", "UTC+9", "UTC+5:30")</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">latitude: f64</h3>
<div class="doc-block">
<p>Precise latitude coordinate in decimal degrees (-90 to 90)</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">longitude: f64</h3>
<div class="doc-block">
<p>Precise longitude coordinate in decimal degrees (-180 to 180)</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">currency: String</h3>
<div class="doc-block">
<p>ISO 4217 currency code (e.g., "USD", "JPY", "EUR")</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">continent_code: String</h3>
<div class="doc-block">
<p>Two-letter continent code (e.g., "NA" for North America, "AS" for Asia, "EU" for Europe)</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">continent_name: String</h3>
<div class="doc-block">
<p>Full continent name (e.g., "North America", "Asia", "Europe")</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">is_eu: bool</h3>
<div class="doc-block">
<p>Whether the location is in a European Union member state</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">dst_active: bool</h3>
<div class="doc-block">
<p>Whether daylight saving time is currently active for this location</p>
</div>
</div>
</div>
<h2>Examples</h2>
<div class="code-example">
<pre><span class="keyword">use</span> genom;
<span class="keyword">let</span> place = genom::<span class="fn-name">lookup</span>(<span class="number">40.7128</span>, <span class="number">-74.0060</span>).<span class="fn-name">unwrap</span>();
<span class="macro">println!</span>(<span class="string">"City: {}"</span>, place.city);
<span class="macro">println!</span>(<span class="string">"Country: {}"</span>, place.country_name);
<span class="macro">println!</span>(<span class="string">"Timezone: {} ({})"</span>, place.timezone, place.timezone_abbr);
<span class="macro">println!</span>(<span class="string">"Currency: {}"</span>, place.currency);
<span class="macro">println!</span>(<span class="string">"EU Member: {}"</span>, place.is_eu);</pre>
</div>
</section>
<section id="struct-Location" class="doc-section">
<h1 class="section-title">
<span class="item-kind">Struct</span>
Location
</h1>
<div class="item-signature">
<pre><span class="keyword">pub struct</span> <span class="type-name">Location</span> {
<span class="keyword">pub</span> latitude: <span class="type">f64</span>,
<span class="keyword">pub</span> longitude: <span class="type">f64</span>,
}</pre>
</div>
<div class="doc-block">
<p class="doc-summary">
A coordinate pair with distance calculation capabilities.
</p>
<p>
This is a simple wrapper around latitude and longitude coordinates that provides
utility methods for geographic calculations.
</p>
</div>
<h2 class="fields-title">Fields</h2>
<div class="field-list">
<div class="field-item">
<h3 class="field-name">latitude: f64</h3>
<div class="doc-block">
<p>Latitude in decimal degrees (-90 to 90)</p>
</div>
</div>
<div class="field-item">
<h3 class="field-name">longitude: f64</h3>
<div class="doc-block">
<p>Longitude in decimal degrees (-180 to 180)</p>
</div>
</div>
</div>
<h2 class="impl-title">Implementations</h2>
<div class="impl-block">
<h3 id="method-new" class="method-title">
<span class="item-kind">pub fn</span>
<span class="method-name">new</span>(latitude: f64, longitude: f64) → Self
</h3>
<div class="doc-block">
<p class="doc-summary">
Constructs a new Location from coordinates.
</p>
<h4>Examples</h4>
<div class="code-example">
<pre><span class="keyword">use</span> genom::Location;
<span class="keyword">let</span> loc = Location::<span class="fn-name">new</span>(<span class="number">40.7128</span>, <span class="number">-74.0060</span>);
<span class="macro">assert_eq!</span>(loc.latitude, <span class="number">40.7128</span>);
<span class="macro">assert_eq!</span>(loc.longitude, <span class="number">-74.0060</span>);</pre>
</div>
</div>
</div>
<div class="impl-block">
<h3 id="method-distance_to" class="method-title">
<span class="item-kind">pub fn</span>
<span class="method-name">distance_to</span>(&self, other: &Location) → f64
</h3>
<div class="doc-block">
<p class="doc-summary">
Calculates the great-circle distance to another location using the haversine formula.
</p>
<p>
Returns the distance in kilometers. This calculation assumes a spherical Earth
with radius 6371 km, which provides accuracy within 0.5% for most distances.
</p>
<h4>Examples</h4>
<div class="code-example">
<pre><span class="keyword">use</span> genom::Location;
<span class="keyword">let</span> nyc = Location::<span class="fn-name">new</span>(<span class="number">40.7128</span>, <span class="number">-74.0060</span>);
<span class="keyword">let</span> la = Location::<span class="fn-name">new</span>(<span class="number">34.0522</span>, <span class="number">-118.2437</span>);
<span class="keyword">let</span> distance = nyc.<span class="fn-name">distance_to</span>(&la);
<span class="macro">assert!</span>(distance > <span class="number">3900.0</span> && distance < <span class="number">4000.0</span>); <span class="comment">// ~3944 km</span></pre>
</div>
</div>
</div>
</section>
<section id="binary-format" class="doc-section">
<h1 class="section-title">
<span class="item-kind">Reference</span>
Binary format
</h1>
<div class="doc-block">
<p class="doc-summary">
The compiled database is a single contiguous blob (<code>geo.bin</code>, ~37 MB) embedded
into your binary via <code>include_bytes!</code> and exposed as <code>&'static [u8]</code>.
All previously-public storage types (<code>CompactPlace</code>, <code>Database</code>)
are gone — there is no <code>bincode</code>, no <code>Vec</code> deserialization, no decompression.
</p>
<h2>Layout</h2>
<ol>
<li><strong>Header</strong> — magic <code>"GEO1"</code>, version, 12× <code>u32</code> section offsets/lengths</li>
<li><strong>Strings</strong> — interned UTF-8 table with <code>u32</code> offsets</li>
<li><strong>Country codes</strong> — packed 2-byte ISO 3166-1 alpha-2 codes indexed by <code>u16</code></li>
<li><strong>City grid</strong> — <code>FxHashMap<u32, u32></code> cell → byte offset, varint+zigzag deltas</li>
<li><strong>Cities blob</strong> — per-cell varint stream of <code>(Δlat, Δlon, name, a1, a2, a1c, tz, cc)</code></li>
<li><strong>Postal directory</strong> — per-country byte ranges</li>
<li><strong>Postal sections</strong> — country-local string table + 0.01° cell grid + delta-encoded entries</li>
<li><strong>Country polygon dir + blob</strong> — Natural Earth simplified rings with bbox, varint deltas</li>
</ol>
<h2>Lookup hot path</h2>
<ul>
<li>Zero-copy slicing of the embedded blob — no allocations per query</li>
<li>Two <code>FxHashMap</code> indexes built once at startup (grid + postal_by_cc)</li>
<li>Distance computed in fixed-point <code>i64</code> squared microdegrees</li>
</ul>
<h2>Build pipeline</h2>
<p>
<code>build.rs</code> downloads GeoNames <code>cities500</code>, <code>admin1/2</code>,
<code>countryInfo</code>, <code>allCountries.zip</code> (postal) and Natural Earth country
polygons, then runs the encoder in <code>build/builder.rs</code> to emit
<code>OUT_DIR/geo.bin</code>. Downloads are cached in <code>OUT_DIR/geonames-cache/</code>.
Skip the build with <code>--features no-build-database</code> (lookups then return <code>None</code>).
</p>
</div>
</section>
<section id="struct-PlaceInput" class="doc-section">
<h1 class="section-title">
<span class="item-kind">Struct</span>
PlaceInput
</h1>
<div class="item-signature">
<pre><span class="keyword">pub struct</span> <span class="type-name">PlaceInput</span><'a> {
<span class="keyword">pub</span> city: &'a <span class="type">str</span>,
<span class="keyword">pub</span> region: &'a <span class="type">str</span>,
<span class="keyword">pub</span> region_code: &'a <span class="type">str</span>,
<span class="keyword">pub</span> district: &'a <span class="type">str</span>,
<span class="keyword">pub</span> country_code: &'a <span class="type">str</span>,
<span class="keyword">pub</span> postal_code: &'a <span class="type">str</span>,
<span class="keyword">pub</span> timezone: &'a <span class="type">str</span>,
<span class="keyword">pub</span> latitude: <span class="type">f64</span>,
<span class="keyword">pub</span> longitude: <span class="type">f64</span>,
}</pre>
</div>
<div class="doc-block">
<p class="doc-summary">
Input structure for the <a href="#fn-enrich_place"><code>enrich_place</code></a> function.
</p>
<p>
This struct contains the basic geographic data that will be enriched with additional
computed fields (country name, currency, continent, timezone details, etc.).
</p>
<p>
Uses borrowed string slices to avoid unnecessary allocations during the enrichment process.
</p>
</div>
<h2 class="fields-title">Fields</h2>
<div class="field-list">
<div class="field-item">
<h3 class="field-name">city: &'a str</h3>
<div class="doc-block"><p>City name</p></div>
</div>
<div class="field-item">
<h3 class="field-name">region: &'a str</h3>
<div class="doc-block"><p>Region/state name</p></div>
</div>
<div class="field-item">
<h3 class="field-name">region_code: &'a str</h3>
<div class="doc-block"><p>Region code</p></div>
</div>
<div class="field-item">
<h3 class="field-name">district: &'a str</h3>
<div class="doc-block"><p>District/county name</p></div>
</div>
<div class="field-item">
<h3 class="field-name">country_code: &'a str</h3>
<div class="doc-block"><p>ISO country code</p></div>
</div>
<div class="field-item">
<h3 class="field-name">postal_code: &'a str</h3>
<div class="doc-block"><p>Postal/ZIP code</p></div>
</div>
<div class="field-item">
<h3 class="field-name">timezone: &'a str</h3>
<div class="doc-block"><p>IANA timezone identifier</p></div>
</div>
<div class="field-item">
<h3 class="field-name">latitude: f64</h3>
<div class="doc-block"><p>Latitude coordinate</p></div>
</div>
<div class="field-item">
<h3 class="field-name">longitude: f64</h3>
<div class="doc-block"><p>Longitude coordinate</p></div>
</div>
</div>
</section>
<section id="fn-lookup" class="doc-section">
<h1 class="section-title">
<span class="item-kind">Function</span>
lookup
</h1>
<div class="item-signature">
<pre><span class="keyword">pub fn</span> <span class="fn-name">lookup</span>(
latitude: <span class="type">f64</span>,
longitude: <span class="type">f64</span>
) → <span class="type">Option</span><<a href="#struct-Place">Place</a>></pre>
</div>
<div class="doc-block">
<p class="doc-summary">
Performs reverse geocoding on the given coordinates, returning enriched place data if found.
</p>
<h2>Conceptual Role</h2>
<p>
This is the primary entry point for all geocoding operations. It abstracts away
database access, spatial indexing, and data enrichment into a single call.
</p>
<h2>What This Function Does</h2>
<ul>
<li>Accesses the global geocoder singleton (lazy initialization on first call)</li>
<li>Performs grid-based spatial lookup to find nearest place</li>
<li>Enriches raw data with timezone, currency, and regional information</li>
<li>Returns <code>None</code> if no place found within search radius</li>
</ul>
<h2>Thread Safety</h2>
<p>
This function is thread-safe and can be called concurrently from multiple threads.
The underlying database is initialized once and shared via a static <code>OnceLock</code>.
</p>
<h2>Performance</h2>
<p>
Sub-microsecond per lookup in steady state. First call incurs
a small one-shot index build (~5 ms). Subsequent calls are lock-free reads.
</p>
<h2>Examples</h2>
<div class="code-example">
<pre><span class="keyword">use</span> genom;
<span class="comment">// Tokyo coordinates</span>
<span class="keyword">if let</span> <span class="keyword">Some</span>(place) = genom::<span class="fn-name">lookup</span>(<span class="number">35.6762</span>, <span class="number">139.6503</span>) {
<span class="macro">println!</span>(<span class="string">"{}, {}"</span>, place.city, place.country_name);
<span class="macro">println!</span>(<span class="string">"Timezone: {}"</span>, place.timezone);
<span class="macro">println!</span>(<span class="string">"Currency: {}"</span>, place.currency);
}
<span class="comment">// Ocean coordinates return None</span>
<span class="macro">assert!</span>(genom::<span class="fn-name">lookup</span>(<span class="number">0.0</span>, <span class="number">-160.0</span>).<span class="fn-name">is_none</span>());</pre>
</div>
</div>
</section>
<section id="fn-enrich_place" class="doc-section">
<h1 class="section-title">
<span class="item-kind">Function</span>
enrich_place
</h1>
<div class="item-signature">
<pre><span class="keyword">pub fn</span> <span class="fn-name">enrich_place</span>(input: <a href="#struct-PlaceInput">PlaceInput</a>) → <a href="#struct-Place">Place</a></pre>
</div>
<div class="doc-block">
<p class="doc-summary">
Enriches basic place data with computed fields.
</p>
<p>
This function takes a <a href="#struct-PlaceInput"><code>PlaceInput</code></a> containing
basic geographic information and returns a fully enriched <a href="#struct-Place"><code>Place</code></a>
with additional computed fields.
</p>
<h2>Enrichment Process</h2>
<ol>
<li><strong>Timezone Parsing:</strong> Parses the IANA timezone to extract current offset, abbreviation, and DST status using <code>chrono-tz</code></li>
<li><strong>Country Lookup:</strong> Maps country code to full country name using static hash map</li>
<li><strong>Currency Lookup:</strong> Maps country code to ISO 4217 currency code</li>
<li><strong>Continent Lookup:</strong> Maps country code to continent code and name</li>
<li><strong>EU Status:</strong> Checks if country is an EU member state</li>
</ol>
<h2>Static Data Sources</h2>
<p>
All enrichment data is stored in static <code>LazyLock<FxHashMap></code> instances:
</p>
<ul>
<li><code>COUNTRY_NAMES</code> - 200+ country code to name mappings</li>
<li><code>COUNTRY_CURRENCIES</code> - 200+ country code to currency mappings</li>
<li><code>COUNTRY_CONTINENTS</code> - 200+ country code to continent mappings</li>
<li><code>CONTINENT_NAMES</code> - 7 continent code to name mappings</li>
<li><code>EU_COUNTRIES</code> - 27 EU member states</li>
</ul>
<h2>DST Detection</h2>
<p>
DST status is determined by comparing the current UTC offset with the minimum offset
observed in January and July. If the current offset differs from the minimum, DST is active.
</p>
<h2>Examples</h2>
<div class="code-example">
<pre><span class="keyword">use</span> genom::enrichment::{enrich_place, PlaceInput};
<span class="keyword">let</span> input = PlaceInput {
city: <span class="string">"New York"</span>,
region: <span class="string">"New York"</span>,
region_code: <span class="string">"NY"</span>,
district: <span class="string">"New York County"</span>,
country_code: <span class="string">"US"</span>,
postal_code: <span class="string">"10001"</span>,
timezone: <span class="string">"America/New_York"</span>,
latitude: <span class="number">40.7128</span>,
longitude: <span class="number">-74.0060</span>,
};
<span class="keyword">let</span> place = <span class="fn-name">enrich_place</span>(input);
<span class="macro">assert_eq!</span>(place.country_name, <span class="string">"United States"</span>);
<span class="macro">assert_eq!</span>(place.currency, <span class="string">"USD"</span>);
<span class="macro">assert_eq!</span>(place.continent_name, <span class="string">"North America"</span>);
<span class="macro">assert_eq!</span>(place.is_eu, <span class="keyword">false</span>);</pre>
</div>
</div>
</section>
<section class="doc-section">
<h1 class="section-title">Performance & Implementation Details</h1>
<div class="doc-block">
<h2>Memory Layout</h2>
<ul>
<li><strong>Single blob:</strong> <code>~37 MB geo.bin</code> embedded via <code>include_bytes!</code>, lives in <code>.rodata</code></li>
<li><strong>String interning:</strong> shared UTF-8 table, every string field is a <code>u32</code> offset</li>
<li><strong>Varint + zigzag deltas:</strong> coordinates and IDs stored as 1–3 byte sequences</li>
<li><strong>Two-level grid:</strong> 0.1° cells for cities, per-country 0.01° cells for postal codes</li>
</ul>
<h2>Initialization</h2>
<ol>
<li><code>include_bytes!</code> bakes <code>geo.bin</code> into the executable</li>
<li>On first <code>lookup</code>, headers are parsed into <code>&'static [u8]</code> slices</li>
<li>Two <code>FxHashMap</code> indexes are built (city grid, postal-by-country)</li>
<li>The <code>Geocoder</code> is cached in a <code>OnceLock</code> for the lifetime of the process</li>
</ol>
<h2>Thread Safety</h2>
<p>
After initialization, all operations are read-only and require no synchronization.
Multiple threads can perform lookups concurrently without contention.
</p>
<h2>Build Process</h2>
<p>
<code>build.rs</code> generates the database during <code>cargo build</code>:
</p>
<ol>
<li>Download GeoNames <code>cities500</code>, <code>admin1/2</code>, <code>countryInfo</code>, <code>allCountries</code></li>
<li>Download Natural Earth <code>ne_110m_admin_0_countries</code> shapefile</li>
<li>Parse, simplify polygon rings (Douglas–Peucker)</li>
<li>Intern strings, group cities by 0.1° cell, group postal codes per country</li>
<li>Emit a single varint-encoded blob to <code>OUT_DIR/geo.bin</code></li>
<li>Library <code>include_bytes!</code>'s the result</li>
</ol>
<p>
Downloads are cached in <code>OUT_DIR/geonames-cache/</code>, so a clean rebuild without
network works as long as the cache exists. Build time on first run: ~30–60 s.
</p>
</div>
</section>
</div>
</main>
</div>
<script>
let isScrollingFromClick = false;
let scrollEndTimeout;
document.querySelectorAll('.nav-link').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
isScrollingFromClick = true;
document.querySelectorAll('.nav-link').forEach(link => link.classList.remove('active'));
this.classList.add('active');
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
clearTimeout(scrollEndTimeout);
scrollEndTimeout = setTimeout(() => {
isScrollingFromClick = false;
}, 1000); }
});
});
function updateActiveSection() {
if (isScrollingFromClick) return;
const sections = document.querySelectorAll('.doc-section');
const scrollPosition = window.scrollY + 150;
let currentSectionId = null;
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionBottom = sectionTop + section.offsetHeight;
if (scrollPosition >= sectionTop && scrollPosition < sectionBottom) {
currentSectionId = section.getAttribute('id');
}
});
if (currentSectionId) {
document.querySelectorAll('.nav-link').forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${currentSectionId}`) {
link.classList.add('active');
}
});
}
}
let scrollTimeout;
window.addEventListener('scroll', () => {
if (scrollTimeout) {
window.cancelAnimationFrame(scrollTimeout);
}
scrollTimeout = window.requestAnimationFrame(() => {
updateActiveSection();
});
});
updateActiveSection();
</script>
</body>
</html>