OSM Importer: Automatisierter Nachbau mit OpenStreetMap

Willkommen in der Transport Fever Community

Wir begrüßen euch in der Fan-Community zu den Spielen Transport Fever und Train Fever, den Wirtschaftssimulatoren von Urban Games. Die Community steht euch kostenlos zur Verfügung damit ihr euch über das Spiel austauschen und informieren könnt. Wir pflegen hier einen freundlichen und sachlichen Umgang untereinander und unser Team steht euch in allen Fragen gerne beiseite.

 

Die Registrierung und Nutzung ist selbstverständlich kostenlos.

 

Wir wünschen euch viel Spaß und hoffen auf rege Beteiligung.

Das Team der Transport-Fever Community


  • Hört sich nach curve fitting bzw. path smoothing Algorithmen an.

    Also quasi wenn du eine Strecke von AB nimmst die keine Weichen beinhaltet, diese dann sehr stark glättest durch einen Filter.



    Wenn ich mich recht erinnere machte das Mapnik (Renderer für OSM Daten) für das OSM Material auch. Nach ein paar Github Issues scheinen die Ergebnisse wohl leider so la la zu sein...

  • Tja, eigentlich wollte ich auch irgenwann noch einen Kurven-Vergleichmäßiger schreiben. ;-) Aber ich kann ja nicht alles gleichzeitig machen. Die Sache mit den Klothoiden habe ich übrigens beim Gleisbauer so gemacht: Ich berechne zunächst die Klothoide nach einer simplen Näherungsformel, wie sie auch bei der "echten" Bahn benutzt wird. Dann gehe ich hin und wende auf die Punkte und Tangenten einfach den UG-Algorithmus für eingepasste Kurven an, zu finden unter streetutil.addEdgeAutoTangents. Richtig, so entstehen im Spiel auch alle manuell gezogenen Gleisverbindungen! Das Ergebnis ist zwar nicht geometrisch exakt, aber auf einer kurzen Strecke völlig ausreichend. Exaktere Algorithmen dürften wohl nur in den Tresoren von irgendwelchen CAD-Softwarefirmen zu finden sein. Auch die Rechengeschwindigkeit spielt im Spiel eine Rolle. Ganz aufwändig wird die Berechnung einer Übergangskurve ("Ei-Kurve") zwischen zwei Bögen. Wenn man da erst einmal die Klothoide hat, wird man sie auch da durch die streetutil-Funktion annähern müssen, alles Andere wäre purer Wahnsinn.


    Elliptische Kurven, die wie im Spiel ihren Radius auf der gesamten Länge ändern, gibt es aber beim Vorbild normalerweise nicht. Es wird auch nicht alles mit Klothoiden gemacht, es gibt da auch z.B. Parabeln als Grundlage.

    ... don't know much trigonometry ... don't know much about algebra ... don't know what a slide rule is for ...

  • Also das Spiel geht davon aus, dass die Tangenten immer auf die Länge der Kurve normiert sind?

    So wie ich das gesehen habe, ist die Länge von kubischen Splines gar nicht mal so leicht berechenbar.

    Weiß jemand was passiert, wenn die Tangenten andere Längen haben?

  • Bei Kreisbögen ist die Länge der Tangenten so ziemlich gleich der Länge der Kurven. Bei S-Kurven und Ellipsen passt immer noch der Mittelwert der Länge der Tangenten. Eine willkürliche Veränderung der Längen ist zwar theoretisch machbar, es ist aber dringend davon abzuraten, da zum Einen keine ansehnliche Ergebnisse, sondern irgendwelche verbeulten, eirigen Splines entstehen. Außerdem werden Modder, die die Quick-and-Dirty-Messmethode (nämlich Tangentenlänge = Splinelänge) einsetzen, z.B. Lollus, dir aufs Dach steigen. ^^ Das gilt auch für Geraden, wo du theoretisch sogar die Tangentenlänge ändern kannst, ohne dass sich die Optik ändert. Da sollte eigentlich auch immer das ungeschriebene Gesetz gelten, dass die Tangente, die ja dann exakt auf der geraden liegt, dieselbe Länge wie die Gerade hat.


    Wie gesagt, die Tangentenlänge entstpricht nur ungefähr (und rein zufällig) der Länge des Splines. Bei regelmäßigen Kreisbögen ist es ziemlich exakt, weicht aber etwa ab 120 Grad ab (irgendwann wird es recht merkwürdig), so dass du wieder eine Spezialformel einsetzen musst. streetutil.addEdgeAutoTangents macht das auch. Du kannst auch grundsätzlich die etwas präzisere Formel anwende, die ich in meiner Anleitung aufgeführt habe. Auf jeden Fall ist von zu großen Kurvenwinkeln abzuraten.


    Aufgrund der Ungenauigkeit wird es interessant, wenn du eine Kurve nach De Casteljau teilst. Dann werden die beiden Tangenten nämlich exakt :D unterschiedlich lang, obwohl der Kurvenverlauf sich nicht ändert. Ich habe da schon mit Lollus einen langen Disput drüber gehabt. Das kann also passieren, UG ändert da übrigens auch nichts dran, sondern belässt die Tangenten so.


    Und die Faustregel: Exakt kriegst du mit den kubischen Splines nichts hin, außer die Unexaktheit bei der Kurventeilung. :D Aber wenn es gut aussieht, ist es die Hauptsache.


    Du kannst übrigens im Spiel nichts anderes als kubische Splines verwenden. Du musst dich also mit den Gegebenheiten anfreunden.

    ... don't know much trigonometry ... don't know much about algebra ... don't know what a slide rule is for ...

  • Nunja, intern nimmt TPF2 ja überall die Hermite Splines.


    Und die Mathematische Berechnung dafür sind nicht so wild aus im Assembler Code. (nein Nachbauen will ich es nicht)

    float Math::{anonymous}::GenHerpLength(const T&, const T&, const T&, const T&, float, float) [with T = CVec2f]

    bzw.

    float Math::{anonymous}::GenHerpLength(const T&, const T&, const T&, const T&, float, float) [with T = CVec3f]


    Da wirft dann TPF2 die beiden node pos und tangent rein, dann den Abschnitt von 0.0 bis 1.0 und bekommt die Länge raus.

    Sehr vieles wird halt als relative position float auf dem Spline gespeichert.
    Wie willst du das dann ändern?


    Wenn du da was anderes nehmen wolltest, werden alle Savegames unbrauchbar. Das Kameratool... alle API Bindings (Proposal).

    Sprich das wäre dann ein neues Spiel, das wird UG bestimmt nicht machen...


    Wobei ich hab irgendwann mal transport::EdgeGeometry als C++ Strukturen nachgebaut, da gab es diese Fälle:


    union EdgeGeometry_params {

    EdgeGeometry_Straight straight;

    EdgeGeometry_Arc arc;

    EdgeGeometry_CubicSpline cubicSpline;

    EdgeGeometry_CubicOffsetSpline cubicOffsetSpline;

    };


    Wenn du aber die Tangenten total obskure Dinge rein kippst, kann es dir passieren das du mit Brücken Probleme bekommst und das Spiel crasht. Zumindest war das zu TPF1 Zeiten so...

  • Übrigens gibt es in der UG API die Funktion Tranport.EdgeGeometry, wohinter sich offensichtlich eine Art Baukasten für Splines verbirgt. Leider konnte mir seinerzeit niemand mit genaueren Informationen weiterhelfen, weil "der Programmierer nicht erreichbar" war. Also wieder das übliche Rätselraten, auch wenn ein paar Parameter dokumentiert sind. Sollte jemand sich damit auskennen, wäre es nicht schlecht, N#heres zu erfahren.


    Zur Längenberechnung von Splines gibt es wohl verschiedene Methoden. Die einfachste, nämlich Quick-and-Dirty, habe ich bereits erwähnt. Sie ist schnell, einfach, mäßig genau und setzt kooperatives Verhalten voraus. (Die Realität kennen wir von den paramkeys :D^^:P8o=O|| ) Ob sie auch bei UG Verwendung findet, weiß ich nicht. Die präziseste ist die nach Gauss-Lengendre, aber selbst die ist ein Näherungsverfahren.


    Zum Thema Splines gibt es auch ein ausführliches Kompendium von Pomax, und mit etwas Einarbeitung versteht man irgendwann sogar mindestens ein Viertel von dem, was da erklärt wird. :D Es handelt zwar von Bézierkurven, aber die lassen sich ja leicht in Hermite-Kurven und auch wieder verlustfrei zurückkonvertieren.

    ... don't know much trigonometry ... don't know much about algebra ... don't know what a slide rule is for ...

  • Danke für euren Input!

    Das streetutil.addEdgeAutoTangents war mir neu, aber scheint sinnvoll.

    Das mit dem EdgeGeometry sieht interessant aus, wobei bei mir auch wenn ich eine Gerade baue in den Proposal Daten (entity2tn) immer EdgeGeometry.CubicSpline angezeigt bekomme. Wird also wohl nicht wirklich genutzt.


    eis_os ich will natürlich nichts im Spiel ändern. Kann ich auch gar nicht.

    Aber gerade weil manche Sachen wie EdgeObjects relativ auf die Länge bezogen werden, stellt sich die Frage wie TPF2 die Länge genau berechnet.


    Ich hatte in meiner Implementierung noch einen Denkfehler, weil ich den Kurvenparameter 0..1 bei den Splines nicht mit der Länge korrigiert hatte. Damit erhalte ich auch sinnvolle Tangenten direkt von der Spline Interpolation. Die muss ich dann auch nicht mehr in der Länge korrigieren. Die Länge der Tangenten hat ja auch einen direkten Einfluss auf die Geometrieform einer Kurve (Gerade ausgenommen).

    Mathematisch ist die Tangente ja nichts anderes als die Geschwindigkeit mit der man durch die Kurve geht. Und die sollte überall konstant sein (bezogen auf die Weglänge also 1). D.h. die Tangenten von einer Edge zur nächsten sind auch tatsächlich gleich in TPF2 - nicht nur die Richtung, sondern auch die Länge - was man an der Darstellung in TPF2 nur nicht direkt sieht, da die Tangenten dort für jede Kurve individuell mit t=0..1 gelten.


    Die Natural Splines sind jetzt implementiert und sehen gut aus.

    Die Nodes können besser zu einer Kurve verbunden werden als mit der vorigen Methode und mit höheren Kurvenradien/Geschwindigkeiten.


    Hier beispielthaft eine Kurve:


    mit Geschwindigkeit(Tangenten) und Beschleunigung (2te Ableitung)


    Der Betrag der Geschwindigkeit sieht so aus:

    die Annahme ist also ziemlich genau erfüllt.


    Hier die Krümmung (Kehrwert vom Kurvenradius):

    Von der Kurve her würde man hier einen deutlich glatteren und einfacheren Verlauf erwarten. Da die Nodes aber nicht perfekt gesetzt wurden, ergibt sich die variiernde Kurvenkrümmung. Maximale Krümmung ist 0.006 -> 167m. Das kommt real bestimmt nicht hin. Kurven in Kartendarstellungen sehen also nur auf den ersten Blick glatt aus.

  • Zitat

    Aber gerade weil manche Sachen wie EdgeObjects relativ auf die Länge bezogen werden, stellt sich die Frage wie TPF2 die Länge genau berechnet.

    Bevor UG es dir verrät, nimm einfach das hier :) :

    Für erweiterte Messungen wie die Nettolänge zwischen echten Kreuzungsknoten (geo.getNetLength) schau mal in mein geoutil. Wenn keine Kreuzungsknoten vorhanden sind, ist die Nettolänge übrigens gleich der Gesamtlänge, und du kannst dir obige Formel sogar ersparen.

    BTW: Im Gegenzug könntest du mir erklären, was Natural Splines sind. ;-)

    Zitat

    Das mit dem EdgeGeometry sieht interessant aus, wobei bei mir auch wenn ich eine Gerade baue in den Proposal Daten (entity2tn) immer EdgeGeometry.CubicSpline angezeigt bekomme

    Eine Gerade ist ja auch ein Cubic Spline, also wahre Aussage. Wobei andere Figuren auch Cubic Splines sind, aber eben schon wieder Sonderfälle. Was kommt denn raus, wenn du einen Kreisbogen baust?

    ... don't know much trigonometry ... don't know much about algebra ... don't know what a slide rule is for ...

    3 Mal editiert, zuletzt von WernerK ()

  • Ganz grob. transport.EdgeGeometry ist für die Lanes. Der transport Namespace wird in der Regel für alles was später mal auch für (Transport) Wege genutzt. Dafür ist dann ggf. das TransportNetworkSystem zuständig.


    Irgendwo werden die mdl Lanes in ConstructionBuilder dann auch umgewandelt, da bin ich aber nur einmal kurz drüber gestolpert.


    (Daher ist es auch nicht möglich eine Mdl, eine Straßen Bushaltestelle mit eigenen transportNetworkProvider auszustatten)


    EdgeGeometry kommt dann beim Rendern der Debug Lanes als Eingangsdaten an.


    Alles was beim Straßenrendern reinkommt sind Faces und Hermit Splines.

    Die echten Daten für das "Aussehen" einer Kreuzung sind in der nicht öffentlichen zugänglichen procedural::AttributeMap im Shape.


    CommonAPI2 kann das euch Anzeigen,

    dort liegen dann für jede Kreuzungspunkt Sachen wie, die Lane ConnectionMap, ob da ne Busspur ist, ob es Trams gibt, ob die Straße gedreht wurde. (Für Einbahnstraßen interessant) und auch Daten wo die Straße und der Kreuzungsbereich anfängt. (Teilweise hab die Berechnungen wo ein Verbindung ist aus den Datan im LUA Code für den NodeEditor nachgebaut)


    Gleise verhalten sich anders, da es ja keine Kreuzungen gibt, aber dort liegt dann auch der momentane Status wie ob ein Gleis elektrifiziert wurde, Tunnel (ja/nein) bzw. Brückentyp.



    -edit-

    Ich hab jetzt eigentlich keine Lust das alles von Assembler wieder in C++ zu überführen ...


    Vielleicht ist ja jemand schon mal auf einen Algorithmus mit math::detail::richardson Tabelle am Ende gestoßen... Vielleicht kann aber Dino ja direkt was zu sagen...

  • Ich habe nun an einer Funktion gearbeitet, die unpassend hohe Krümmungen erkennt und dann entsprechende Nodes entfernt.

    Es gibt ja eh unnötig viele Nodes, weil OSM keine echten Kurven darstellt. Daher besteht hier auch Optimierungspotential, um die Anzahl Edges im Spiel möglichst gering zu halten, da dies ja anscheinend hohen Performance Impact hat.

    Daher habe ich eine weitere Funktion geschrieben, welche prüft ob eine Node entfernt werden kann, ohne dass die neue Kurve dabei einen zu hohen Fehler erzeugt.

    Damit kann man insgesamt etwa 40% Edges einsparen!

    Und die Kurven werden dadurch auch nochmal deutlich glatter.

    Und man spart sich einen Teil der Nachbearbeitung.


     


     


    OSM Importer: Curves

  • --- Version 1.2 ---


    Ich habe die Neuerungen in einer neuen Version zusammengefasst: https://github.com/Vacuum-Tube/OSM-TPF2-Importer/releases


    Außerdem habe ich auch aus dem Python Code eine exe Anwendung für Windows Nutzer gemacht. Das ist praktisch für User die kein Python installieren wollen und vereinfacht hoffentlich die Anwendung etwas.

  • hab auch mal einen Versuch mit dem Tool gewagt.

    dank der exe geht die Erzeugung der osm-importdaten doch recht einfach, problematisch wird es jetzt aber beim import:

    die UG console schluckt die Eingabe von require "osm_importer.main" noch, der script thread dann aber nicht mehr:

    bei 3 Eingaben hintereinander kommt jedes mal eine andere Fehlermeldung.

    hab ich irgendwas übersehen?

  • In der Tat, man sollte auch das richtige save öffnen.


    Wäre aber auch zu schön wenn es jetzt großartig weitergehen würde:
    beim Erstellen der town-labels stürzt das Spiel nun ab. Auszug aus der stdout:

    Code
    Load osm_importer.main
    Thread did not respond to ping. Possible hang detected!
    true
    Load osm_importer.main
    Thread did not respond to ping. Possible hang detected!
    Create Town Labels	146
    urban_games/train_fever/src/Lib/ecs/ComponentManager.h:224: int __cdecl ecs::ComponentManager::GetComponentTypeIndex(const class std::type_index &) const: Assertion `it != m_compType2index.end()' failed.
BlueBrixx