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
| Attribut | Default | Bedeutung |
|---|---|---|
| Pflicht | ||
data-geoapi-place | — | Markiert das Feld als Autosuggest. |
Hidden-Feld-Namen — Default jeweils {inputname}_{key} | ||
data-geoapi-id | {name}_geonameid | Hidden-Field-Name für die geonameid. |
data-geoapi-tz | {name}_timezone | Hidden-Field-Name für die Zeitzone. |
data-geoapi-lat | — | Hidden-Field für Breitengrad. |
data-geoapi-lon | — | Hidden-Field für Längengrad. |
data-geoapi-country | — | Hidden-Field für ISO-Ländercode. |
| Format der Anzeige | ||
data-geoapi-format-list | name + admin1 + Land + Untertitel | Template für die Vorschlagsliste. Platzhalter {key} oder {a.b} (Dot-Pfade in verschachtelten Blöcken). |
data-geoapi-format-input | name + Land | Was nach Auswahl im Eingabefeld steht. Gleiche Syntax wie format-list. |
API-Parameter (werden an /api/search.php durchgereicht) | ||
data-geoapi-extended | admin | Welcher 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-lang | — | ISO-Sprachcode für lokalisierte Namen (z. B. de → „München" statt „Munich", „Bayern" statt „Bavaria"). |
| Verhalten | ||
data-geoapi-limit | 8 | Maximale Vorschläge in der Liste (1–30). |
data-geoapi-min | 2 | Mindestlänge des Suchbegriffs. |
data-geoapi-base | (geraten) | Alternative API-Base-URL. |
data-geoapi-debug | aus | Zeigt eine Debug-Box unter dem Input. |
data-geoapi-autoresolve | aus | Beim 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 wenn | Keys |
|---|---|
| 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=country | country.name, country.iso_3, country.continent, country.currency_code, country.languages, country.phone, country.postal_format |
extended=timezone | timezone_info.tz_id, timezone_info.gmt_offset, timezone_info.dst_offset, timezone_info.raw_offset |
extended=flag | flag_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}_timezoneist 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:
| Key | Beispiel | Bedeutung |
|---|---|---|
timeshift | +01:00 | Offset im Format ±HH:MM. |
timeshift_hours | 1, 5.5 | Dezimalstunden — Halbstunden-Zonen wie Indien +05:30 ergeben 5.5. |
timeshift_hours_int | 1, 6 | Gerundete Ganzzahl — passt in tinyint-Felder. |
timeshift_seconds | 3600 | Offset in Sekunden. |
abbrev | CET, CEST | Zeitzonen-Kürzel des Moments. |
dst | false | Sommerzeit aktiv? |
at_utc | 1987-03-01T13:30:00Z | Der Moment als UTC-ISO. |
normalized | false | true, wenn die Lokalzeit nicht existierte (Spring-Forward). |
Datum-Bindung — Attribute auf dem Fill-Element:
| Attribut | Erwartet | Bedeutung |
|---|---|---|
data-geoapi-timeshift-date | Input-Name | Datum als YYYY-MM-DD oder deutsch DD.MM.YYYY (Tag/Monat 1- oder 2-stellig). |
data-geoapi-timeshift-time | Input-Name | Zeit als HH:MM (Sekunden optional). |
data-geoapi-timeshift-at | Input-Name | Alternative 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:
| Event | Wann | detail |
|---|---|---|
geoapi:fetch-start | vor jedem API-Call | { url, query } |
geoapi:fetch-end | nach Antwort | { url, response, results } oder { url, error } |
geoapi:select | beim Klick/Enter auf einen Vorschlag | Treffer-Objekt |
geoapi:clear | wenn der Nutzer das Feld verändert | — |
geoapi:auto-resolved | nach 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);
});
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
| Attribut | Default | Bedeutung |
|---|---|---|
data-geoapi-map | — | Pflicht — markiert das Element als Karte. |
data-geoapi-center | 50,10 | Initial-Zentrum als "lat,lon". |
data-geoapi-zoom | 4 | Initial-Zoomlevel (0 = Welt, 19 = Hausnummer). |
data-geoapi-mode | city | city oder exact. |
data-geoapi-marker | single | single oder double (Klick-Marker + zweiter Marker am Treffer + Distanz-Linie). |
data-geoapi-tiles | OSM | Alternative Tile-Server-URL — siehe Tile-Tabelle weiter unten. |
data-geoapi-name | — | Präfix für automatisch angelegte Hidden-Felder. |
Auto-Fill funktioniert genauso wie beim Place-Widget: data-geoapi-fill="key" auf beliebige Elemente.
JavaScript-Events
| Event | Wann | detail |
|---|---|---|
geoapi:map-click | direkt nach jedem Klick auf die Karte | { lat, lon } |
geoapi:fetch-start | vor dem API-Call | { url, lat, lon, mode } |
geoapi:fetch-end | nach Antwort | { url, response, hit } |
geoapi:map-result | nach erfolgreichem Reverse-Geocoding | Treffer + { 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: © OpenTopoMap (CC-BY-SA)"
data-geoapi-max-zoom="17"
style="height: 500px"></div>
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());
}