दस्तावेज़ / एक्सटेंशन लेयर / एक्सटेंशन मॉड्यूल बनाना

एक्सटेंशन मॉड्यूल बनाना

हीटमैप, GeoJSON लेयर और मार्कर क्लस्टरिंग ये सभी Core को बदले बिना बाहर से जोड़े गए अलग-अलग पैकेज हैं। एक ही इंटरफेस सार्वजनिक है, इसलिए तीसरे पक्ष भी उसी तरीके से अपने एक्सटेंशन वितरित कर सकते हैं। यह पृष्ठ उस चरण-दर-चरण प्रक्रिया के बारे में है।

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

00 · एक्सटेंशन मॉड्यूल का रूप

एक्सटेंशन मॉड्यूल एक स्वतंत्र पैकेज है जो केवल कोर मॉड्यूल पर निर्भर करता है। यह प्रोवाइडर पैकेज (react-for-*, android-for-*, ios-for-*) पर निर्भर नहीं करता है। यदि इसका पालन किया जाता है, तो उपयोगकर्ता द्वारा किसी भी मैप प्रोवाइडर को चुने जाने पर भी आपका एक्सटेंशन काम करेगा।

OK

केवल कोर के सार्वजनिक प्रकारों (स्थिति, कलेक्टर, कंट्रोलर कॉन्ट्रैक्ट, टाइल सर्वर, सर्विस रजिस्ट्री) का उपयोग करें।

NG

प्रोवाइडर के पैकेज को import करें। कंट्रोलर को अन्य प्रकार में कास्ट करके आंतरिक फ़ील्ड देखें।

शिप किए गए 3 पैकेज संदर्भ कार्यान्वयन के रूप में उपलब्ध हैं। जो चीज़ आप बनाना चाहते हैं उसके समान को पढ़ना सबसे आसान तरीका है।

संदर्भ के लिए कार्यान्वयन
heatmap            रास्टर टाइल खुद खींचता है        · टाइल सर्वर + RasterLayer
geojson-layer      बहुत सारे वेक्टर को टाइल बनाता है · टाइल सर्वर + RasterLayer
marker-clustering  मार्कर बदलकर वापस देता है         · MarkerState + सर्विस रजिस्ट्री
प्लेटफ़ॉर्म

01 · पैकेज बनाना

निर्भरता केवल कोर तक सीमित रखें। प्रोवाइडर उपयोगकर्ता के ऐप द्वारा चुना जाता है, इसलिए आपके पैकेज से यह दिखाई नहीं देता है।

build.gradle.kts
dependencies {
    implementation("com.mapconductor:core")
    implementation("com.mapconductor:compose") // अगर Composable बाहर देना है
    // android-for-* पर निर्भर न रहें
}

02 · स्थिति परिभाषित करना

उपयोगकर्ता स्टेट ऑब्जेक्ट के साथ इंटरैक्ट करते हैं। यदि आप कोर के स्टेट प्रकार (MarkerState आदि) के समान कन्वेंशन का पालन करते हैं, तो उपयोगकर्ताओं को कुछ और याद रखने की आवश्यकता नहीं होगी। मान बदलने की सूचना देने की एक विधि (फिंगरप्रिंट) शामिल करने से, आप इसे कोर कलेक्टर पर लोड करके इनक्रीमेंटल अपडेट प्राप्त कर सकते हैं।

कोर द्वारा प्रदान की गई चीज़ें
OverlayCollector       id → स्टेट का मैप; जोड़ना/हटाना/अंतर और बदलाव की सूचना एक जगह
ComponentState         id रखने वाले स्टेट का अनुबंध (Android)
fingerPrint()          तुलना के लिए मान, जिसमें सिर्फ़ वही है जो चित्रण पर असर डालता है

03 · रेंडरिंग एक्सिट चुनना

यह सबसे महत्वपूर्ण डिज़ाइन निर्णय है। प्रत्येक प्रोवाइडर के लिए रेंडरिंग कोड लिखने के बजाय, इसे कोर द्वारा पहले से प्रदान किए गए एक्सिट में भेजें। दो एक्सिट हैं।

A · RasterLayer

खुद से टाइल बनाएं

स्थानीय टाइल सर्वर पर रेंडरिंग फ़ंक्शन पंजीकृत करें और उस URL टेम्पलेट की ओर इशारा करने वाला एक RasterLayer रखें। हीटमैप और 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)

// हटते समय clear() नहीं, remove() — ताकि दूसरी capability साथ में न गिरें
DisposableEffect(state) {
    onDispose { state.serviceRegistry.remove(MyCapabilityKey) }
}
कुंजी को ज़रूर समाधान करने वाला पक्ष (एक्सटेंशन मॉड्यूल) सार्वजनिक करना चाहिए। प्रदाता उस कुंजी को import करके पंजीकृत करता है। इसके विपरीत करने से प्रदाता एक्सटेंशन पर निर्भर हो जाएगा, और निर्भरता की दिशा उलट जाएगी।

पालन करने योग्य

इन 4 का पालन करते हुए, उपयोगकर्ता प्रदाता को बदलने पर भी या दूसरे एक्सटेंशन के साथ एक साथ उपयोग करने पर भी यह टूटेगा नहीं।

1 · प्रोवाइडर के पैकेज को import न करें

ज़रूरत पड़ने पर, वह एक ऐसी सुविधा है जिसे आपको सर्विस रजिस्ट्री से प्राप्त करना चाहिए।

2 · कंट्रोलर को कास्ट करके अंदर न झांकें

react-heatmap पहले ऐसा करता था और सिंगल-स्लॉट कैमरा लिसनर को सेव और रिस्टोर करता था। दो एक्सटेंशन लगने पर वे एक-दूसरे को ओवरराइट कर देंगे। इसे सार्वजनिक registerOverlayController से बदल दिया गया है।

3 · पंजीकृत चीज़ों को हमेशा रद्द करें

ओवरले कंट्रोलर, टाइल सर्वर का ग्रुप, सर्विस रजिस्ट्री की कुंजी। remove() केवल एक आइटम वापस लेता है।

4 · अगर पंजीकृत नहीं है तो शांत से अक्षम करें

उन प्रोवाइडर पर जिनमें आवश्यक capability नहीं है, बिना अपवाद फेंके कुछ भी न बनाएं। उपयोगकर्ता अन्य हिस्सों को चलाते रह सकते हैं।

संबंधित पेज