Hallo,
ich bin Anfänger in Sachen Openstreetmap.
Ich programmiere eine progressive Web App mit HTML, CSS und Javascript.
Der Anwender soll die Möglichkeit haben, sich eine Strecke (Startort und Zielort jeweils mit Angabe des Ortes oder Postleitzahl + Strasse) sich auf der Karte anzeigen zu lassen.
Folgender Code funktioniert für mich in einer herkömmlichen Webseite für die Anzeige einer Streckevon A nach B, die als geoJSON (hier im gleichen Verzeichnis des Webservers) vorliegt mit Marker für Start- und Endpunkt. Das ganze kann man sich unter Leaflet - Route als geoJSON einbinden anschauen.
@Vinzenz_Mai
Vielen Dank für Deine Antwort und das teilen Deines Codes.
Leider bin ich in Sachen JS nicht so fit, dass ich genau weiß was zu tun ist.
Im Browser Google Chrome bekomme ich in der Konsole diese Fehlermeldung:
Failed to load resource: the server responded with a status of 404 (Not Found)
assistent.js:241 Empfangene Routendaten:
LatLng.js:32 Uncaught Error: Invalid LatLng object: (NaN, NaN)
at new D (LatLng.js:32:9)
at j (LatLng.js:123:11)
at i.setView (Map.js:181:30)
at displayRouteOnMap (assistent.js:253:24)
at marlemsAssistent (commandos.js:290:7)
at HTMLButtonElement. (assistent.js:122:3)
index.html:1 Access to internal resource at ‘file:///C:/Projekte/Apps/HTML/Marlems%20Assistent/manifest.webmanifest’ from origin ‘null’ has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted.
Also: Du brauchst einen lokalen Webserver, um solche Sachen auszuprobieren, Start der HTML-Datei direkt aus dem Explorer heraus funktioniert nicht. Es muss nichts kompliziertes sein, so was kleines wie tinyweb reicht aus.
Alternativ könntest Du z.B. den IIS Deines Windows-Rechners aktivieren oder XAMPP nutzen (gibt es auch als portable Version, die nicht installiert werden muss)
Hier der Link zur App: https://www.marlems-assistent.de/ .
Die Anweisung heißt “autofahren”.
Ich habe eine Funktion implementiert, die eine bestimmte Adresse anzeigt (=Ort+Strasse+Hausnummer), das funktioniert.
Nur das anzeigen einer Strecke klappt nicht.
Beim “autofahren” fehlt auf jeden Fall die Geocodierung:
/* AUTOFAHREN */
keywords = [
/autofahren/i,
];
matches = validateSearchQuery(userCommandstr, keywords);
if (matches){
const startort = prompt("Geben Sie den Startort ein:");
const zielort = prompt("Geben Sie den Zielort ein:");
if (startort && zielort) {
displayRouteOnMap(startort, zielort);
} else {
alert("Start- und Zielort müssen angegeben werden.");
}
responseGiven = true;
}
Du übergibst die Texteingaben des Benutzers, typischerweise zwei Adressen, an die Funktion displayRouteOnMap, diese erwartet jedoch Koordinaten:
function displayRouteOnMap(startCoordinates, endCoordinates) {
Die Eingaben müsstest Du wie bei der Adressanzeige zuerst geocodieren lassen - und anschließend daraus die Route generieren lassen, was OSM nicht von selbst kann.
Du müsstest also einen Routingdienst befragen (oder selbst zur Verfügung stellen). Die Form der Antwort muss kein geojson sein, d.h. diese wäre entsprechend zu transformieren oder andere Funktionen zur Anzeige zu nutzen.
… und zwar eine wiederverwertbare, die Du für den Start- und Zielpunkt und auch für die Adressanzeige verwenden kannst. Das bedeutet: Du übergibst dieser Funktion eine Adresse(genauer: einen Text) und die Funktion gibt im Erfolgsfall, wenn die Eingabe von Nominatim gefunden wurde, die Koordinaten dieser Adresse zurück, könnte z.B. so aussehen:
async function geocodeAddress(vaddress) {
let url = `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(vaddress)}`;
var coords;
const response = await fetch(url);
// console.log(response);
if (response.ok) {
// Bis alle Daten da sind, kann es etwas dauern
data = await response.json();
// console.log(data);
if (data.length > 0) {
coords = [data[0].lat, data[0].lon];
}
// andernfalls nicht gefunden
}
return(coords);
}
Anschließend testen, ob das auch funktioniert, z.B. einfach statisch:
// Funktion zum Testen der Geocodierung
async function main() {
// Startpunkt (existiert)
var startAddress = "Veledastraße 9, Köln";
// Endpunkt (existiert nicht)
var endAddress = "Titusweg 17, Köln";
const startCoords = await geocodeAddress(startAddress);
console.log(startAddress);
console.log(startCoords);
const endCoords = await geocodeAddress(endAddress);
console.log(endAddress);
console.log(endCoords);
}
main();
Und Deine displayRoute könnte von der Logik so aussehen:
async function displayRoute(startAddress, endAddress) {
const startCoords = await geocodeAddress(startAddress);
const endCoords = await geocodeAddress(endAddress);
if (!(startCoords === undefined || endCoords === undefined)) {
// beide Adressen konnten geocodiert werden
// Nun brauchst Du die Routendaten
routeData = getRoute(startCoords, endCoords);
}
// Wenn die Route erfolgreich erstellt werden konnte
// Erzeuge passenden Kartenausschnitt
// Erstelle Marker für Startpunkt
// Erstelle Marker für Zielpunkt
// Zeige die Route an
}
Bei async function displayRouteOnMap(startAddress, endAddress) { gibt es folgende Fehlermeldung:
LatLng.js:32 Uncaught (in promise) Error: Invalid LatLng object: (NaN, NaN)
at new D (LatLng.js:32:9)
at j (LatLng.js:123:11)
at i.setView (Map.js:181:30)
at displayRouteOnMap (assistent.js:257:24)
Was ich online aufrufen kann, ist immer noch das gleiche wie gestern.
Grundsätzlich ist jedoch eine Umwandlung der Koordinaten von Zeichenketten in Kommazahlen erforderlich, am einfachsten gleich in der Geocodierung:
// geocodeAddress
// Übergabe: Texteingabe, idealerweise eine Adresse, mit der Nominatim was anfangen kann.
// Rückgabe:
// - im Erfolgsfall ein Array mit Breite und Länge als Kommazahlen
// - im Fehlerfall (nicht gefunden oder sonstiger Fehler, z.B. nominatim nicht erreichbar) undefined
async function geocodeAddress(vaddress) {
let url = `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(vaddress)}`;
var coords;
const response = await fetch(url);
// console.log(response);
if (response.ok) {
// Bis alle Daten da sind, kann es etwas dauern
data = await response.json();
// console.log(data);
if (data.length > 0) {
// Aus den Zeichenketten Kommazahlen machen
coords = [parseFloat(data[0].lat), parseFloat(data[0].lon)];
}
// andernfalls nicht gefunden
}
return(coords);
}
Die Callback-Hölle vom ChatCPT tue ich mir nicht an …
Kann sein Du musst den Browser-Cache leeren.
Dein Vorschlag hat geholfen.
Ich bekomme jetzt diese beiden Fehlermeldungen:
Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received
LatLng.js:32 Uncaught (in promise) Error: Invalid LatLng object: (NaN, NaN)
at new D (LatLng.js:32:9)
at j (LatLng.js:123:11)
at i.setView (Map.js:181:30)
at displayRouteOnMap (assistent.js:235:24)
gibt es nur Array-Elemente mit numerischem Index, aber kein Objekt mit den Eigenschaften latitude oder longitude,
deswegen greife ich über
var map = new L.map('mapid').setView(
[
// lat ist im ersten Array-Element
(startCoords[0] + endCoords[0]) / 2,
(startCoords[1] + endCoords[1]) / 2,
], 14
);
auf Längen- und Breitenangabe zu. Möchtest Du ein Object mit den Properties latitude und longitude haben, dann müsstest Du die Zuweisung zur Variablen coords wie folgt verändern:
coords = {
latitude : parseFloat(data[0].lat),
longitude : parseFloat(data[0].lon)
};
Ich habe ftp und Codepen aktualisiert.
Bekomme folgende Fehlermeldung:
LatLng.js:32 Uncaught (in promise) Error: Invalid LatLng object: (NaN, NaN)
at new D (LatLng.js:32:9)
at j (LatLng.js:123:11)
at i.setView (Map.js:181:30)
at displayRouteOnMap (assistent.js:235:24)
// Zentriere die Karte basierend auf den durchschnittlichen Koordinaten von Start und Ziel
map = L.map("mapid").setView(
[// lat ist im ersten Array-Element
(startCoordinates[0] + endCoordinates[0]) / 2,
(startCoordinates[1] + endCoordinates[1]) / 2,
],
10
);
Danke! Die Karte wird jetzt angezeigt, aber keine Strecke in der Karte.
Änderungen per ftp hochgeladen codepen ergänzt.
Hier mein ganzer JS-Code für Openstreetmap:
Damit kann man folgende Funktion implementieren (Demo, ohne echte Fehlerbehandlung):
// OSRM will Koordinaten haben: long,lat;long,lat; ...
async function getRouteData(start, ende) {
// Ermittle Route zwischen Start- und Endpunkt
// Hier verwendet: Demo-Server on OSRM
// API siehe https://project-osrm.org/docs/v5.5.1/api/#general-options
const apiUrl = `https://router.project-osrm.org/route/v1/car/${encodeURIComponent(start[1])},${encodeURIComponent(start[0])};${encodeURIComponent(ende[1])},${encodeURIComponent(ende[0])}?geometries=geojson&overview=simplified`;
// Abrufen der Routendaten
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error("Fehler beim Abrufen der Routendaten");
}
const routeData = await response.json();
return(routeData);
}
Der Aufruf in displayRouteData() könnte wie folgt aussehen:
const routeData = await getRouteData(startCoordinates, endCoordinates);
console.log(routeData);
// Das GeoJSON-Objekt mit der Route befindet sich in den route-Elementen in geometry
// Grundsätzlich kann OSRM auch alternative Routen zurückliefern, dann gibt es mehr als ein Array-Element.
const geometry = routeData.routes[0].geometry;
const route = L.geoJSON(geometry);
route.addTo(map);
Bitte beachte, dass ich noch weiter Korrekturen im Code vorgenommen habe, z.B. ist map jetzt eine globale Variable, damit man sich auch mehr als einmal eine Route anzeigen lassen kann, mit dem map.off();map.remove();, das bereits in Deiner Originalversion von ChatCPT vorhanden war.
Danke!
Das was die Demo kann: Leaflet - Route mit OSRM (Demo-Server) , dass möchte ich können.
Aber ich weiß nicht ob ich es blicke, dass in meiner App zu übernehmen.
Es ist alles so komplex …
Du bist doch schon fast durch - und den in den letzten Tagen natürlich gewachsenen (sprich nicht sonderlich strukturierten) Code kann und sollte man etwas aufräumen, was für bessere Verständlichkeit hilft.
Zusammenfassung:
Die Hauptfunktion displayRoute(),
der die Benutzereingaben von Start- und Zielort als Adressen übergeben werden, benötigt zwei Hilfsfunktionen
geocodeAddress(),
die eine Adresse von Nominatim in Geokoordinaten umwandeln lässt.
getRouteData(),
die von einem Routingdienst die Route zwischen Start- und Zielkoordinaten ermittlen lässt.
Die Hauptfunktion displayRoute() lässt sich in die Abschnitte unterteilen
Hallo,
bist ein ganz lieber! Vielen Dank für Deine Geduld und Deine tollen Erklärungen. Ich habe mir alles gespeichert.
Ich habe es per ftp hochgeladen und den Codepen aktualisiert.
Ich bekomme folgende Fehlermeldungen:
assistent.js:422 Fehler bei der Reverse Geokodierung: TypeError: callback is not a function
(anonym) @ assistent.js:422
assistent.js:423 Uncaught (in promise) TypeError: callback is not a function
at assistent.js:423:7 nominatim.openstreetmap.org/directions?format=json&geometry=geojson&origin=undefined&destination=undefined:1
Failed to load resource: the server responded with a status of 404 ()
assistent.js:234 Error: Fehler beim Abrufen der Routendaten
at getRouteData (assistent.js:228:13)
at async displayRouteOnMap (assistent.js:309:23)
getRouteData @ assistent.js:234
assistent.js:228 Uncaught (in promise) Error: Fehler beim Abrufen der Routendaten
at getRouteData (assistent.js:228:13)
at async displayRouteOnMap (assistent.js:309:23)
assistent.js:422 Fehler bei der Reverse Geokodierung: TypeError: callback is not a function
(anonym) @ assistent.js:422
assistent.js:423 Uncaught (in promise) TypeError: callback is not a function
at assistent.js:423:7