Drop-in-Tools

Place-Widget (JS), Map-Widget (JS) und PHP-Client als One-Line-Setup für eigene Seiten.

🧩 Place-Widget (JavaScript)

Verwandelt jedes <input>-Feld mit dem Attribut data-geoapi-place in ein Autosuggest-Feld mit Tastatur-Navigation, Hidden-Field-Pflege und Auto-Fill.

Schnellstart

<form method="post" action="speichern.php">
  <input type="text" name="ort" data-geoapi-place placeholder="Ort eingeben">
  <button type="submit">Speichern</button>
</form>

<script src="https://geoapi.world/dist/geoapi-widget.js"></script>

Im Server-PHP stehen anschließend zur Verfügung:

  • $_POST["ort"] — der angezeigte Text
  • $_POST["ort_geonameid"] — die GeoNames-ID
  • $_POST["ort_timezone"] — die IANA-Zeitzone

data-Attribute

AttributDefaultBedeutung
Pflicht
data-geoapi-placeMarkiert das Feld als Autosuggest.
Hidden-Feld-Namen — Default jeweils {inputname}_{key}
data-geoapi-id{name}_geonameidHidden-Field-Name für die geonameid.
data-geoapi-tz{name}_timezoneHidden-Field-Name für die Zeitzone.
data-geoapi-latHidden-Field für Breitengrad.
data-geoapi-lonHidden-Field für Längengrad.
data-geoapi-countryHidden-Field für ISO-Ländercode.
Format der Anzeige
data-geoapi-format-listname + admin1 + Land + UntertitelTemplate für die Vorschlagsliste. Platzhalter {key} oder {a.b} (Dot-Pfade in verschachtelten Blöcken).
data-geoapi-format-inputname + LandWas nach Auswahl im Eingabefeld steht. Gleiche Syntax wie format-list.
API-Parameter (werden an /api/search.php durchgereicht)
data-geoapi-extendedadminWelcher extended-Block in jeden Treffer kommt. Default admin sorgt dafür, dass admin1.name (Bundesland/State) verfügbar ist. Andere Werte: country, all, country,admin,timezone. Leerer String deaktiviert.
data-geoapi-langISO-Sprachcode für lokalisierte Namen (z. B. de → „München" statt „Munich", „Bayern" statt „Bavaria").
Verhalten
data-geoapi-limit8Maximale Vorschläge in der Liste (1–30).
data-geoapi-min2Mindestlänge des Suchbegriffs.
data-geoapi-base(geraten)Alternative API-Base-URL.
data-geoapi-debugausZeigt eine Debug-Box unter dem Input.
data-geoapi-autoresolveausBeim Seitenladen automatisch suchen, wenn das Input bereits einen Wert hat (z. B. via URL-Pre-Fill), aber noch nichts ausgewählt wurde. Ohne Wert oder ="strict": nur picken bei genau 1 Treffer, sonst Liste öffnen. ="top": immer den ersten Treffer picken.

Format-Templates: verfügbare Keys

Jeder Key kann sowohl in data-geoapi-format-list/-input als auch in data-geoapi-fill verwendet werden. Verschachtelte Blöcke (admin, country) werden mit Punkt-Notation adressiert.

Verfügbar wennKeys
immer (Standard-Antwort)name, asciiname, country_code, admin1_code, feature_code, population, timezone, lat, lon, geonameid
extended=admin (Default)admin1.name, admin1.code, admin1.geonameid, admin2.name, admin2.code
extended=countrycountry.name, country.iso_3, country.continent, country.currency_code, country.languages, country.phone, country.postal_format
extended=timezonetimezone_info.tz_id, timezone_info.gmt_offset, timezone_info.dst_offset, timezone_info.raw_offset
extended=flagflag_url

Beispiel: Bundesland in der Vorschlagsliste

Default-Rendering enthält das Bundesland bereits (aus admin1.name bzw. admin1_code). Eigene Variante mit Untertitel:

<input data-geoapi-place
       data-geoapi-lang="de"
       data-geoapi-format-list="<strong>{name}</strong>, {admin1.name} ({country_code})<small>{population} Einw. · {timezone}</small>">

Auto-Fill in beliebige Elemente

Mit data-geoapi-fill="key" auf einem beliebigen Element wird dieses nach jeder Auswahl automatisch gefüllt:

<input type="text" name="ort" data-geoapi-place>

<p>Land:        <span data-geoapi-fill="country_code"></span></p>
<p>Zeitzone:    <span data-geoapi-fill="timezone"></span></p>
<p>Einwohner:   <span data-geoapi-fill="population"></span></p>
<p>Koordinaten: <span data-geoapi-fill="lat"></span>, <span data-geoapi-fill="lon"></span></p>

Timeshift — Zeitzonen-Verschiebung pro Datum

Spezielle Fill-Keys, die nicht direkt aus dem Suche-Treffer kommen, sondern den Offset für ein konkretes Datum + Zeit über /api/offset.php nachladen — DST-aware, mit historischen Regeln.

Trigger-Bedingungen:

  • Es muss ein Ort ausgewählt sein (das Hidden-Field {name}_timezone ist gefüllt) und
  • Datum/Zeit-Felder enthalten gültige Werte.

Datums-/Zeit-Änderungen ohne Ortsauswahl lösen keine API-Anfrage aus. Nach dem ersten Auswählen werden die Datum/Zeit-Felder mit Listenern versehen (debounced 250 ms, vorheriger In-Flight-Request wird gegen Race Conditions abgebrochen).

<input type="text" name="ort" data-geoapi-place>
<input type="date" name="geburtstag">
<input type="time" name="geburtszeit">

<input type="text" data-geoapi-fill="timeshift"
       data-geoapi-timeshift-date="geburtstag"
       data-geoapi-timeshift-time="geburtszeit"
       name="zeitverschiebung">

Verfügbare Fill-Keys:

KeyBeispielBedeutung
timeshift+01:00Offset im Format ±HH:MM.
timeshift_hours1, 5.5Dezimalstunden — Halbstunden-Zonen wie Indien +05:30 ergeben 5.5.
timeshift_hours_int1, 6Gerundete Ganzzahl — passt in tinyint-Felder.
timeshift_seconds3600Offset in Sekunden.
abbrevCET, CESTZeitzonen-Kürzel des Moments.
dstfalseSommerzeit aktiv?
at_utc1987-03-01T13:30:00ZDer Moment als UTC-ISO.
normalizedfalsetrue, wenn die Lokalzeit nicht existierte (Spring-Forward).

Datum-Bindung — Attribute auf dem Fill-Element:

AttributErwartetBedeutung
data-geoapi-timeshift-dateInput-NameDatum als YYYY-MM-DD oder deutsch DD.MM.YYYY (Tag/Monat 1- oder 2-stellig).
data-geoapi-timeshift-timeInput-NameZeit als HH:MM (Sekunden optional).
data-geoapi-timeshift-atInput-NameAlternative zu -date + -time: ein einzelnes Feld im Format YYYY-MM-DDTHH:MM (z. B. <input type="datetime-local">) oder DD.MM.YYYY HH:MM.

Slash-Datumsformate (11/01/1988) werden bewusst nicht unterstützt — sie sind zwischen DD/MM/YYYY und MM/DD/YYYY mehrdeutig.

URL-Pre-Fill / Auto-Resolve

Typischer Fall: das Formular wird mit URL-Parametern befüllt (z. B. ?ort=Berlin) und du brauchst trotzdem die Hidden-Felder (geonameid, lat, lon, timezone, …) ohne Klick des Nutzers.

<input type="text" name="ort" data-geoapi-place
       data-geoapi-autoresolve
       value="<?= htmlspecialchars($_GET['ort'] ?? '') ?>">

Beim Laden prüft das Widget: ist der Input gefüllt, das Hidden-_geonameid aber leer? Falls ja, läuft eine Suche. Verhalten je nach Wert des Attributs:

  • ohne Wert oder ="strict" — wenn die Suche genau einen Treffer hat, wird er still ausgewählt (Hidden-Felder + Timeshift werden ohne Sichtkontakt befüllt). Bei mehreren Treffern öffnet sich die Vorschlagsliste — der Nutzer entscheidet.
  • ="top" — der erste Treffer wird immer ausgewählt, ohne Liste. Riskant bei gängigen Namen wie „Springfield".

Beide Modi feuern geoapi:auto-resolved (siehe Events) — du kannst die Auto-Auswahl mitlesen oder weiter verarbeiten.

JavaScript-Events

Fünf Events bubbeln bis document:

EventWanndetail
geoapi:fetch-startvor jedem API-Call{ url, query }
geoapi:fetch-endnach Antwort{ url, response, results } oder { url, error }
geoapi:selectbeim Klick/Enter auf einen VorschlagTreffer-Objekt
geoapi:clearwenn der Nutzer das Feld verändert
geoapi:auto-resolvednach erfolgreichem autoresolve beim Seitenladen{ picked, candidates, mode }
document.querySelector("[data-geoapi-place]")
  .addEventListener("geoapi:select", (e) => {
    const ort = e.detail;
    console.log("Gewählt:", ort.name);
    // map.setView([ort.lat, ort.lon], 13);
  });

→ Alle Widget-Anpassungen live ausprobieren

🧩 Map-Widget (JavaScript)

Interaktive OpenStreetMap-Karte mit Klick-Reverse-Geocoding. Lädt Leaflet 1.9 automatisch nach.

Schnellstart

<div data-geoapi-map style="height: 500px"></div>
<script src="https://geoapi.world/dist/geoapi-map.js"></script>

Klick auf die Karte → Marker → Popup mit Stadt-Name + Zeitzone + Distanz. Mit Hidden-Feldern (siehe Attribute) kommen die Werte beim Form-Submit als $_POST beim Server an — analog zum Place-Widget.

Wichtigste data-Attribute

AttributDefaultBedeutung
data-geoapi-mapPflicht — markiert das Element als Karte.
data-geoapi-center50,10Initial-Zentrum als "lat,lon".
data-geoapi-zoom4Initial-Zoomlevel (0 = Welt, 19 = Hausnummer).
data-geoapi-modecitycity oder exact.
data-geoapi-markersinglesingle oder double (Klick-Marker + zweiter Marker am Treffer + Distanz-Linie).
data-geoapi-tilesOSMAlternative Tile-Server-URL — siehe Tile-Tabelle weiter unten.
data-geoapi-namePräfix für automatisch angelegte Hidden-Felder.

Auto-Fill funktioniert genauso wie beim Place-Widget: data-geoapi-fill="key" auf beliebige Elemente.

JavaScript-Events

EventWanndetail
geoapi:map-clickdirekt nach jedem Klick auf die Karte{ lat, lon }
geoapi:fetch-startvor dem API-Call{ url, lat, lon, mode }
geoapi:fetch-endnach Antwort{ url, response, hit }
geoapi:map-resultnach erfolgreichem Reverse-GeocodingTreffer + { clickedLat, clickedLon }
document.querySelector("[data-geoapi-map]")
  .addEventListener("geoapi:map-result", (e) => {
    const ort = e.detail;
    document.getElementById("name").textContent = ort.name;
    document.getElementById("timezone").textContent = ort.timezone;
  });

Frei nutzbare Tile-Server

OpenStreetMap (Default), CartoDB Positron, Voyager & Dark Matter, OpenTopoMap, Esri Imagery, CyclOSM. Jeder Anbieter hat eigene Lizenz-Bedingungen — bei produktivem Traffic eigenen Tile-Server (siehe switch2osm) oder kostenpflichtigen Anbieter (Mapbox, MapTiler, Stadia) nutzen.

<div data-geoapi-map
     data-geoapi-tiles="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"
     data-geoapi-attribution="Karte: &copy; OpenTopoMap (CC-BY-SA)"
     data-geoapi-max-zoom="17"
     style="height: 500px"></div>

→ Alle Map-Anpassungen live ausprobieren

🧩 PHP-Client

Single-File-Klasse für eigenes Server-PHP, mit optionalem Datei-Cache. Erfordert PHP 8.1+.

Schnellstart

<?php
require __DIR__ . "/GeoApiClient.php";

$api = new GeoApiClient();

// Suche
$treffer = $api->search("Münch", limit: 5);

// Reverse-Geocoding
$ort = $api->lookupCity(48.137, 11.575);
$ort = $api->lookupExact(48.137, 11.575);  // ohne Städte-Präferenz

Konstruktor mit Cache

$api = new GeoApiClient(
    baseUrl:         "https://geoapi.world",
    timeoutSeconds:  5,
    userAgent:       "MeineApp/1.0",
    cacheDir:        __DIR__ . "/cache",
    cacheTtlSeconds: 900,   // 15 Min
);

Fehlerbehandlung

Bei HTTP- oder Parsing-Fehlern wirft der Client GeoApiException:

try {
    $ort = $api->lookupCity($lat, $lon);
    if ($ort === null) {
        echo "Keine Stadt gefunden in der Nähe.";
    } else {
        echo "Stadt: " . $ort["name"];
    }
} catch (GeoApiException $e) {
    error_log("geoAPI-Fehler: " . $e->getMessage());
}