Dokumentation / Konzepte / Architektur

Architekturübersicht

MapConductor fungiert als Brücke zwischen der App und verschiedenen Karten-SDKs. Die App ruft nur die einheitliche Karten-API auf, und MapConductor leitet die Verarbeitung an den ausgewählten Anbieter weiter. Unter Android (Kotlin + Compose), iOS (Swift + SwiftUI) und React (Web) sind sowohl die Aufteilung der Pakete als auch die interne Aufgabenteilung gleich gestaltet.

ANDROID
android-sdk-core
iOS
ios-sdk-core
REACT
js-sdk-core / js-sdk-react

01 · Gesamtbild

Der Ablauf ist auf jeder Plattform gleich: App → Einheitliche Karten-API → Core → Verschiedene Karten-SDK-Treiber → Verschiedene Karten-SDKs. Nur die oberen beiden Ebenen sind vom Code, den die App schreibt; die unteren beiden Ebenen können ausgetauscht werden.

APP

UI und Geschäftslogik

React · Compose · SwiftUI
UNIFIED API

Einheitliche Karten-API

MapViewState · Marker · Camera
CORE

Kernfunktionen (Karte, Marker, Formen, Ereignisse)

Manager · Controller · Overlay
DRIVER

Anbieter-Treiber

*-for-googlemaps / -maplibre / …
MAP SDK

Karten-SDKs (nativ / JavaScript)

Google Maps · MapLibre · Mapbox · MapKit · ArcGIS · HERE …

Wenn der Anbieter gewechselt wird, werden nur die untersten beiden Ebenen ausgetauscht. Da der Code der Benutzeroberfläche gegen die einheitliche API geschrieben ist, funktioniert er unverändert weiter.

Plattform

02 · Plattformspezifische Konfiguration

Core und Treiber sind als Pakete getrennt. Die App fügt nur Core und den Treiber des verwendeten Anbieters als Abhängigkeiten hinzu.

android-sdk-core enthält das gemeinsame Modell und die Differenzanwendung, während android-sdk-compose Statushalter für Compose bereitstellt. GeoJSON, Clustering und Heatmaps sind als zusätzliche Module (android-geo-layer / android-marker-clustering / android-heatmap) eigenständig.

03 · Ebenenzusammensetzung

Damit sie plattformübergreifend wiederverwendet werden können, sind die Verantwortlichkeiten in 6 Ebenen unterteilt. Die oberen Ebenen kennen die Implementierungen der unteren Ebenen nicht.

#
Ebene
Rolle
1
UI-Framework
Deklarative UI, die von Anwendungen verwendet wird, wie React / Vue / Jetpack Compose / SwiftUI usw.
2
Vereinheitlichte Karten-API
Die API von MapConductor, die Marker, Formen, Kamera und Ereignisse mit gemeinsamen Konzepten behandelt. Der Code, den die Anwendung tatsächlich schreibt, wird für diese API geschrieben.
3
Brücke
Verbindet die Operationen der einheitlichen API abhängig von der Ausführungsumgebung nach unten. Sie ist nicht erforderlich, wenn UI und Rendering in derselben Ausführungsumgebung abgeschlossen sind; sie greift nur ein, wenn beide in getrennten Ausführungsumgebungen liegen.
4
Cross-Platform-Ebene
Die Einbindungsmethode und Deklarationsmethode des nativen Karten-SDK variieren je nach Plattform nur in dieser Ebene.
5
Nativer SDK / Treiber
Die Ebene, die tatsächlich für das Rendering von Kamera, Stil und Overlays zuständig ist (Native für Android / iOS oder der JS-Treiber im Web).
6
Karten-SDK
Die tatsächlichen Entitäten wie Google Maps / MapKit / MapLibre / Mapbox / ArcGIS / HERE usw.

Durch diese Trennung kann Code, der sich darauf konzentriert, „was mit der Karte gemacht werden soll“ statt „welche Plattform“, direkt in mehreren Umgebungen bereitgestellt werden. Plattformspezifische Unterschiede werden grundsätzlich nur auf die Installations- / Deklarationsmethoden der Cross-Platform-Ebene beschränkt.

Im Fall von React Native

Web und React Native nutzen denselben Code, den die App schreibt. Auch die Zustandsobjekte und Komponenten sind identisch; Sie müssen nur das ausgewählte MapView austauschen. Der Unterschied liegt darunter. Im Web ruft der Treiber eine JavaScript-Kartenbibliothek in derselben Laufzeitumgebung auf, während React Native über eine Bridge zum nativen SDK von MapConductor gelangt und am Ende die nativen Karten-SDKs rendern.

Wo derselbe Code sich aufspaltet
SHARED

Was die App schreibt

js-sdk-core · js-sdk-react
WEB

React

DRIVER
JavaScript-Treiber
react-for-*
Keine Bridge — eine Laufzeitumgebung
MAP SDK
JavaScript-Karten-SDK
Google Maps · MapLibre · Leaflet · Cesium …
REACT NATIVE

React Native

DRIVER
Treiber, der an nativ übergibt
reactnative-for-*
BRIDGE
Natives RN-Modul
Schicht 3
NATIVE SDK
Natives MapConductor-SDK → Karten-SDK
Google Maps · MapLibre · ArcGIS · HERE

Die Ebene 3 in der obigen Tabelle ist die Bridge. Im Web ist sie nicht vorhanden, da UI und Rendering in derselben Laufzeitumgebung abgeschlossen werden. In React Native ist sie erforderlich, da beide in unterschiedliche Laufzeitumgebungen aufgeteilt sind. Nur diese Ebene unterscheidet sich je nach Plattform; darüber bleibt alles unverändert.

Die mit React Native nutzbaren Provider sind Google Maps, MapLibre, ArcGIS und HERE – insgesamt vier. Da nur Anbieter mit einem entsprechenden nativen Modul möglich sind, ist die Anzahl geringer als die 13 Provider im Web.

04 · Interne Struktur von Core

Core ist für Elemente wie Marker, Polylines, Polygone, Kreise, Bodenbilder und Rasterebenen so aufgebaut, dass dieselben sieben Rollen wiederholt werden. Wenn man eine versteht, lassen sich die anderen Elemente auf dieselbe Weise nachvollziehen.

Rolle
Verantwortung
Ort der Implementierung
State
Unveränderliche Werte, die die App übergibt. Koordinaten, Farbe, zIndex usw. Gibt mit fingerPrint() einen Hash des Inhalts zurück.
core
Entity
Ein Eintrag, der State, das vom Provider tatsächlich erstellte Objekt und den zugehörigen fingerPrint bündelt.
core
Manager
Register, das Entity per id hält. Auch die Treffertests von Koordinaten (find) werden hier übernommen.
core
Controller
Schnittstelle für Operationen mit add / update / clear / find / onCameraChanged / destroy. Führt eine Differenzprüfung durch.
core
Overlay
Zeicheneinheit, die Elemente derselben Art zusammenfasst. Legt die Überlagerungsreihenfolge durch zIndex fest.
core
OverlayRenderer
Wandelt onAdd / onChange / onRemove / onPostProcess in Aufrufe der jeweiligen Karten-SDKs um. Es müssen nur die drei Methoden ausgefüllt werden, indem die für jedes Element vorbereiteten abstrakten Klassen (z. B. AbstractPolygonOverlayRenderer) vererbt werden.
driver
Capable
Deklariert als Typ, welche Elemente der Anbieter verarbeiten kann (compositionPolygons / updatePolygon / hasPolygon). Für jedes Element ist eines vorhanden und wird in Kotlin beispielsweise PolygonCapableInterface und in TypeScript PolygonCapable benannt. In Swift wird dies nicht durch ein Protokoll ausgedrückt, sondern dadurch, ob der Anbieter über einen PolygonController verfügt.
driver

Wichtig ist die Position der Grenze. Die Core-Seite (State / Entity / Manager / Controller / Overlay) ist in fast gleichem Code in den 3 Sprachen geschrieben, und für jeden Anbieter müssen nur der Renderer und die Deklaration, dass dieser die Elemente verarbeiten kann, geschrieben werden.

ANDROID · Kotlin
iOS · Swift
REACT · TypeScript
core/polygon/PolygonManager.kt
PolygonCapableInterface.kt
AbstractPolygonOverlayRenderer.kt
polygon/PolygonManager.swift
polygon/PolygonOverlayRenderer.swift
polygon/PolygonHoleSplit.swift
polygon/PolygonManager.ts
polygon/PolygonCapable.ts
AbstractPolygonOverlayRenderer.ts

05 · Ablauf der Differenzanwendung

Die App übergibt nur ein „Array des Zustands, der jetzt bestehen sollte“. Core berechnet die Differenz zum vorherigen Mal, teilt sie in Zunahmen, Änderungen und Entfernungen und übergibt sie an den Treiber. Da die Kartenobjekte nicht neu erstellt werden, ist die Darstellung auch bei einer großen Menge von Elementen stabil.

controller/OverlayRendererInterface.ts · Gemeinsamer Vertrag für 3 Plattformen
export interface OverlayRendererInterface<ActualType, StateType, EntityType> {
    onAdd(data: StateType[]): Promise<Array<ActualType | null>> | Array<ActualType | null>;
    onChange(data: Array<ChangeParamsInterface<EntityType>>): Promise<Array<ActualType | null>> | Array<ActualType | null>;
    onRemove(data: EntityType[]): Promise<void> | void;
    onPostProcess(): Promise<void> | void;
}

onChange empfängt sowohl die vorherige Entity (prev) als auch den aktuellen State (current). Der Treiber kann nur die geänderten Eigenschaften aktualisieren, und die Signatur ist auch in Kotlin / Swift gleich.

06 · Vom Treiber zu implementierende Dinge

Die Arbeit zur Unterstützung eines neuen Karten-SDK beschränkt sich darauf, von dem abstrakten Renderer für jedes Element zu erben und die 3 Operationen auszufüllen. Das Core-Modell und die Differenzlogik werden nicht berührt.

android-for-googlemaps · Implementierung des Renderers
// Der Treiber erbt vom abstrakten Renderer und füllt nur drei Operationen aus
internal class GoogleMapPolygonOverlayRenderer(
    override val holder: GoogleMapViewHolder,
    override val coroutine: CoroutineScope,
) : AbstractPolygonOverlayRenderer<GoogleMapActualPolygon>() {

    override suspend fun createPolygon(state: PolygonState) = /* map.addPolygon(...) */
    override suspend fun updatePolygonProperties(polygon, current, prev) = /* Nur die Differenz anwenden */
    override suspend fun removePolygon(entity: PolygonEntityInterface<GoogleMapActualPolygon>) =
        entity.polygon.remove()
}

Nur die 3 Vorgänge Erstellen, Aktualisieren und Löschen sind anbieterspezifisch. Die Differenzbestimmung, die Verwaltung des Registers und der Hit-Test werden aufgerufen, nachdem Core sie bereits erledigt hat.

07 · Umfang der Abstraktion und Notausgänge

MapConductor kapselt nicht alle Funktionen jedes Karten-SDKs. Der Fokus liegt auf gemeinsam genutzten, häufig verwendeten Operationen; für Anforderungen, die darüber hinausgehen, werden zwei Auswege angeboten.

getMapViewHolder() · Direkter Aufruf nativer Funktionen
// map der gemeinsamen Schnittstelle ist unknown — vor der Nutzung auf den Anbietertyp einengen
const holder = mapViewState.getMapViewHolder();
const map = holder?.map as maplibregl.Map | undefined;

map?.addLayer({ id: 'buildings', type: 'fill-extrusion', source: 'composite' });

Ein Entwurf, der die gemeinsame API einfach hält und gleichzeitig die spezifischen Stärken der einzelnen Anbieter nicht beeinträchtigt. Weitere Informationen finden Sie unter „Native Erweiterungen“.

Verwandte Seiten