Auf Abwegen - Mobile Karten ohne große Anbieter

Michel

auf-abwegen-mapbox-abloesen

In vielen unserer Projekte kommen individuell gestaltete, digitale Karten zum Einsatz - so auch in den beiden mobilen Anwendungen Nebolus und der App des Erlebnis-Zoo Hannover. Aus vergangenen Projekten war uns Mapbox bereits als Anbieter für vielfältig anpassbare Karten bekannt, sodass wir auch für diese beiden Projekte auf Mapbox zurückgriffen. Bereits in der Umsetzung von Nebolus wurde uns allerdings klar, dass nicht alles so einfach sein sollte, wie es uns aus dem Web bekannt war. Insbesondere eigens gestaltete Marker waren uns dabei immer wieder ein Dorn im Auge. Für den Erlebnis-Zoo Hannover haben wir nun eine Lösung geschaffen, die flexibel, performant und von Drittanbietern größtenteils unabhängig ist. Der Weg dahin war nicht leicht, aber lest selbst!

Marker

Sowohl Nebolus als auch die Erlebnis-Zoo-Hannover-App sind in Flutter geschrieben. Mit Widgets individuell auf den Kunden zugeschnittene Marker zu erzeugen, ist ein Leichtes! Da das Rendering der Karte selbst allerdings ressourcenintensiv ist, wird dieser Teil der Darstellung auf einen nativen Thread ausgelagert. Versucht man nun also ein Flutter-Widget als Marker auf der Karte zu platzieren und mittels Callbacks nach Kartenbewegung die Position dynamisch anzupassen, entsteht ein spürbares Nachschleifen des Markers. Um in Flutter eigene Marker an einer bestimmten Koordinate zu fixieren, muss diese asynchrone Brücke umgangen werden. Sprich: Das gesamte Rendering der Marker und der Karte muss auf einer Seite passieren - entweder nativ oder in Flutter.

Für Nebolus haben wir den ersten Weg (nativ) eingeschlagen und alle statischen Assets nach Erstellung der Karte über das Mapbox SDK geladen, sodass wir die Grafiken anschließend nutzen konnten. Beim Erlebnis-Zoo Hannover waren es allerdings nicht ein knappes Dutzend, sondern mehr als 200 Assets. Jedes Tier und jedes Objekt sollte einen eigenen Marker auf der Karte erhalten, um direkt sehen zu können, wo sie sich befinden. Es ist zwar möglich, Flutter-Widgets zur Runtime als Bilder zu rendern und entsprechend mit Mapbox einzubinden, allerdings hätte das Laden der Bilder größere Ladezeiten der Karte verursacht, die wir für ein besseres Nutzererlebnis vermeiden wollten. Das Nutzen des Mapbox SDKs war für uns hier also keine Option!

Raster- vs. Vector-Tiles

Bevor wir tiefer einsteigen, ergibt es Sinn, den Unterschied zwischen Raster-Tiles und Vector-Tiles zu erklären. Raster-Tiles sind kleine, meist quadratische PNG-Dateien, die einen Kartenausschnitt bereits eingefärbt darstellen. Einfach gesagt, ergibt sich aus vielen aneinander gelegten Raster-Tiles ein Kartenbild. Raster-Tiles werden üblicherweise auf einem Server gerendert. In den meisten Fällen ist dies der Server eines Drittanbieters wie Mapbox. Vector-Tiles hingegen enthalten lediglich die Informationen über den Kartenausschnitt, den sie beschreiben, beispielsweise "Ein Strich von dieser Ecke zu dem Punkt beschreibt eine Straße mit dem Namen Theaterstraße". Diese Vector-Tiles sind sehr auf ihre Größe optimiert. Allerdings müssen sie für die Darstellung mithilfe eines Karten-Themes zu tatsächlichen Strichen und Formen gerendert werden. Ein Theme beschreibt die Darstellung der Informationen basierend auf bestimmten Datenpunkten (zum Beispiel "Eine Straße wird als weiße Linie mit einer Breite von 3px gezeichnet").

Zurück zur Zoo-Karte

Das Package flutter_map stellt eine API für die Darstellung verschiedener Kartenlayer in einer Flutter-App zur Verfügung. Mit einigen der bereits zur Verfügung stehenden Layer lassen sich so sehr schnell Raster-Tiles als Karte einbinden. Für den Zoo wollten wir allerdings eine Lösung finden, die uns von Mapbox unabhängig macht, uns aber dennoch volle Anpassbarkeit bietet und die Karte offline zur Verfügung stellt.

Für eine illustrierte Karte, auf der im Sambesi, einer der Themenwelten im Zoo, beispielsweise kleine Boote fahren, sind die Daten normaler Datensets wie OpenStreetMap (OSM) nicht ausgelegt. In Zusammenarbeit mit Martina Nowak entstand eine illustrierte Karte im richtigen Maßstab im SVG-Format. Da sich auch SVG-Dateien leicht als Flutter-Widget einbinden lassen, war das unser erster naiver Versuch, unser Vorhaben umzusetzen. Auf höheren Zoomstufen verbrauchte das Rendern der Grafik allerdings so viel Speicher, dass die App schlicht abstürzte.

Vektorgrafik, die den Zoo Hannover illustriert darstellt

Unser nächster Ansatz versprach mehr Erfolg, war aber auch bedeutend steiniger. Mithilfe verschiedener Tools wollten wir das SVG über Umwege zu Vector-Tiles konvertieren, um es so in der App mit einem Theme rendern zu können. Also:

  1. SVG zu GeoJSON konvertieren
  2. GeoJSON zu Vector-Tiles konvertieren
  3. Ein Theme schreiben, das Metadaten im GeoJSON nutzt, um die grafische Darstellung des SVGs wieder herzustellen
  4. Bibliotheken für flutter_map finden, die die Darstellung unterstützen

Für den ersten Schritt fanden wir svg-plotter von Liam Clarke. Zunächst sorgte ein Fehler in dem Tool dafür, dass Aussparungen bzw. Löcher in Flächen nicht korrekt generiert wurden. Das Problem konnten wir finden und haben den Fix selbstverständlich an Liam weitergegeben, um das Tool auch für andere Nutzer zu verbessern! Open-Source am Werk ;)

SVG geladen in svg-plotter über eine Mapbox Karte des Zoo Hannover gelegt

svg-plotter bietet ein sehr praktisches Callback, um für jeden Path eines SVGs, der zu einem GeoJSON Feature umgewandelt wird, entsprechende Properties zu generieren. Um später die Farben und andere Informationen für die Darstellung zur Verfügung zu haben, wurde das style Attribut des Pfades zu den Properties konfiguriert (zum Beispiel fill: #ffffff).

Um das GeoJSON anschließend zu Vector-Tiles zu konvertieren, haben wir das Tool tippecanoe mit folgenden Settings genutzt:

tippecanoe -b 10 -pi -pw -l zoo -Z15 -z22

  • -b 10 erzeugt eine Bufferzone um die Tiles herum, um Artefakte zwischen Tiles zu vermeiden
  • -pi stellt sicher, dass die Reihenfolge der Features nicht geändert wird, da sie sich zum Teil überlappen (andernfalls sortiert tippecanoe nach Geolocation)
  • -pw sorgt dafür, dass bei Pfaden, die Reihenfolge der Punkte der Pfade für Aussparungen verantwortlich sind und nicht die Reihenfolge der Layer
  • -l zoo bezeichnet das resultierende Layer mit einem festen Namen, den wir später im Theme nutzen können
  • -Z15 -z22 erzeugt die Tiles in Zoomlevels 15-22

Der Output von tippecanoe ist eine MBTiles Datei (eine SQLite Datenbank mit spezifizierter Struktur) mit wenigen MB Größe, die alle Daten für unsere illustrierte Karte enthält. Mit den bereits in den Daten enthaltenen Style-Informationen lässt sich nun ein Theme mit nur zwei Layern schreiben, das in einem Test mit Maputnik bereits den gewünschten Effekt erzielt. Jetzt muss die Karte also nur noch in Flutter gerendert werden!

Das Package vector_map_tiles ist ein Plugin für flutter_map, das Vector-Tiles rendern kann! Wir haben das Plugin bereits vor einigen Jahren entdeckt und schon Zeit investiert, noch nicht unterstützte Style Expressions zu unterstützen. Auch wenn die Arbeit von damals am Ende nicht in der Bibliothek gelandet ist, haben wir viel daraus gelernt! Mittlerweile unterstützt die Bibliothek alle Expressions und das Laden von Tiles aus verschiedenen Quellen. Die Library vector_mbtiles lädt zum Beispiel Vector-Tiles aus den vorhin erwähnten MBTiles-Dateien.

Um nun auch die Umgebung des Zoos nicht zu verlieren, haben wir uns vorgenommen, mit dem Wissen über das Erstellen von MBTiles einen Ausschnitt der OSM-Karte als Hintergrund zu platzieren. Der Prozess, einen entsprechenden Ausschnitt zu erzeugen und für verfügbare Styles aufzubereiten war allerdings so umfangreich, dass er vermutlich einen eigenen Blogbeitrag wert ist. Zusammengefasst können wir jetzt aber mit einem Skript wochenaktuelle Daten direkt als MBTiles erzeugen lassen und in die App einbetten (allerdings gehen wir nicht von regelmäßigen Änderungen aus).

Das Ergebnis kann sich sehen lassen und ist unglaublich performant! Zum Abschluss gibt es noch einen Vergleich zwischen der alten und der neuen Karte:

Die alte Karte der Zoo Hannover App

Die neue Karte der Zoo Hannover App

Oder einfach selbst ausprobieren: Im App Store und bei Google Play

Hast du Lust auf App Entwicklung bekommen?

Dann freuen wir uns darauf, Dich bald kennenzulernen!

Themen:

  • Softwareentwicklung
  • Programmiersprachen
  • Von Entwickler zu Entwickler
  • Technik