เอกสาร / เลเยอร์ส่วนขยาย / สร้างโมดูลส่วนขยาย

สร้างโมดูลส่วนขยาย

Heatmap, GeoJSON Layer และ Marker Clustering เป็นแพ็กเกจแยกต่างหากที่เพิ่มเข้ามาจากภายนอกโดยไม่ต้องแก้ไข Core ด้วยเหตุที่ส่วนติดต่อเดียวกันถูกเปิดเผยสู่สาธารณะ บุคคลที่สามจึงสามารถกระจายส่วนขยายของตนเองด้วยวิธีเดียวกันได้ หน้านี้แสดงขั้นตอนดังกล่าว

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

00 · รูปแบบของโมดูลส่วนขยาย

โมดูลส่วนขยายคือ แพ็กเกจอิสระที่ขึ้นอยู่กับโมดูลหลักเท่านั้น ไม่ได้ขึ้นอยู่กับแพ็กเกจของผู้ให้บริการ (react-for-*, android-for-*, ios-for-*) หากปฏิบัติตามสิ่งนี้ ส่วนขยายของคุณจะทำงานได้โดยไม่คำนึงถึงว่าผู้ใช้เลือกผู้ให้บริการแผนที่รายใด

OK

ใช้เฉพาะประเภทสาธารณะของคอร์เสียง (สถานะ, ตัวรวบรวม, สัญญาคอนโทรลเลอร์, TileServer, ServiceRegistry).

NG

นำเข้าแพ็กเกจของผู้ให้บริการ แคสต์คอนโทรลเลอร์ไปยังประเภทอื่นและดูภายในฟิลด์

ทั้ง 3 รายการที่จัดส่งแล้วทำหน้าที่เป็นการนำไปใช้จริงอ้างอิง วิธีที่เร็วที่สุดคืออ่านรายการที่ใกล้เคียงกับสิ่งที่คุณต้องการสร้างที่สุด

การนำไปใช้จริงอ้างอิง
heatmap            วาดไทล์ราสเตอร์เอง            · เซิร์ฟเวอร์ไทล์ + RasterLayer
geojson-layer      แปลงเวกเตอร์จำนวนมากเป็นไทล์   · เซิร์ฟเวอร์ไทล์ + RasterLayer
marker-clustering  ปรับมาร์กเกอร์แล้วส่งกลับ      · MarkerState + รีจิสทรีบริการ
แพลตฟอร์ม

01 · สร้างแพ็กเกจ

ข้อกำหนดเบื้องต้นจะจำกัดอยู่ที่ Core เท่านั้น ผู้ให้บริการคือสิ่งที่แอปของผู้ใช้เลือก ดังนั้นจึงมองไม่เห็นจากแพ็กเกจของคุณ

build.gradle.kts
dependencies {
    implementation("com.mapconductor:core")
    implementation("com.mapconductor:compose") // หากจะเปิดเผย Composable
    // ไม่ต้องพึ่ง android-for-*
}

02 · กำหนดสถานะ

ผู้ใช้โต้ตอบกับออบเจ็กต์สถานะ หากคุณคงรูปแบบเดียวกับประเภทสถานะของ Core (เช่น `MarkerState`) ผู้ใช้จะไม่ต้องเรียนรู้สิ่งใหม่หากคุณรวมกลไกในการแจ้งเตือนการเปลี่ยนแปลงค่า (ลายนิ้วมือ) คุณสามารถวางไว้ในตัวรวบรวมของ Core เพื่อรับการอัปเดตแบบเพิ่มหลัง

องค์ประกอบที่ Core จัดเตรียมไว้
OverlayCollector       แมป id → สถานะ รวมการเพิ่ม/ลบ/ส่วนต่าง และการแจ้งค่าที่เปลี่ยน
ComponentState         สัญญาของสถานะที่มี id (Android)
fingerPrint()          ค่าสำหรับเปรียบเทียบ เก็บเฉพาะสิ่งที่มีผลต่อการวาด

03 · เลือกเอาต์พุตการเรนเดอร์

นี่คือการตัดสินใจด้านการออกแบบที่สำคัญที่สุด แทนที่จะเขียนโค้ดการเรนเดอร์สำหรับแต่ละผู้ให้บริการ ให้ส่งต่อไปยังเอาต์พุตที่ Core มีอยู่แล้ว มีเอาต์พุต 2 รายการ

A · RasterLayer

วาดไทล์เอง

ลงทะเบียนฟังก์ชันการเรนเดอร์กับ TileServer ในเครื่อง และวาง RasterLayer ชั้นเดียวที่ชี้ไปที่เทมเพลต URL นั้น แผนที่ความร้อนและเลเยอร์ GeoJSON เป็นเช่นนี้ ยิ่งจำนวนมากยิ่งได้เปรียบ และรูปลักษณ์จะเหมือนกันทุกประการในทุกผู้ให้บริการ

B · การซ้อนทับที่มีอยู่

ส่งคืนเป็นเครื่องหมายหรือรูปร่าง

ประมวลผลอินพุตและแปลงเป็น MarkerState / PolygonState ฯลฯ แล้วเขียนลงในตัวรวบรวม จากนั้นเส้นทางการเรนเดอร์ปกติของผู้ให้บริการจะดำเนินการต่อ การจัดกลุ่มเป็นเช่นนี้ การคลิกและลากยังคงทำงานได้ตามปกติ

A · เซิร์ฟเวอร์ไทล์ + RasterLayer (เหมือน android-heatmap)
val tileServer = TileServerRegistry.get()
val renderer = MyTileRenderer(tileSize = 256)   // มีฟังก์ชันที่วาดไทล์หนึ่งแผ่น

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)
หากคุณเลือก B ใน React คุณไม่เขียนโดยตรงลงในตัวรวบรวม แต่คุณรับ `MarkerRenderingSupport` ที่ผู้ให้บริการลงทะเบียนจากรีจิสตรีบริการและสร้างเรนเดอร์ (ดู 05) นี่คือเพื่อให้ผู้ให้บริการสามารถแทนที่เส้นทางการเรนเดอร์ได้

04 · กล้องและการทำความสะอาด

หากคุณต้องการติดตามการซูมหรือการพาน อย่าแก้ไขเหตุการณ์ของตัวควบคุมแผนที่โดยตรง สิ่งนี้จะนำไปสู่การแย่งชิงผู้ฟังสล็อตเดียวและจะเสียหายในสภาพแวดล้อมที่มีการโหลดส่วนขยายสองตัว หากคุณลงทะเบียนเป็นตัวควบคุมซ้อนทับ การเปลี่ยนแปลงกล้องจะมาถึงผ่านเส้นทางเดียวกันกับซ้อนทับอื่นๆ

รูปแบบเดียวกับ `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() {}
}

// ลงทะเบียน
val mapController = LocalMapViewController.current
DisposableEffect(mapController, cameraController) {
    mapController.registerOverlayController(cameraController)
    onDispose { cameraController.destroy() }
}
คุณต้องยกเลิกสิ่งที่ลงทะเบียนไว้ทั้งหมดอย่างแน่นอน เนื่องจากมีผู้ใช้ที่เปลี่ยนผู้ให้บริการเท่านั้นโดยไม่ทำลายแผนที่ การไม่ยกเลิกจะทำให้ยังคงจับเรนเดอเรอร์ของผู้ให้บริการก่อนหน้าไว้

05 · ส่งต่อฟังก์ชันการทำงาน

จะใช้เฉพาะเมื่อ 4 รายการก่อนหน้านี้ไม่เพียงพอ เมื่อคุณต้องการสิ่งที่ ผู้ให้บริการเท่านั้นที่สร้างได้ คุณจะได้รับสิ่งนั้นผ่าน `MapServiceRegistry` เนื่องจากการลงทะเบียนและการดึงข้อมูลทำด้วยคีย์ที่มีประเภท จึงไม่ต้องการการแปลงประเภท

ตัวอย่างที่เป็นรูปธรรมคือการคลัสเตอร์มาร์กเกอร์ เนื่องจากประเภทมาร์กเกอร์เนทีฟแตกต่างกันไปในแต่ละผู้ให้บริการ การคลัสเตอร์จึงไม่สามารถสร้างเรนเดอเรอร์ด้วยตนเองได้ ดังนั้นจึงดึง `MarkerRenderingSupport` ที่ลงทะเบียนโดยผู้ให้บริการและสร้างเรนเดอเรอร์จากที่นั่น

ฝั่งที่แก้ไข (โมดูลส่วนขยาย)
// นิยามคีย์เป็น singleton object
object MyCapabilityKey : MapServiceKey<MyCapability>

// ดึงออกมา หากยังไม่ลงทะเบียนจะได้ null จึงต้องเลือกว่าจะทำงานต่อโดยไม่มีฟีเจอร์นี้หรือหยุด
val services = LocalMapServiceRegistry.current
val capability = services.get(MyCapabilityKey) ?: return
ฝั่งที่ลงทะเบียน (ผู้ให้บริการ)
// หนึ่งรีจิสทรีต่อหนึ่งแผนที่ เก็บอยู่ใน state (เหมือน react และ ios)
state.serviceRegistry.put(MyCapabilityKey, myCapability)

// ตอนหายไปให้ใช้ remove() ไม่ใช่ clear() เพื่อไม่ให้ capability อื่นหลุดไปด้วย
DisposableEffect(state) {
    onDispose { state.serviceRegistry.remove(MyCapabilityKey) }
}
คีย์ต้องถูกเผยแพร่โดยฝั่งที่แก้ไข (โมดูลส่วนขยาย) เท่านั้น ผู้ให้บริการจะนำเข้าคีย์นั้นและลงทะเบียน หากทำกลับกัน ผู้ให้บริการจะขึ้นอยู่กับส่วนขยาย ซึ่งจะทำให้ทิศทางการพึ่งพากลับด้าน

สิ่งที่ต้องปฏิบัติตาม

หากคุณปฏิบัติตามจุดทั้ง 4 นี้ จะไม่เสียหายเมื่อผู้ใช้เปลี่ยนผู้ให้บริการหรือใช้ร่วมกับส่วนขยายอื่น

1 · อย่านำเข้าแพ็กเกจของผู้ให้บริการ

หากจำเป็นต้องใช้ ควรเป็นฟังก์ชันที่ได้รับผ่าน ServiceRegistry

2 · อย่าแคสต์คอนโทรลเลอร์เพื่อดูภายใน

react-heatmap ทำเช่นนี้มาก่อน โดยบันทึกและคืนค่าสล็อตผู้ฟังกล้องเดียว เมื่อมีส่วนขยายสองตัว จะเขียนทับซึ่งกันและกัน ได้รับการเปลี่ยนแทนที่ด้วย registerOverlayController สาธารณะแล้ว

3 · ต้องยกเลิกสิ่งที่ลงทะเบียนไว้เสมอ

คอนโทรลเลอร์การซ้อนทับ, กลุ่ม TileServer, คีย์ของ ServiceRegistry remove() จะยกเลิกเพียงรายการเดียว

4 · หากไม่ได้ลงทะเบียน ให้ปิดใช้งานอย่างเงียบๆ

สำหรับผู้ให้บริการที่ไม่มีความสามารถที่จำเป็น ไม่ควรวาดอะไรเลยโดยไม่โยนข้อยกเว้น ผู้ใช้สามารถใช้งานส่วนอื่นต่อได้

หน้าที่เกี่ยวข้อง