Filialsuche über Kartenausschnitt
Eine Filialsuche lädt die Standorte im aktuell sichtbaren Kartenausschnitt und zeigt, wie weit
jeder davon entfernt ist. Dieses Beispiel nutzt
Suche per Rechteck mit der Bounding-Box der
Karte sowie Addition für die Entfernung zu einem Referenzpunkt.
Nur serverseitig
Anders als die übrigen SmartMaps-Dienste authentifiziert sich Address Search über HTTP Basic
Auth (Authorization: Basic base64("<SystemPartner>:<SecurityID>")). Diese Zugangsdaten
lassen sich nicht auf eine Domain beschränken und dürfen daher niemals in den Browser
gelangen — sie wären im Seitenquelltext lesbar und von überall nutzbar.
Rufen Sie die API aus Ihrem eigenen Backend auf und geben Sie nur das Ergebnis an Ihr Frontend weiter. Das Muster zeigt der Abschnitt Serverseitige Integration. Aus demselben Grund hat diese Seite kein im Browser lauffähiges Beispiel.
Anfrage
Lux/Luy sind die obere linke, Rlx/Rly die untere rechte Ecke des Kartenausschnitts.
Branches wählt die zu suchenden Kategorien.
curl -H "Authorization: Basic <TOKEN>" \
"https://yellowmap.de/api_rst/v2/addresssearch/ByBranchesAndRectangle?\
Branches=GACP&Top=2\
&Lux=8.552856&Luy=49.553725&Rlx=8.659973&Rly=48.623831\
&Addition=LocXForDistanceCalculation%3D8.47029%26LocYForDistanceCalculation%3D49.00129"
Entfernung zu einem Referenzpunkt
Addition enthält den Punkt, von dem aus die Entfernung gemessen wird — typischerweise die
Position des Besuchers oder die gesuchte Adresse:
| Schlüssel | Beschreibung |
|---|---|
LocXForDistanceCalculation |
Längengrad des Referenzpunkts, von dem aus gemessen wird |
LocYForDistanceCalculation |
Breitengrad des Referenzpunkts |
Jeder Treffer enthält dann BasicData.Geo.Distance — die Luftlinienentfernung in Metern.
Ohne Addition bleibt das Feld leer.
Trennzeichen innerhalb von Addition kodieren
Addition enthält eigene key=value-Paare, deren Trennzeichen URL-kodiert werden müssen:
= wird zu %3D, & wird zu %26. Unkodiert werden die Paare als normale
Query-Parameter gelesen und die Entfernung wird nicht berechnet.
Antwort
Auf die Felder gekürzt, die eine Filialsuche benötigt:
{
"Paging": { "Page": 1, "MaxPage": 1, "Count": 2, "MaxCount": 5 },
"AddressItems": [
{
"BasicData": {
"Identifiers": { "YMID": "XrU/pHy5NmEm3LD6Z6wh1Q==", "YMIDDecoded": "800059122" },
"Address": {
"CompanyName": "Geldautomat BBBank eG",
"Street": "Schlossstr. 2 a",
"Zip": "76646",
"City": "Bruchsal",
"Country": "D"
},
"Contact": { "Phone": "", "Email": null, "Url": "" },
"Geo": { "XCoord": "8.59422", "YCoord": "49.12565", "Distance": "16514" },
"BranchListElements": [
{ "BranchCode": "B090010016", "BranchText": "Geldautomaten" }
]
}
}
]
}
Nutzen Sie Identifiers.YMID als stabilen Schlüssel eines Datensatzes, Geo.XCoord/Geo.YCoord
zum Setzen des Markers und Geo.Distance zum Sortieren der Trefferliste.
Serverseitige Integration
Die Zugangsdaten bleiben im Backend, der Browser spricht ausschließlich mit Ihrem eigenen
Endpunkt. Der Ausschnitt setzt Node.js 18 oder neuer voraus (wegen des eingebauten
fetch) sowie ESM — dazu "type": "module" in der package.json setzen:
// server.js — minimal proxy (Node.js with Express)
import express from 'express'
const app = express()
const BASE = 'https://yellowmap.de/api_rst/v2/addresssearch'
// Credentials stay on the server, e.g. from environment variables
const token = Buffer
.from(`${process.env.SM_SYSTEM_PARTNER}:${process.env.SM_SECURITY_ID}`)
.toString('base64')
app.get('/api/stores', async (req, res) => {
const { lux, luy, rlx, rly, lon, lat } = req.query
const params = new URLSearchParams({
Branches: 'GACP',
Top: '50',
Lux: lux, Luy: luy, Rlx: rlx, Rly: rly,
})
// URLSearchParams encodes the inner "=" and "&" of Addition for us
if (lon && lat) {
params.set('Addition',
`LocXForDistanceCalculation=${lon}&LocYForDistanceCalculation=${lat}`)
}
const upstream = await fetch(`${BASE}/ByBranchesAndRectangle?${params}`, {
headers: { Authorization: `Basic ${token}` },
})
if (!upstream.ok) {
return res.status(upstream.status).json({ error: 'Address Search request failed' })
}
const data = await upstream.json()
// Forward only what the frontend needs — not the full payload
res.json(data.AddressItems.map(({ BasicData: b }) => ({
id: b.Identifiers.YMID,
name: b.Address.CompanyName,
street: b.Address.Street,
zip: b.Address.Zip,
city: b.Address.City,
lng: Number(b.Geo.XCoord),
lat: Number(b.Geo.YCoord),
distance: b.Geo.Distance ? Number(b.Geo.Distance) : null,
})))
})
app.listen(3000)
Das Frontend fragt dann den eigenen Endpunkt ab, sobald die Karte zur Ruhe kommt:
map.on('moveend', async () => {
const bounds = map.getBounds()
const query = new URLSearchParams({
lux: bounds.getWest(), luy: bounds.getNorth(),
rlx: bounds.getEast(), rly: bounds.getSouth(),
lon: reference.lng, lat: reference.lat,
})
const stores = await fetch(`/api/stores?${query}`).then(r => r.json())
// Records without a distance (request sent without Addition) sort to the end —
// a plain `a.distance - b.distance` would put them first.
stores.sort((a, b) => (a.distance ?? Infinity) - (b.distance ?? Infinity))
// … render markers and the result list
})
Attribution
Jeder Datensatz enthält einen Copyright-Block, die Antwort zusätzlich AddressItemsCopyright.
Zeigen Sie den mitgelieferten Copyright-Hinweis zusammen mit den Daten an — siehe
Attribution & Lizenzierung.