Mathe für Geoinformatiker: Koordinate auf Linie verschieben

Gegeben: Koordinate-a und Koordinate-b
dazwischen ergibt sich eine Gerade a-b (<1000km), die ich später verändet als Gerade a-b2 auf Leaflet anzeigen will.

Gesucht: eine Koordinate-b2, die die Linie am b-Ende so “kürzer” macht, dass die Richtung der Linie beibehalten wird. Als Mass der Verkürzung soll “x Kilometer” verwendet werden.
Anders ausgedrückt: Die Koordinate-b2 wird, ausgehend von der Koordinate-b, auf der imaginären Linie a-b um “x Kilometer” in Richtung Koordinate-a verschoben und der dortige Wert ausgegeben.

Hintergrund: Auf einer OSM-Karte (WGS84) werden via Leaflet Teilnehmer einer Veranstaltung gezeigt, von denen aus sternförmig Linien zum Veranstaltungsort führen. Damit die Karte am Veranstaltungsort sichtbar bleibt, müssen die Linien entsprechend vorher enden.

HowTo: Zur Verfügung steht eine Excel-Tabelle mit den Spalten: Koordinate-a, Koordinate-b, Koordinate-b2 und Kilometer (für die Verkürzung). Das Ganze soll webbasiert serverseitig gemacht werden (JavaScript, PHP, …): Die Teilnehmer tragen in ein Webformular Daten ein (Name, PLZ, Ort, ihre Wünsche bezüglich “gemeinsam Fahren”, Mailadresse). Diese werden (derzeit per Excel-VBA) weiterverarbeitet. Ergebnis wird als CSV exportiert und via Leaflet dargestellt.
Derzeit wird die Koordinate händisch via “PLZ Ort” bestimmt (kann man nur für wenige TN machen) und nur ungefähr angegeben wegen Datenschutz.
Ideal wäre, wenn der Teilnehmer beim Eingabeformular eine Karte hätte, auf der er mit verschiebbarem Cursor seinen Ort selber markieren könnte…

Da ich nicht so fit bin in Mathe wäre es hilfreich, die Berechnung als Zellen-Formel und/oder als VBA-Formel zu bekommen :slightly_smiling_face:

Ich kann Dir keine Lösung nennen, aber das mathematische Problem anders (auch nicht exakt mathematisch) beschreiben:
Gesucht sind alle Schnittpunkte B(n) von Geraden (eigentlich Strecken) von den Punkten A(n) nach B mit einem Kreis um den Punkt B mit dem Radius r=B-B2. Danach konstruiere n Strecken mit allen Punkten A(n);B(n).
n sind die Teilnehmer.

Vielleicht hilft der Denkansatz ein wenig.

Das Problem (da bin ich mir jetzt nicht ganz sicher) ist, dass sich die gegebenen Koordinaten nicht in einem streng mathematisch rechtwinkligen Koordinatensystem befinden, sondern auf einer Kugeloberfläche, genaugenommen einer Kartoffel. Aber dafür gibt es bestimmt auch eine Lösung.

Claude.ai ist manchmal ziemlich begabt bei solchen Dingen, ich kann das Ergebnis allerdings nicht einschätzen. Es könnte auch Bullshit sein, man weiß nie so genau:

Ich verstehe Ihre Anforderung. Sie möchten eine Formel oder VBA-Code, um eine neue Koordinate (b2) zu berechnen, die auf der Linie zwischen zwei gegebenen Koordinaten (a und b) liegt, aber um eine bestimmte Distanz von b in Richtung a verschoben ist. Hier sind Lösungen sowohl als Excel-Formel als auch als VBA-Funktion:

Option Explicit

' Funktion zur Berechnung der neuen Koordinate b2
Function BerechneKoordinateB2(lat1 As Double, lon1 As Double, lat2 As Double, lon2 As Double, distanzKm As Double) As String
    Const PI As Double = 3.14159265358979
    Const ERDE_RADIUS As Double = 6371 ' Erdradius in Kilometern
    
    ' Umrechnung in Radianten
    lat1 = lat1 * PI / 180
    lon1 = lon1 * PI / 180
    lat2 = lat2 * PI / 180
    lon2 = lon2 * PI / 180
    
    ' Berechnung der Gesamtdistanz
    Dim dLon As Double
    dLon = lon2 - lon1
    Dim gesamtDistanz As Double
    gesamtDistanz = 2 * ERDE_RADIUS * ArcSin(Sqr((Sin((lat2 - lat1) / 2)) ^ 2 + Cos(lat1) * Cos(lat2) * (Sin(dLon / 2)) ^ 2))
    
    ' Verhältnis der neuen Distanz zur Gesamtdistanz
    Dim fraction As Double
    fraction = 1 - (distanzKm / gesamtDistanz)
    
    ' Berechnung der neuen Koordinate
    Dim a As Double, b As Double, x As Double, y As Double, z As Double
    Dim c As Double, d As Double
    
    a = Sin((1 - fraction) * gesamtDistanz / ERDE_RADIUS) * Cos(lat2)
    b = Cos((1 - fraction) * gesamtDistanz / ERDE_RADIUS) - Sin(lat1) * Sin(lat2)
    
    lat2 = ArcSin(Sin(lat1) * Cos((1 - fraction) * gesamtDistanz / ERDE_RADIUS) + Cos(lat1) * a)
    lon2 = lon1 + ArcTan2(a * Sin(dLon), b - Sin(lat1) * Sin(lat2))
    
    ' Umrechnung zurück in Grad
    lat2 = lat2 * 180 / PI
    lon2 = lon2 * 180 / PI
    
    ' Rückgabe als String im Format "Latitude, Longitude"
    BerechneKoordinateB2 = Format(lat2, "0.000000") & ", " & Format(lon2, "0.000000")
End Function

' Hilfsfunktion für ArcSin
Function ArcSin(x As Double) As Double
    If Abs(x) = 1 Then
        ArcSin = Sgn(x) * PI / 2
    Else
        ArcSin = Atn(x / Sqr(1 - x * x))
    End If
End Function

' Hilfsfunktion für ArcTan2
Function ArcTan2(y As Double, x As Double) As Double
    If x > 0 Then
        ArcTan2 = Atn(y / x)
    ElseIf x < 0 Then
        If y >= 0 Then
            ArcTan2 = Atn(y / x) + PI
        Else
            ArcTan2 = Atn(y / x) - PI
        End If
    ElseIf y > 0 Then
        ArcTan2 = PI / 2
    ElseIf y < 0 Then
        ArcTan2 = -PI / 2
    Else
        ArcTan2 = 0 ' Undefiniert, könnte auch einen Fehlerwert zurückgeben
    End If
End Function

Um diese VBA-Funktion in Excel zu verwenden, fügen Sie sie in ein VBA-Modul ein. Dann können Sie sie in einer Zelle wie folgt aufrufen:

=BerechneKoordinateB2(A2, B2, C2, D2, E2)

Dabei wird angenommen, dass:

  • A2 und B2 die Breiten- und Längengrade von Koordinate-a enthalten
  • C2 und D2 die Breiten- und Längengrade von Koordinate-b enthalten
  • E2 die Distanz in Kilometern enthält, um die die Linie verkürzt werden soll

Alternativ können Sie auch eine Excel-Formel verwenden, die jedoch komplexer und möglicherweise weniger genau ist. Hier ist eine Annäherung:

=LET(
    lat1, RADIANS(A2),
    lon1, RADIANS(B2),
    lat2, RADIANS(C2),
    lon2, RADIANS(D2),
    d, E2/6371,
    f, 1-d/ACOS(SIN(lat1)*SIN(lat2)+COS(lat1)*COS(lat2)*COS(lon2-lon1)),
    A, SIN((1-f)*d)*COS(lat2),
    B, COS((1-f)*d)-SIN(lat1)*SIN(lat2),
    lat3, ASIN(SIN(lat1)*COS((1-f)*d)+COS(lat1)*A),
    lon3, lon1+ATAN2(A*SIN(lon2-lon1), B-SIN(lat1)*SIN(lat3)),
    CONCATENATE(TEXT(DEGREES(lat3),"0.000000"), ", ", TEXT(DEGREES(lon3),"0.000000"))
)

Diese Formel verwendet die LET-Funktion, die in neueren Excel-Versionen verfügbar ist. Sie führt die gleiche Berechnung wie die VBA-Funktion durch, ist aber möglicherweise etwas weniger genau aufgrund der Einschränkungen von Excel-Formeln.

Beide Methoden verwenden die Haversine-Formel zur Berechnung von Distanzen auf einer Kugel und berücksichtigen die Krümmung der Erde.

Ineressant - zumindest fühle ich mich von “Claude” bei der Aufgabenbeschreibung genau verstanden. Den Rest müssen Fachkundige bewerten…

Auf der kartoffeligen Kugel kann man bis wenige 100km Loxodrome nutzen. Ab 4-stelligem km-Bereich verwendet man Orthodrome. Dazwischen verwendete man in der Nautik früher eine Näherung. Aber mit derzeitigen Möglichkeiten bieten sich vermutlich Orthodrome an (wobei es beim Beispiel vermutlich unerheblich ist)…

Bei der Eingangsfrage steht aber nur

Sind hier karthesische Koordinaten gemeint?
Sind hier geographische Koordinaten gemeint?
Wenn karthesisch: welches System? Bessel 6-Streifen, Bessel-3-Streifen, UTM, Pseuro-Mercator…?
Wenn geographisch: welches System? Gauß-Krüger, Krassowski, GRS80, WGS84…?

Sven

Vielleicht verstehe ich die Aufgabenstellung nicht richtig, aber für mich klingt das nach einer klassischen Aufgabe für ein GIS (z.B. QGIS). Das nimmt einem ja die Mathematik ab (die mit geographischen Koordinaten ohnehin nur wenigen Sadisten Spaß macht :-p).

Ein Workflow könnte z.B. sein: Koordinaten als CSV speichern > CSV mit QGIS in Punkte umwandeln > gewünschte Linien zwischen den Punkten erzeugen > Linien um gewünschten Betrag kürzen – wofür es verschiedene Möglichkeiten gäbe wie z.B. von einem Puffer am Zielpunkt ausgehen, einen Punkt mit bestimmtem Abstand auf der Linie zu interpolieren o.ä.

grafik

P. S. Nach dem zweiten Lesen denke ich, dass es am einfachsten wäre, zunächst die sternförmigen Linien zwischen den Punkten und dem Veranstaltungsort zu ziehen und dann einfach einen Puffer von X km um den Veranstaltungsort zu ziehen, da der ja vermutlich für alle Linien gleich groß ist. Das Ergebnis ist dann die Differenz der Linien mit dem Puffer.

P. P. S. Hier nochmal beispielhaft mit Knotenlinien zwischen “Teilnehmenden” und einem “Veranstaltungsort”, dann ein Puffer von 50 km um den Veranstaltungsort, der die Linien abschneidet:

Vermutlich helfen dir einige Geocaching-Tools weiter:

Bestimme den Winkel zwischen den Punkten Veranstaltungsort und Wohnort.
Führe mit dem Winkel, bezogen auf den Veranstaltungsort, eine Peilung von x Metern durch.
Du erhältst den Startpunkt der Linie ‘Veranstaltungsort + x Meter’ zu Wohnort.

Zum Beispiel hier kannst du alles ausprobieren:

http://gps-cache.de/geocaching/koordinaten-peilung.htm

Die Linien sollen mit Leaflet auf einer OpenStreetMap-Karte dargestellt werden.
Koordinaten sind in WGS84.
Sorry, dass ich das eingangs nicht erwähnt hatte (jetzt nachgeholt).

Danke für die schöne Visualisierung :slightly_smiling_face:
So habe ich mir das vorgestellt…
Das mit dem “Puffer” könnte bei ~20 TN funktionieren. Aber bei 200 oder 1000 TN müsste dieser sehr gross sein - und TN mit Standort innerhalb des Puffers wären dann nicht mehr sichtbar. Deshalb die Idee mit “Kilometer abschneiden” - dann kann man die Darstellung wenigstens händisch manipulieren (idealerweise natürlich algorithmisch - aber das wäre eine neue Aufgabe…)

QGIS ist cool und mächtig :slightly_smiling_face:
Aber ich habe 0 Erfahrung damit, und auch keine Ahnung, ob und wie man dieses (oder ein anderes Tool) in einen webbasierten Workflow einbinden kann…

Schönes und vielseitiges Tool für Geeocaching :slightly_smiling_face:
Rechnet das geometrisch oder orthodrom oder automatisch je nach Distanz?
(Geocacher, Seeleute und Piloten haben da ja unterschiedliche Anforderungen)
Wie kann man das in einen Prozess einbinden?
(Idee für den Entwickler: zur Visualisierung OSM verwenden)

Wenn der Startpunkt point1 um tdist (km) in Richtung Endpunkt point2 verschoben werden soll, so kann man den neuen Punkt point1a näherungsweisenach mit folgenden Formeln berechnen (python):

        dlat = point2['lat'] - point1['lat']
        dlon = point2['lon'] - point1['lon']
        dist = math.sqrt(dlat**2 + (dlon*latfak)**2)*111   # Entfernung zw. point1 und 2

        point1a['lat'] = point1['lat'] + tdist / dist * dlat  # verschieben anteilig zur Entfernung
        point1a['lon'] = point1['lon'] + tdist / dist * dlon

Dabei wird latfak = math.cos(point1[‘lat’]/180*math.pi) gesetzt.

Ich würde zur Sicherheit eine zusätzliche Klammer setzen, dann ist man nicht davon abhängig, dass von links nach rechts abgearbeitet wird
latfak = math.cos((point1[‘lat’]/180)*math.pi)
oder
latfak = math.cos(point1[‘lat’]*math.pi/180)
dann spielt die Reihenfolge keine Rolle.
Beides setzt natürlich voraus, dass math.cos im Bogenmaß arbeitet.

Deshalb die ganze Übung.