Skip to content

Datasources

A datasource resolves a location name (e.g. "Lausanne") to a geometry. etter ships with three implementations and a composable aggregator.

All datasources implement the GeoDataSource protocol — no inheritance required.

SwissNames3D

Wraps the swisstopo SwissNames3D dataset (Shapefile/GDB). Covers Switzerland with ~80 geographic feature types.

python
from etter.datasources import SwissNames3DSource

source = SwissNames3DSource("data/swissnames3d/")
results = source.search("Lausanne", type="settlement", max_results=5)

Handles EPSG:2056 → WGS84 reprojection and fuzzy name matching automatically. Data is loaded lazily on first use.

IGN BD-CARTO

Wraps the IGN BD-CARTO GeoPackage for France. Covers 14 thematic layers (administrative boundaries, hydrography, named places, protected areas, etc.).

python
from etter.datasources import IGNBDCartoSource

source = IGNBDCartoSource("data/bdcarto/")
results = source.search("Rhône", type="water")

Handles Lambert-93 (EPSG:2154) → WGS84 reprojection and French article stripping (le, la, l', les, de, du, des).

PostGIS

A generic PostGIS datasource that works with any table. The connection is validated at construction time.

python
from etter.datasources import PostGISDataSource

source = PostGISDataSource(
    connection="postgresql+psycopg2://...",
    table="public.my_geodata",
    type_map={"municipality": ["COMMUNE"], "river": ["COURS_EAU"]},
)
results = source.search("Genève", type="city")

The type_map maps normalized type names (as used by etter's type system) to lists of raw values in the database's type column — the same direction as SwissNames3DSource's OBJEKTART_TYPE_MAP.

Install the extra for PostGIS support:

bash
uv sync --extra postgis

The search cascade is: exact match → fuzzy (pg_trgm) → ILIKE. CRS reprojection is done at query time via ST_Transform when the stored SRID differs from 4326.

See PostGISDataSource for the full constructor reference.

CompositeDataSource

Fan-out across multiple datasources. Sources are queried in order and results are accumulated until max_results is reached:

python
from etter.datasources import CompositeDataSource, SwissNames3DSource, IGNBDCartoSource

source = CompositeDataSource(
    SwissNames3DSource("data/swissnames3d/"),
    IGNBDCartoSource("data/bdcarto/"),
)
results = source.search("Geneva", type="settlement")

Type System

All datasources share a common type hierarchy for fuzzy type matching. Query with a category and it matches all concrete types within it:

CategoryConcrete types (examples)
waterlake, river, pond, spring, glacier
landformsmountain, peak, hill, pass, valley
settlementcity, town, village, hamlet
administrativecountry, canton, municipality, region
transporttrain_station, airport, road, bridge
buildingbuilding, tower, monument, fountain
amenityrestaurant, hospital, school, park
naturalcave, forest, nature_reserve
python
# Matches lake, river, pond, spring, ...
source.search("Morat", type="water")

# Matches only "lake"
source.search("Morat", type="lake")

See location_types for the complete hierarchy.

Implementing a Custom Datasource

Any class with a search method matching the protocol qualifies:

python
class MyDataSource:
    def get_available_types(self) -> list[str]:
        return ["city", "river", "lake"]

    def search(
        self,
        name: str,
        type: str | None = None,
        max_results: int = 10,
    ) -> list[dict]:
        # Return standard GeoJSON feature dicts
        ...

See GeoDataSource for the full protocol definition.

Released under the BSD-3-Clause License.