Dokumentation / Erweiterungs-Layer / Ein Erweiterungsmodul erstellen

Ein Erweiterungsmodul erstellen

Heatmap, GeoJSON-Layer und Marker-Clustering sind allesamt separate Pakete, die von außen hinzugefügt wurden, ohne den Core zu ändern. Da die gleiche Schnittstelle öffentlich verfügbar ist, können Dritte ihre eigenen Erweiterungen auf die gleiche Weise verteilen. Diese Seite beschreibt die Vorgehensweise.

ANDROID
com.mapconductor:core
iOS
MapConductorCore
REACT
@mapconductor/js-sdk-core

00 · Form eines Erweiterungsmoduls

Ein Erweiterungsmodul ist ein unabhängiges Paket, das nur vom Kernmodul abhängt. Es hängt nicht von Provider-Paketen (react-for-*, android-for-*, ios-for-*) ab. Wenn dies eingehalten wird, funktioniert Ihre Erweiterung unabhängig davon, welchen Karten-Provider der Nutzer wählt.

OK

Nur die öffentlichen Typen des Kerns verwenden (Status, Collector, Controller-Vertrag, TileServer, ServiceRegistry).

NG

Das Paket des Providers importieren. Den Controller in einen anderen Typ casten und in die internen Felder schauen.

Die drei bereits ausgelieferten Module dienen als Referenzimplementierungen. Der kürzeste Weg ist, das Modul zu lesen, das dem am ähnlichsten ist, das Sie erstellen möchten.

Referenzimplementierung
heatmap            zeichnet Rasterkacheln selbst     · Kachelserver + RasterLayer
geojson-layer      kachelt große Vektormengen        · Kachelserver + RasterLayer
marker-clustering  bearbeitet Marker und gibt zurück · MarkerState + Service-Registry
Plattform

01 · Ein Paket erstellen

Die Abhängigkeiten beschränken sich auf den Core. Der Provider wird von der App des Nutzers gewählt und ist daher von Ihrem Paket aus nicht sichtbar.

build.gradle.kts
dependencies {
    implementation("com.mapconductor:core")
    implementation("com.mapconductor:compose") // Wenn Sie ein Composable veröffentlichen
    // Nicht von android-for-* abhängen
}

02 · Zustand definieren

Der Nutzer interagiert mit dem Zustandsobjekt. Wenn Sie dasselbe Muster wie die Core-Zustandstypen (z. B. `MarkerState`) beibehalten, muss der Nutzer nichts Neues lernen. Wenn Sie einen Mechanismus zum Benachrichtigen über Wertänderungen (Fingerabdruck) vorsehen, können Sie ihn im Core-Collector verwenden, um inkrementelle Updates zu erhalten.

Vom Core bereitgestellte Elemente
OverlayCollector       eine id → State-Map; bündelt Hinzufügen/Entfernen/Differenz und Änderungsmeldungen
ComponentState         der Vertrag für einen State mit id (Android)
fingerPrint()          ein Vergleichswert, der nur das Zeichnungsrelevante enthält

03 · Ausgabe für das Rendering wählen

Dies ist die wichtigste Designentscheidung. Anstatt für jeden Provider Rendering-Code zu schreiben, leiten Sie ihn an die bereits im Core vorhandenen Ausgaben weiter. Es gibt zwei Ausgaben.

A · RasterLayer

Tiles selbst zeichnen

Eine Renderingfunktion bei einem lokalen TileServer registrieren und ein einziges RasterLayer platzieren, das auf dieses URL-Template zeigt. Heatmap und GeoJSON-Ebenen sind so aufgebaut. Je größer die Anzahl, desto vorteilhafter; das Erscheinungsbild ist bei jedem Provider völlig identisch.

B · Vorhandenes Overlay

Als Marker oder Form zurückgeben

Die Eingabe verarbeiten und in MarkerState / PolygonState usw. umwandeln, dann in den Collector schreiben. Ab dort übernimmt der normale Rendering-Pfad des Providers. Clustering funktioniert so. Klicks und Drag-and-Drop funktionieren unverändert.

A · Kachelserver + RasterLayer (gleich wie android-heatmap)
val tileServer = TileServerRegistry.get()
val renderer = MyTileRenderer(tileSize = 256)   // Enthält die Funktion, die eine einzelne Kachel zeichnet

DisposableEffect(groupId) {
    tileServer.register(groupId, renderer)
    onDispose { tileServer.unregister(groupId) }
}

val layer = remember {
    RasterLayerState(
        id = "my-ext-$groupId",
        source = RasterLayerSource.UrlTemplate(
            template = tileServer.urlTemplate(groupId, renderer.tileSize),
            tileSize = renderer.tileSize,
            scheme = TileScheme.XYZ,
        ),
    )
}
RasterLayer(layer)
Wenn Sie B wählen, schreiben Sie in React nicht direkt in den Collector, sondern Sie erhalten den vom Anbieter registrierten `MarkerRenderingSupport` aus dem Serviceregister und erstellen einen Renderer (siehe 05). Dies dient dazu, dass der Anbieter den Rendering-Pfad austauschen kann.

04 · Kamera und Bereinigung

Wenn Sie Zoom- oder Schwenkbewegungen folgen möchten, manipulieren Sie nicht direkt die Ereignisse des Karten-Controllers. Dies führt zu einer Konkurrenz um einzelne Slot-Listener und bricht in Umgebungen, in denen zwei Erweiterungen geladen sind. Wenn Sie sich als Overlay-Controller registrieren, erhalten Kameraänderungen über denselben Weg wie andere Overlays.

Dasselbe Format wie `HeatmapCameraController.kt`
class MyCameraController(
    private val renderer: MyTileRenderer,
) : OverlayControllerInterface<Unit, Unit>, OnCameraChangeReceiverInterface {
    override val zIndex: Int = 0
    override suspend fun add(data: List<Unit>) {}
    override suspend fun update(state: Unit) {}
    override suspend fun clear() {}
    override fun find(position: GeoPointInterface): Unit? = null

    override suspend fun onCameraChanged(mapCameraPosition: MapCameraPosition) {
        renderer.updateCameraZoom(mapCameraPosition.zoom)
    }

    override fun destroy() {}
}

// Registrieren
val mapController = LocalMapViewController.current
DisposableEffect(mapController, cameraController) {
    mapController.registerOverlayController(cameraController)
    onDispose { cameraController.destroy() }
}
Sie müssen alles Registrierte unbedingt wieder aufheben. Da es Nutzer gibt, die nur den Anbieter austauschen, ohne die Karte zu verwerfen, führt eine fehlende Aufhebung dazu, dass der Renderer des vorherigen Anbieters weiterhin festgehalten wird.

05 · Funktionalität übergeben

Wird nur verwendet, wenn die bisherigen 4 nicht ausreichen. Wenn Sie etwas benötigen, das nur vom Anbieter erstellt werden kann, erhalten Sie es über `MapServiceRegistry`. Da die Registrierung und der Abruf über typisierte Schlüssel erfolgen, ist kein Casting erforderlich.

Ein konkretes Beispiel ist das Marker-Clustering. Da der native Markertyp je nach Anbieter unterschiedlich ist, kann das Clustering den Renderer nicht selbst erstellen. Daher wird das vom Anbieter registrierte `MarkerRenderingSupport` abgerufen und von dort ein Renderer erstellt.

Die lösende Seite (Erweiterungsmodul)
// Den Schlüssel als Singleton-Object definieren
object MyCapabilityKey : MapServiceKey<MyCapability>

// Abrufen. Nicht registriert heißt null — entscheiden Sie, ob Sie ohne die Funktion weitermachen oder abbrechen
val services = LocalMapServiceRegistry.current
val capability = services.get(MyCapabilityKey) ?: return
Die registrierende Seite (Anbieter)
// Eine Registry pro Karte, gehalten vom State (wie bei react und ios)
state.serviceRegistry.put(MyCapabilityKey, myCapability)

// Beim Verschwinden remove() statt clear() — damit andere Capabilities nicht mitgerissen werden
DisposableEffect(state) {
    onDispose { state.serviceRegistry.remove(MyCapabilityKey) }
}
Der Schlüssel muss unbedingt von der lösenden Seite (Erweiterungsmodul) öffentlich gemacht werden. Der Anbieter importiert diesen Schlüssel und registriert ihn. Andernfalls würde der Anbieter von der Erweiterung abhängen, wodurch sich die Abhängigkeitsrichtung umkehrt.

Einzuhalten

Wenn Sie diese 4 Punkte einhalten, wird es nicht beschädigt, wenn Nutzer den Anbieter austauschen oder es gleichzeitig mit einer anderen Erweiterung verwenden.

1 · Das Paket des Providers nicht importieren

Wenn es erforderlich wird, sollte es eine Funktion sein, die über die ServiceRegistry empfangen wird.

2 · Den Controller nicht casten, um in ihn hineinzuschauen

react-heatmap hat das früher getan und einen einzelnen Camera-Listener-Slot gesichert und wiederhergestellt. Wenn zwei Erweiterungen geladen sind, überschreiben sie sich gegenseitig. Es wurde bereits durch das öffentliche registerOverlayController ersetzt.

3 · Registrierte Dinge immer aufheben

Overlay-Controller, Gruppen von TileServern, Schlüssel der ServiceRegistry. remove() hebt nur einen Eintrag auf.

4 · Wenn nicht registriert, still deaktivieren

Bei Providern ohne die erforderliche Capability sollte nichts gezeichnet werden, ohne eine Exception zu werfen. Nutzer können den Rest der Anwendung weiter bedienen.

Verwandte Seiten