Map SDK API
Package: @agastyadreamspty/map-sdk
This document is the developer reference for integrating the reusable Map SDK into Angular applications.
It is written for engineers who need to:
- understand the supported public API
- embed the full host or individual widgets
- wire custom adapters or renderers
- control runtime behavior through configuration
- verify which responsibilities belong to the SDK versus the consumer app
The SDK is designed to be composable, so consumers can adopt:
- the full map explorer page
- filters only
- legend only
- parcel search only
- zoom and layer status only
- a custom map renderer
- a custom backend adapter
The package name is SDK-first, but some exported contract names still use the
MapFeature* prefix for compatibility with the current codebase.
Public Surface
The long-term supported entry points are:
MapFeatureConfigMapFeatureZoomPolicyMapFeatureAdapterMapFeatureAuthAdapterMapFeaturePermissionAdapterMapFeatureRendererAdapterMapFeatureProviderOptionsMapFeatureAngularAdapterMapFeatureRuntimeConfigMapFeatureHttpConfigMapFeatureThemeVarsFeatureEventBusFeatureSdkEventFeatureSdkSelectionEventFeatureSdkLayerVisibilityEventMapFeatureLayerClickEventFeatureErrorFeatureErrorCodeprovideMapSdk(...)provideMapFeatureRuntimeConfig(...)provideMapFeatureHttpConfig(...)provideMapFeatureAdapter(...)provideMapFeatureDataSource(...)provideMapFeatureRendererAdapter(...)MapSdkHostComponentMapSdkHostShellComponentMapSdkFiltersComponentMapSdkLegendPanelComponentMapSdkParcelSearchComponentMapSdkZoomLayerStatusComponentMapSdkDetailDialogDataMapFeatureFilterOptionMapFeatureParcelSearchSuggestionMapFeatureGeometryResponseMapFeatureLegendGroupMapFeatureLegendItemMapLegendResponseMapLegendGroupApiMapLegendEntryApiMapLegendStylingMapLegendMatchingMAP_FEATURE_DATA_SOURCEMAP_FEATURE_RUNTIME_CONFIGMAP_FEATURE_HTTP_CONFIGMAP_SDK_RENDERER
Everything else should be treated as implementation detail unless it is documented here.
Package Philosophy
The SDK is responsible for:
- backend data loading
- legend grouping and normalization
- zoom thresholds and layer visibility
- parcel search behavior
- popup data normalization
- theming variables
- common events and callbacks
The consumer app is responsible for:
- the surrounding page layout
- the host app chrome
- any custom map vendor if they do not want the full-page host
- local wrapper components
- app-specific styling overrides
Capability Matrix
Use this table to choose the right SDK mode for your app.
| Capability | Full host | Widgets only | Custom renderer | Headless |
|---|---|---|---|---|
| Province and municipality filters | Yes | Yes | Yes | No |
| Boundary rendering | Yes | Yes | Yes | No |
| Parcel rendering | Yes | Yes | Yes | No |
| Parcel search | Yes | Yes | Yes | No |
| Legend panel | Yes | Yes | Yes | No |
| Built-in Leaflet canvas | Yes | Yes | No | No |
| Custom map vendor | Optional | Optional | Yes | Yes |
| SDK event bus | Yes | Yes | Yes | Yes |
| SDK fetches backend data | Yes | Yes | Yes | Yes |
Typical usage:
- Full host: use when you want the SDK to behave like the warehouse map explorer.
- Widgets only: use when your app owns layout but wants the SDK filters, legend, and search.
- Custom renderer: use when your app already owns the map canvas but wants SDK data and behavior.
- Headless: use when your app wants data, events, and policy logic without any default UI.
Architectural Overview
The SDK sits between the consumer app and the map backend. In the default mode, the SDK owns data loading and rendering while the consumer owns the page shell.
flowchart LR
subgraph Consumer["Consumer App"]
A["Route / Page Shell"]
B["Theme Wrapper"]
C["Event Handlers"]
D["Optional Custom Renderer"]
end
subgraph SDK["Map SDK"]
E["MapSdkHostComponent"]
F["MapSdkHostShellComponent"]
G["Widgets\nFilters - Legend - Search - Status"]
H["Providers\nConfig - Adapter - Renderer"]
I["FeatureEventBus"]
end
subgraph Backend["Map Backend API"]
J["Provinces"]
K["Municipalities"]
L["Legends"]
M["Parcel Search"]
N["Geometry + Popup Data"]
end
A --> E
A --> G
B --> E
C --> I
D --> H
E --> H
G --> H
H --> Backend
Backend --> N
H --> I
Fishbone View
This fishbone-style diagram shows the main responsibility branches that make up the SDK experience.
flowchart LR
SDK["Map SDK"]
Consumer["Consumer Shell\nRoute - Layout - Theme"]
Data["Data Contract\nAPI base URL - adapter - runtime config"]
UI["UI Widgets\nFilters - Legend - Search - Status"]
Render["Rendering\nLeaflet host - custom renderer"]
Events["Events\noutputs - event bus - layer clicks"]
Policy["Policy\nzoom rules - visibility - gating"]
Docs["Docs + Deployment\nAPI - integration - theming"]
Consumer --- SDK
Data --- SDK
UI --- SDK
Render --- SDK
Events --- SDK
Policy --- SDK
Docs --- SDK
Configuration
MapFeatureZoomPolicy
Controls when each boundary or parcel family becomes visible.
export interface MapFeatureZoomPolicy {
provinceBoundaryZoom: number;
districtBoundaryZoom: number;
municipalityBoundaryZoom: number;
suburbBoundaryZoom: number;
parcelZoom: number;
labelZoomByKind: Record<MapLayerKind, number>;
}
Default values:
- province boundary:
7 - district boundary:
8 - municipality boundary:
9 - suburb boundary:
10 - parcel zoom:
12
MapFeatureConfig
This is the top-level host configuration.
export interface MapFeatureConfig {
apiBaseUrl: string;
includeInactive: boolean;
zoomPolicy: Partial<MapFeatureZoomPolicy>;
features: MapFeatureFeatureFlags;
ui: MapFeatureUiConfig;
search: MapFeatureSearchConfig;
legend: MapFeatureLegendConfig;
}
Key fields
apiBaseUrl- Base URL used by the SDK when it talks to the backend.
- Default:
/api - Common runtime examples:
- local:
http://localhost:8080/api - production:
https://api-warehouse.idti.dev/api
- local:
includeInactive- Whether inactive legend entries should be returned or hidden.
zoomPolicy- Partial override of the default zoom thresholds.
features- Feature gate block that turns major SDK behaviors on or off.
ui- Host labels, theming class, container ID, and mount mode.
search- Search prompt, debounce, and limit behavior.
legend- Legend toggles and default collapsed state.
MapFeatureFeatureFlags
These gates let the consumer decide which behaviors are active.
legendsearchprovinceFiltersmunicipalityFiltersselectedMunicipalityOverlayrefreshButtonboundariesparcels
MapFeatureUiConfig
export interface MapFeatureUiConfig {
title: string;
subtitle: string;
themeClass: string;
mapContainerId: string;
showStatusBanner: boolean;
showLoadingOverlay: boolean;
mountMode: MapFeatureMountMode;
}
Supported mountMode values:
full-pagewidgetsheadless
Default UI values:
- title:
Map Explorer - subtitle:
Browse boundaries, parcels, and live legend layers. - theme class:
map-sdk-theme - map container ID:
leaflet-map - mount mode:
full-page
MapFeatureSearchConfig
export interface MapFeatureSearchConfig {
placeholder: string;
minLength: number;
debounceMs: number;
resultLimit: number;
}
Default search values:
- placeholder:
Search parcels by LPI, SG number, or description - minimum length:
2 - debounce:
300ms - result limit:
8
MapFeatureLegendConfig
export interface MapFeatureLegendConfig {
allowVisibilityToggle: boolean;
allowColorEdit: boolean;
defaultCollapsed: boolean;
}
Default legend behavior:
- visibility toggle enabled
- color editing enabled
- legend expanded by default
Data Contracts
MapFeatureAdapter
MapFeatureAdapter is the main backend data contract. It is an alias for MapFeatureDataSource.
Use it when you want the SDK to fetch data from your API, but you need to replace the HTTP layer or adapt to your own backend.
Required method:
loadLegend(provinceId, includeInactive?, municipalityId?)
Optional methods:
loadProvinces()loadMunicipalities(provinceId)getParcelDetail(provinceId, parcelType, sourceId)getProvinceGeometry(provinceId)getMunicipalityGeometry(provinceId, municipalityId)searchParcels(provinceId, municipalityId, search, limit?)getParcelGeometryBySuggestion(provinceId, suggestion)getAdminBoundaryDetails(sourceKind, provinceId, sourceId?, displayName?, locationId?)
Common request intent:
| Method | Purpose |
|---|---|
loadLegend(...) |
Load visible legend groups and colors for the current scope |
loadProvinces() |
Populate province dropdowns |
loadMunicipalities(provinceId) |
Populate municipality dropdowns after a province is selected |
getProvinceGeometry(provinceId) |
Render or refresh the selected province overlay |
getMunicipalityGeometry(provinceId, municipalityId) |
Render or refresh the selected municipality overlay |
searchParcels(...) |
Return parcel suggestions for the search panel |
getParcelGeometryBySuggestion(...) |
Render the selected searched parcel geometry |
getParcelDetail(...) |
Load the popup record for a clicked parcel |
getAdminBoundaryDetails(...) |
Load the popup record for a clicked boundary or administrative feature |
Example adapter sketch:
export class AppMapAdapter implements MapFeatureAdapter {
loadLegend(provinceId: string, includeInactive = false, municipalityId?: string) {
return this.http.get<MapLegendResponse>(
`${this.baseUrl}/map/legend/${provinceId}`,
{ params: { includeInactive, municipalityId: municipalityId ?? '' } }
);
}
searchParcels(provinceId: string, municipalityId: string, search: string, limit = 8) {
return this.http.get<MapFeatureParcelSearchResponse>(
`${this.baseUrl}/map/parcels/search`,
{ params: { provinceId, municipalityId, q: search, limit } }
);
}
getParcelGeometryBySuggestion(provinceId: string, suggestion: MapFeatureParcelSearchSuggestion) {
return this.http.get<MapFeatureGeometryResponse>(
`${this.baseUrl}/map/parcels/${provinceId}/geometry`,
{ params: { lpi: suggestion.lpi, parcelType: suggestion.parcelType } }
);
}
}
MapFeatureAuthAdapter
export interface MapFeatureAuthAdapter {
getAccessToken?(): Promise<string | null> | string | null;
getUserId?(): string | null;
}
Use this when the SDK needs identity context for future auth-aware features or auditing.
MapFeaturePermissionAdapter
export interface MapFeaturePermissionAdapter {
canAccessFeature?(permission: string): boolean | Promise<boolean>;
}
Use this when the consumer app wants to let the SDK ask whether a feature is allowed.
MapFeatureRendererAdapter
Use this when the consumer wants the SDK to drive a custom map vendor instead of Leaflet.
Methods:
mount(container)setViewport(viewport)setZoomPolicy(policy)setLayerVisibility(layerId, visible)setLayerLabelsVisible(layerId, visible)setLayerStyle(layerId, style)renderLayer(layerId, geometry, descriptor?)clearLayer(layerId)clearAll()destroy()- optional
getZoomState()
This is the cleanest integration point for consumers who want to keep their own map canvas but still reuse the SDK data model.
MapFeatureApiClient
Minimal HTTP abstraction:
get<TResponse>(url)post<TResponse, TBody>(url, body)
MapFeatureProviderOptions
Options accepted by provideMapSdk(...).
export interface MapFeatureProviderOptions {
adapter?: MapFeatureAdapter | null;
rendererAdapter?: MapFeatureRendererAdapter | null;
runtimeConfig?: Partial<MapFeatureRuntimeConfig> | null;
httpConfig?: Partial<MapFeatureHttpConfig> | null;
}
MapFeatureAngularAdapter
Angular-specific adapter surface for auth and permission hooks:
getAccessToken?()getUserId?()canAccessFeature?(permission)
FeatureEventBus
Lightweight typed event channel for SDK-wide signaling.
export class FeatureEventBus {
readonly events$: Observable<FeatureSdkEvent>;
emit(event: FeatureSdkEvent): void;
}
Events
FeatureSdkEvent
Generic event envelope:
export interface FeatureSdkEvent<TPayload = unknown> {
type: string;
payload: TPayload;
}
FeatureSdkSelectionEvent
Selection-specific envelope:
export interface FeatureSdkSelectionEvent<TPayload = unknown> extends FeatureSdkEvent<TPayload> {
type: 'selection';
}
FeatureSdkLayerVisibilityEvent
Emitted when a layer is shown or hidden.
export interface FeatureSdkLayerVisibilityEvent {
layerId: string;
visible: boolean;
reason?: 'legend' | 'zoom' | 'selection' | 'refresh' | 'api' | 'consumer';
}
MapFeatureLayerClickEvent
Emitted when a boundary or parcel feature is clicked.
export interface MapFeatureLayerClickEvent {
layerId: string;
kind: string;
sourceId: string;
displayName: string;
featureId?: string | number | null;
properties: Record<string, unknown>;
latLng?: [number, number] | null;
}
Event Lifecycle
| User action | Main SDK events | Typical UI update |
|---|---|---|
| Select province | selection + visibility updates |
Refresh municipality list, reload legend, refresh overlays |
| Select municipality | selection + visibility updates |
Focus map, redraw selected boundary, reload parcel scope |
| Toggle a legend item | layerVisibility |
Show or hide the target boundary or parcel layer |
| Zoom the map | visibility update reason zoom |
Show or hide zoom-gated layers and labels |
| Search and select a parcel | selection + layer click |
Render parcel geometry and open popup |
| Click a boundary or parcel | layerClick |
Open popup with the normalized record data |
| Refresh map layers | visibility update reason refresh |
Reload current legend and visible geometry |
Angular Providers
provideMapSdk(...)
The top-level Angular helper for environment providers.
provideMapSdk({
runtimeConfig,
httpConfig,
adapter,
rendererAdapter
});
Use it when you want the SDK to be bootstrapped once at the application level.
provideMapFeatureRuntimeConfig(...)
Injects base URL and default legend endpoint values.
provideMapFeatureHttpConfig(...)
Injects the HTTP config override path.
provideMapFeatureAdapter(...)
Registers a custom backend adapter.
provideMapFeatureDataSource(...)
Registers a MapFeatureDataSource directly.
provideMapFeatureRendererAdapter(...)
Registers a custom renderer adapter for headless/custom-map usage.
Injection Tokens
MAP_FEATURE_DATA_SOURCEMAP_FEATURE_RUNTIME_CONFIGMAP_FEATURE_HTTP_CONFIGMAP_SDK_RENDERER
UI Components
MapSdkHostComponent
Selector: dw-map-sdk-host
This is the main full-page host that wraps the explorer experience.
Inputs:
configadapterincludeInactivezoomPolicythemeClassmapContainerIdshowLegendshowSearchdefaultProvinceId
Outputs:
provinceChangemunicipalityChangelayerVisibilityChangelayerClick
Use this component when the SDK should own:
- data loading
- map canvas
- legend rendering
- search rendering
- layer visibility rules
MapSdkHostShellComponent
Selector: dw-map-sdk-host-shell
This is the layout shell used by the host. It is useful when you want to compose around the SDK UI pieces but still keep the default explorer shell structure.
Inputs:
configadapterheaderTitleheaderSubtitlehostThemeClasshostMapContainerIdprovinceOptionsmunicipalityOptionsselectedProvinceIdselectedMunicipalityIdprovinceLoadingmunicipalityLoadingincludeInactivezoomlegendResponsemapLoadingmapLoadingLabelzoomPolicyshowLegendshowSearch
Outputs:
provinceChangemunicipalityChangezoomChangelayerVisibilityChangelayerClick
MapSdkFiltersComponent
Selector: dw-map-sdk-filters
Inputs:
provinceOptionsmunicipalityOptionsselectedProvinceIdselectedMunicipalityIdprovinceLoadingmunicipalityLoadingtitlesubtitle
Outputs:
provinceChangemunicipalityChange
MapSdkLegendPanelComponent
Selector: dw-map-sdk-legend-panel
Inputs:
titlesubtitlegroupsadminMode
Outputs:
legendVisibilityTogglelegendActiveTogglelegendColorChange
MapSdkParcelSearchComponent
Selector: dw-map-sdk-parcel-search
Inputs:
querysuggestionsloadingstatusplaceholderautocompleteOpenactiveSuggestionIndexisFocused
Outputs:
queryChangefocusblurkeydownclearsuggestionSelect
The suggestionSelect event returns:
{
suggestion: MapFeatureParcelSearchSuggestion;
event?: Event | MouseEvent | PointerEvent;
}
MapSdkZoomLayerStatusComponent
Selector: dw-map-sdk-zoom-layer-status
Inputs:
zoompolicysummary
MapSdkDetailDialogData
The detail dialog is currently represented as a simple data contract:
export interface MapSdkDetailDialogData {
title: string;
body: string;
}
Response and DTO Models
These models are part of the public data contract and are useful when the consumer wants to understand the shape of backend responses or typed widget data.
Filter and search models
MapFeatureFilterOption- used for province and municipality dropdowns
- fields:
id,name, optionalcode, optionalparentId MapFeatureParcelSearchSuggestion- represents a parcel suggestion row
- includes
lpi,sgNo,displayLabel,sourceSchema,sourceTable, and location fields MapFeatureParcelSearchResponse- contains
suggestionsand an optionalreason
Geometry models
MapFeatureGeometryResponse- wraps a geometry payload plus status metadata
- includes
layerAvailable,status,reason,minZoom, andfeatureCollection MapFeatureGeometryFeatureCollection- lightweight GeoJSON-style collection wrapper used by the SDK
Legend models
MapLegendResponse- the backend legend response shape
MapLegendGroupApi- backend legend group DTO
MapLegendEntryApi- backend legend entry DTO
MapLegendMatching- matching keys that determine when a legend entry applies
MapLegendStyling- backend styling rules for color, opacity, and icon
MapFeatureLegendGroup- normalized group used by the UI widgets
MapFeatureLegendItem- normalized legend item used by the UI widgets
Default Runtime Behavior
If a consumer does not override anything, the SDK will:
- load provinces and municipalities from its configured backend
- load legends from the configured backend
- show the full-page explorer host
- apply the default theme token set
- enforce zoom thresholds for boundaries and parcels
- keep parcel rendering gated behind the parcel zoom policy
Default Backend URLs
The SDK uses its own backend config and does not require the consumer app to fetch map payloads directly.
- local:
http://localhost:8080/api - production:
https://api-warehouse.idti.dev/api
Template Extension Points
The SDK currently does not expose native content projection slots such as custom <ng-template> inputs.
Instead, consumers extend the SDK by composing the exported Angular widgets in their own wrapper component.
Example:
<section class="my-map-page">
<header class="my-map-page__header">
<app-my-toolbar></app-my-toolbar>
</header>
<dw-map-sdk-filters
[provinceOptions]="provinceOptions"
[municipalityOptions]="municipalityOptions"
[selectedProvinceId]="selectedProvinceId"
[selectedMunicipalityId]="selectedMunicipalityId"
(provinceChange)="onProvinceChange($event)"
(municipalityChange)="onMunicipalityChange($event)"
/>
<dw-map-sdk-host
[config]="mapFeatureConfig"
[adapter]="mapAdapter"
[showLegend]="true"
[showSearch]="true"
(layerClick)="onLayerClick($event)"
/>
</section>
If you later want fully projected slots, treat that as a future SDK enhancement rather than a current API.
Theming
Consumers can override the SDK tokens through a wrapper class:
.smoc-map-theme {
--map-feature-surface: #ffffff;
--map-feature-surface-elevated: #f6f8fc;
--map-feature-border: rgba(15, 23, 42, 0.12);
--map-feature-text: #0f172a;
--map-feature-muted-text: #475569;
--map-feature-accent: #0f766e;
--map-feature-accent-strong: #115e59;
--map-feature-chip-surface: rgba(15, 118, 110, 0.12);
--map-feature-chip-text: #0f172a;
}
Build and Pack
From H:\workspace\data-warehouse\frontend:
npm run build:lib
npm pack
The package name is:
@agastyadreamspty/map-sdk
Consumer Guidance
Use the full host if you want the SDK to own the whole map explorer experience.
Use the widgets and adapters if you want:
- only filters
- only legend rendering
- only parcel search
- only layer status
- a custom map vendor
- custom event handling
- app-specific layout and chrome
The SDK should remain the source of truth for:
- API fetch behavior
- legend gating
- search gating
- zoom thresholds
- popup data normalization
- layer visibility decisions
- theming tokens
Troubleshooting
Layers do not appear after zooming
- Confirm the active zoom policy still allows the layer to render.
- Confirm the selected province and municipality are valid for the current API response.
- Confirm the consumer is not hiding the layer via legend visibility.
Legend or search appears behind the map
- Put the SDK host and overlay containers in a stacking context above the canvas.
- Make sure the consumer wrapper does not reset the SDK z-index tokens.
- Keep search and legend panels outside any container that clips overflow.
Search suggestions appear but clicking them does nothing
- Confirm the consumer is using the SDK click handler, not a mouse-down override.
- Confirm the adapter implements
getParcelGeometryBySuggestion(...). - Confirm the parcel geometry endpoint returns a non-empty feature collection.
Popup content renders with the wrong province or record
- Confirm the active province and municipality are passed into the adapter request.
- Confirm the backend response matches the current filter scope.
- Confirm the consumer is not reusing cached popup data from a previous selection.