Osmium ist ein CLI-Tool für die Arbeit mit OSM-Daten (OpenStreetMap) und basiert auf der C-Bibliothek libosmium.

Search-Source extrahieren

wget https://download.geofabrik.de/europe/austria-latest.osm.pbf

osmium tags-filter austria-latest.osm.pbf \
  nwr/place \
  nwr/amenity \
  nwr/shop \
  nwr/tourism \
  nwr/leisure \
  nwr/railway \
  nwr/public_transport \
  nwr/highway \
  nwr/addr:* \
  -o search-source.osm.pbf

osmium export search-source.osm.pbf -o search-source.geojson

search.json bauen

import json
 
INPUT = "search-source.geojson"
OUTPUT = "search.json"
 
def center_of_geometry(geom):
    t = geom.get("type")
    coords = geom.get("coordinates")
 
    if t == "Point":
        return coords[0], coords[1]
 
    if t == "LineString" and coords:
        mid = coords[len(coords) // 2]
        return mid[0], mid[1]
 
    if t == "Polygon" and coords and coords[0]:
        ring = coords[0]
        mid = ring[len(ring) // 2]
        return mid[0], mid[1]
 
    if t == "MultiPolygon" and coords and coords[0] and coords[0][0]:
        ring = coords[0][0]
        mid = ring[len(ring) // 2]
        return mid[0], mid[1]
 
    return None, None
 
def norm(props, *keys):
    for k in keys:
        v = props.get(k)
        if v:
            return str(v).strip()
    return ""
 
with open(INPUT, "r", encoding="utf-8") as f:
    geo = json.load(f)
 
out = []
seen = set()
 
for feat in geo.get("features", []):
    props = feat.get("properties", {})
    geom = feat.get("geometry", {})
 
    lng, lat = center_of_geometry(geom)
    if lng is None or lat is None:
        continue
 
    name = norm(props, "name:de", "name_de", "name")
    place = norm(props, "place")
    housenumber = norm(props, "addr:housenumber")
    street = norm(props, "addr:street")
    city = norm(props, "addr:city", "addr:place")
    postcode = norm(props, "addr:postcode")
    amenity = norm(props, "amenity")
    shop = norm(props, "shop")
    tourism = norm(props, "tourism")
    leisure = norm(props, "leisure")
    railway = norm(props, "railway")
    public_transport = norm(props, "public_transport")
    highway = norm(props, "highway")
 
    item = None
 
    if housenumber and street:
        item = {
            "t": "house",
            "name": f"{street} {housenumber}",
            "street": street,
            "housenumber": housenumber,
            "city": city,
            "postcode": postcode,
            "lat": round(lat, 6),
            "lng": round(lng, 6),
        }
    elif highway and name:
        item = {
            "t": "street",
            "name": name,
            "city": city,
            "postcode": postcode,
            "lat": round(lat, 6),
            "lng": round(lng, 6),
        }
    elif place and name:
        item = {
            "t": "place",
            "name": name,
            "place": place,
            "postcode": postcode,
            "lat": round(lat, 6),
            "lng": round(lng, 6),
        }
    elif name and (amenity or shop or tourism or leisure or railway or public_transport):
        item = {
            "t": "poi",
            "name": name,
            "class": amenity or shop or tourism or leisure or railway or public_transport,
            "lat": round(lat, 6),
            "lng": round(lng, 6),
        }
 
    if not item:
        continue
 
    key = (item["t"], item["name"], item["lat"], item["lng"])
    if key in seen:
        continue
    seen.add(key)
    out.append(item)
 
with open(OUTPUT, "w", encoding="utf-8") as f:
    json.dump(out, f, ensure_ascii=False, separators=(",", ":"))
 
print(f"{len(out)} Einträge nach {OUTPUT} geschrieben")

Im Browser suchen

let SEARCH_INDEX = [];
 
async function loadSearchIndex() {
  const res = await fetch("search.json");
  SEARCH_INDEX = await res.json();
}
 
function searchLocal(query) {
  const q = query.trim().toLowerCase();
  if (!q) return [];
 
  return SEARCH_INDEX
    .filter(item => {
      const hay = [
        item.name || "",
        item.street || "",
        item.city || "",
        item.postcode || ""
      ].join(" ").toLowerCase();
 
      return hay.includes(q);
    })
    .slice(0, 20);
}