Expose core to plugins via engine-agnostic map:* APIs#75
Expose core to plugins via engine-agnostic map:* APIs#75BhattaraiSijan wants to merge 21 commits into
map:* APIs#75Conversation
| tileSize: 512, | ||
| zoomOffset: -1, | ||
| minZoom: 1, | ||
| attribution: '© Mapbox © OpenStreetMap', |
There was a problem hiding this comment.
Would it make sense to extract these into shared constants/defaults? Mainly to avoid scattering hardcoded values (tileSize, zoomOffset, attribution strings) and magic numbers/strings
There was a problem hiding this comment.
That makes sense @dzole0311 . Thanks.
For now, I just added those constants in the helper and imported them in the adapter.
We should discuss about maybe storing all such engine specific constants to some engine specific configuration files.
| setBasemapStyle(styleUrl: string): void { | ||
| if (!this._map) return | ||
| const spec = this._resolveBasemapTileSpec({ | ||
| provider: this._inferProvider(styleUrl), |
There was a problem hiding this comment.
Is provider actually used here? From this flow it looks like the resolution is done by styleUrl alone so wondering if provider is redundant
There was a problem hiding this comment.
Yes, it is redundant here. Thanks @dzole0311 .
As leaflet adapter does not actually spin up mapbox-gl or maplibre-gl based on provider, instead leafletadapter just uses L.tileLayer for both, I just ommitted the provider from the type setBasemapStyle
…-IMPACT/MMGIS into feat/add-basemap-to-leaflet
|
|
||
| const config = { | ||
| msv: { | ||
| view: [39, -98, 4], |
There was a problem hiding this comment.
Should this view be hard coded? This is not limited to earth so doesn't make sense to have it center on USA.
Also, I created new mission and it is still initializing to 0, 0, 0 so this might not be working as intended.
| "field": "msv.basemap.provider", | ||
| "name": "Provider", | ||
| "description": "Renders a vector-tile basemap beneath deck.gl layers using MapboxOverlay. Has no effect on Leaflet-engine missions. 'maplibre' is open-source and requires no token. 'mapbox' requires an access token.", | ||
| "description": "Renders a vector-tile basemap beneath map layers using MapLibre or Mapbox GL. Works with both Leaflet and deck.gl engine missions. 'maplibre' is open-source and requires no token. 'mapbox' requires an access token.", |
There was a problem hiding this comment.
Do we need to mention that it works with both Leaflet and deck.gl? Might make more sense to only specify if something is engine specific or UI specific
| ] | ||
|
|
||
| const mapboxDefaults = MAPBOX_DEFAULTS | ||
| const maplibreDefaults = isLeaflet ? MAPLIBRE_DEFAULTS_LEAFLET : MAPLIBRE_DEFAULTS_DECKGL |
There was a problem hiding this comment.
Is it safe to assume we'll only have leaflet or deckgl? or should we add explicit checks?
| { name: 'Light', style: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png' }, | ||
| { name: 'Dark', style: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png' }, | ||
| { name: 'Terrain', style: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png' }, | ||
| ] |
There was a problem hiding this comment.
Should these styles be hardcoded? Might be difficult to change/update things in future.
There was a problem hiding this comment.
There might be the case where different style might need different access tokens
| ? [...basemapConfig.styles] | ||
| : basemapConfig.provider === 'mapbox' | ||
| ? [...mapboxDefaults] | ||
| : [...maplibreDefaults] |
There was a problem hiding this comment.
If I understand the logic correctly, it'll use the styles from mission config and fall back to default values if basemap isn't provided.
If that's the case, we need a way to provide multiple styles in the mission config page and a field to mark default style.
One potential issue might be when we have something other than earth. Right now, leaflet doesn't render any basemap so user can just add other planet/satellite as tile layer. This need not be addressed right away but should be a simple fix.
There was a problem hiding this comment.
I might have found out the root of confusion, there's basemapConfig.styles to store all the styles and basemapConfig.style to store current style. I believe there might be better way to handle this.
One potential drawback will be the need to update both .style and .styles if there's change in name resulting in inconsistency.
| Map_.engine.setZoom(next) | ||
| return true | ||
| }), | ||
| window.mmgisAPI.provide('map:addOverlay', ({ id, geojson, style } = {}) => { |
There was a problem hiding this comment.
Is the geojson already validated before being passed here?
| return true | ||
| }), | ||
| window.mmgisAPI.provide('map:removeOverlay', (id) => { | ||
| if (!Map_.engine || !_overlayIds.has(id)) return false |
There was a problem hiding this comment.
will it be helpful if we log error (here and all other functions) when the action fails?
| * on top of a MapLibre/Mapbox GL basemap. | ||
| * | ||
| * **Leaflet**: Creates a MapLibre/Mapbox GL map behind a transparent Leaflet canvas | ||
| * and synchronises pan/zoom events so the basemap and Leaflet layers stay aligned. |
There was a problem hiding this comment.
Have we not scrapped this idea and instead decided on having basemap as one of the layer in leaflet?
| * Optional vector-tile basemap to render beneath map layers. | ||
| * | ||
| * - **deck.gl**: renders via `MapboxOverlay` (overlay mode). | ||
| * - **Leaflet**: renders a MapLibre/Mapbox GL map behind a transparent Leaflet canvas. |
There was a problem hiding this comment.
same as above, will we have mapbox beneath leaflet or will we use a raster layer as basemap?
| * | ||
| * @throws {Error} If `options.type` is not a supported layer type. | ||
| */ | ||
| function _toRgba( |
There was a problem hiding this comment.
- I don't see this being used anywhere
- This is not specific to deck.gl so even if this is needed, it can be moved from DeckGLHelpers to some place generic
- The comment above this function is for buildDeckLayer and should be moved
| } | ||
| } | ||
|
|
||
| setBasemapStyle(styleUrl: string): void { |
There was a problem hiding this comment.
Do we need to respect basemap's minZoom setting when making the switch?
| // BASEMAP TILE LAYER METHODS | ||
| // ======================================== | ||
|
|
||
| private _initBasemapTileLayer(basemap: BasemapOptions): void { |
There was a problem hiding this comment.
Can this call setBasemapStyle for setting the style? I can see quite a bit of overlap between the two
Main change
Prepare the core to listen to plugins through the
mmgisAPIEvent Bus. New providers and emits are registered inMap_.jsand routed throughMap_.engine.*so Leaflet and deck.gl behave identically.addOverlay/removeOverlay/clearOverlayssetBasemap/getBasemap/getBasemapStyleszoomIn/zoomOuton('map:click' | 'map:mousemove', h){lat, lng, latlng, containerPoint?}src/essence/Ancillary/BasemapSwitcher.js(jQuery UI) is removed.Adapter changes
DeckGLAdapter.setBasemapStyle()— didn't exist; added for parity.DeckGLAdapter._emitClick/_emitMouseMove— deck.gl'sonClick/onHoveronly invoked internal feature handlers, soon('click' | 'mousemove', …)subscribers never fired. Added bridges normalizingPickingInfoto the same shapeLeafletAdapter._normalizeEventemits (includinglatlngso legacy consumers likeAncillary/Coordinates.jswork unchanged on deck.gl).DeckGLHelperscase 'vector'— deck.gl accepted only its native style keys (strokeColor/strokeWidthas RGBA). Leaflet's adapter accepts Leaflet's native vocab (color/weight/fillColoras CSS) directly. Made both adapters accept the same style dictionary via a_toRgbaparser and Leaflet→deck.gl key aliases (deck.gl-native wins when both given). Also fixed a latent bug — deck.gl's default point radius is in meters, sub-pixel at typical zooms — viagetPointRadius+pointRadiusUnits: 'pixels'.Leaflet basemap via native
L.tileLayerThe Leaflet engine now supports
msv.basemap.provider = 'mapbox' | 'maplibre'(previously deck.gl-only). An earlier iteration mounted a MapLibre/Mapbox GLMapbehind a transparent Leaflet with event-based camera sync — two render loops and a 256-vs-512 tile-size mismatch caused overlay drift and mis-projected clicks (tools recording coordinates saved wrong data).Replaced with native
L.tileLayer._resolveBasemapTileSpectranslatesmapbox://styles/...→ Mapbox Static Tiles API, raw{z}/{x}/{y}templates → passthrough, anything else → OSM fallback. One render loop, one projection.Other
minZoom: 1on Mapbox tile layers. Mapbox's 512-sized Static Tiles +zoomOffset: -1means Leaflet zoom 0 resolves to URL zoom -1, which doesn't exist. MapminZoombumped to match._resolveBasemapStyles(config, engineType)returns engine-appropriate defaults — Leaflet gets raster templates, deck.gl gets MapLibre vector style.json URLs. Same user-facing names on both (Streets / Light / Dark; Leaflet adds Terrain).'none'added toBasemapProviderunion, removing a pre-existingas any.tab-ui-configupdated to surface the basemap option for Leaflet missions.Test plan
Until the companion plugin PR lands, exercise the APIs from the browser console on a running mission via
window.mmgisAPI.*. For example:Verify on each matrix cell (Leaflet + Mapbox, Leaflet + MapLibre, deck.gl + Mapbox, deck.gl + MapLibre):
setBasemap(name)swaps styles;zoomIn/zoomOutstep and clamp.on('map:click' | 'map:mousemove', …)fires with{lat, lng, latlng, containerPoint}.removeOverlay/clearOverlaysremove cleanly.