interface MapRuntime {
init(container: HTMLDivElement): Promise<void>;
destroy(): void;
requestRender(): void;
getViewer(): Viewer;
}Port interface for store operations, injected into LayerService to decouple it from the Zustand store.
interface MapStorePort {
showLayer(layerId: string): void;
hideLayer(layerId: string): void;
setLayerLoading(layerId: string, loading: boolean): void;
}type LayerType = "imagery" | "terrain" | "geojson" | "3dtiles" | "czml";
type LayerStatus = "idle" | "loading" | "ready" | "error";
type LayerErrorCode = "NETWORK" | "PARSE" | "TIMEOUT" | "ABORTED";
interface LayerSpec<T = unknown> {
id: string;
type: LayerType;
visible: boolean;
status: LayerStatus;
requestId?: string;
attempts?: number;
options: T;
}
interface LayerController {
mount(spec: LayerSpec): Promise<void>;
update(patch: Partial<LayerSpec>): void;
flyTo(duration?: number): Promise<void>;
retry?(maxAttempts?: number): Promise<void>;
cancel?(): void;
unmount(): void;
}/** Typed payload for pick/hover interactions */
interface PickPayload {
id: string;
lng: number;
lat: number;
}
/** UI descriptor returned by plugins for toolbar/panel integration */
interface PluginUIDescriptor {
toolbarItems?: ToolbarItem[];
panelId?: string;
}
/** Context passed to plugins during activation */
interface PluginContext {
runtime: MapRuntime;
emit: (e: MapEvent) => void;
store: MapStorePort;
broker: InteractionBroker;
}
/** Lifecycle interface for map plugins */
interface MapPlugin {
id: string;
priority: number;
exclusiveGroup?: "draw" | "measure" | "none";
activate(ctx: PluginContext): void;
deactivate(): void;
dispose(): void;
getUIDescriptor?(): PluginUIDescriptor;
configure?(config: Record<string, unknown>): void;
}
/** Consumer that receives pick events from InteractionBroker */
interface PickConsumer {
pluginId: string;
priority: number;
/** Handle a pick event. Return true if consumed (stops propagation). */
onPick(payload: PickPayload): boolean;
}
/** Broker that dispatches pick events to consumers by priority */
interface InteractionBroker {
register(consumer: PickConsumer): void;
unregister(pluginId: string): void;
dispatch(payload: PickPayload): void;
}type MapEvent =
| { type: "pick.hit"; payload: { id: string; lng: number; lat: number } }
| { type: "pick.miss"; payload: { lng: number; lat: number } }
| { type: "track.finished"; payload: { trackId: string } }
| { type: "track.progress"; payload: { trackId: string; ts: number } }
| { type: "layer.error"; payload: { layerId: string; message: string; code?: LayerErrorCode } }
| { type: "layer.ready"; payload: { layerId: string } }
| { type: "layer.visibilityChanged"; payload: { layerId: string; visible: boolean } }
| { type: "layer.reordered"; payload: { layerIds: string[] } }
| { type: "layer.loading"; payload: { layerId: string } }
| { type: "camera.moveEnd"; payload: Record<string, never> }
| { type: "camera.zoomChange"; payload: { level: number } }
| { type: "extent.changed"; payload: { west: number; south: number; east: number; north: number } }
| { type: "entity.hover"; payload: { id: string; lng: number; lat: number } | null };Abstract interfaces for core services, enabling alternative implementations and testing.
interface ILayerService {
registerFactory(type: LayerType, factory: (runtime: any) => LayerController): void;
addLayer(spec: LayerSpec): Promise<void>;
removeLayer(layerId: string): void;
retryLayer(layerId: string, maxAttempts?: number): Promise<void>;
cancelLayer(layerId: string): void;
flyToLayer(layerId: string, duration?: number): Promise<void>;
updateLayer(layerId: string, patch: Partial<LayerSpec>): void;
reorderLayer(layerId: string, newIndex: number): void;
getLayerOrder(): string[];
getController(layerId: string): LayerController | undefined;
dispose(): void;
}
interface ICameraService {
flyTo(target: any, policy?: "enqueue" | "replace" | "drop"): Promise<void>;
cancelCurrent(reason?: string): void;
clearQueue(): void;
dispose(): void;
}
interface ITrackService {
load(trackId: string, points: any[]): void;
play(trackId: string): void;
pause(trackId: string): void;
seek(trackId: string, ts: number): void;
setSpeed(multiplier: number): void;
clear(trackId: string): void;
dispose(): void;
}
type EmitFn = (event: MapEvent) => void;Constructor now accepts a MapStorePort for decoupled store access.
class LayerService {
constructor(runtime: MapRuntime, emit: EmitFn, store: MapStorePort);
registerFactory(type: LayerType, factory: (rt: MapRuntime) => LayerController): void;
addLayer(spec: LayerSpec): Promise<void>;
removeLayer(layerId: string): void;
retryLayer(layerId: string, maxAttempts?: number): Promise<void>;
cancelLayer(layerId: string): void;
flyToLayer(layerId: string, duration?: number): Promise<void>;
updateLayer(layerId: string, patch: Partial<LayerSpec>): void;
reorderLayer(layerId: string, newIndex: number): void;
getLayerOrder(): string[];
getController(layerId: string): LayerController | undefined;
dispose(): void;
}Manages a flyTo queue with three policies. The queue is capped at MAX_QUEUE_SIZE = 50 entries; excess enqueue requests are rejected.
type FlyToPolicy = "enqueue" | "replace" | "drop";
class CameraServiceImpl {
constructor(runtime: MapRuntime);
flyTo(target: FlyToParams, policy?: FlyToPolicy): Promise<void>;
cancelCurrent(reason?: string): void;
clearQueue(): void;
dispose(): void;
}class PickService {
start(): void; // Begin listening for screen-space events
dispose(): void; // Clean up all handlers
}interface TrackPoint { lng: number; lat: number; alt?: number; ts: number; }
class TrackServiceImpl {
load(trackId: string, points: TrackPoint[]): void;
play(trackId: string): void;
pause(trackId: string): void;
seek(trackId: string, ts: number): void;
setSpeed(multiplier: number): void;
clear(trackId: string): void;
dispose(): void;
}Controls scene view mode and atmosphere/skybox visibility.
type SceneViewMode = "2d" | "3d" | "columbus";
interface ISceneService {
setMode(mode: SceneViewMode): void;
getMode(): SceneViewMode;
enableSkyBox(enabled: boolean): void;
enableAtmosphere(enabled: boolean): void;
}
class SceneServiceImpl implements ISceneService {
constructor(runtime: MapRuntime);
}Captures screenshots and exports data source entities as GeoJSON.
interface IExportService {
captureScreenshot(options?: { width?: number; height?: number }): Promise<string>;
exportAsGeoJson(layerId: string): object | null;
}
class ExportServiceImpl implements IExportService {
constructor(runtime: MapRuntime);
}Enables/disables entity clustering on Cesium data sources.
interface ClusterOptions {
pixelRange?: number;
minimumClusterSize?: number;
}
interface IClusterService {
enableClustering(dataSourceId: string, options?: ClusterOptions): void;
disableClustering(dataSourceId: string): void;
}
class ClusterServiceImpl implements IClusterService {
constructor(runtime: MapRuntime);
}Geocoding interface with a Tianditu adapter implementation.
interface SearchResult {
name: string;
lng: number;
lat: number;
address?: string;
}
interface ISearchService {
geocode(address: string): Promise<SearchResult[]>;
reverseGeocode(lng: number, lat: number): Promise<SearchResult | null>;
}
// Adapter: TiandituGeocoder implements ISearchService
class TiandituGeocoder implements ISearchService {
constructor(token?: string);
}type HeightRef = "ELLIPSOID" | "MSL";
class DefaultCoordinateTransformer {
wgs84ToCartesian(lng: number, lat: number, alt?: number, ref?: HeightRef): Cartesian3;
cartesianToWgs84(pos: Cartesian3, ref?: HeightRef): { lng: number; lat: number; alt: number };
}interface MapStoreState {
activeEntityId?: string;
visibleLayerIds: string[];
loadingLayerIds: string[];
selectedTrackId?: string;
toolMode: "none" | "measure" | "draw";
cameraBookmarkId?: string;
}
// Actions
setActiveEntity(id: string | undefined): void;
showLayer(layerId: string): void;
hideLayer(layerId: string): void;
toggleLayer(layerId: string): void;
setLayerLoading(layerId: string, loading: boolean): void;
setSelectedTrack(trackId: string | undefined): void;
setToolMode(mode: ToolMode): void;
setCameraBookmark(id: string | undefined): void;
reset(): void;class DisposeBag {
add(disposer: () => void): void;
addEventHandler(target: EventTarget, event: string, handler: EventListener, options?: AddEventListenerOptions): void;
addCesiumRemoval(removalFn: () => void): void;
dispose(): void; // LIFO order
}class MapEventBus {
on<T extends MapEventType>(type: T, handler: (payload: MapEventPayload<T>) => void): () => void;
onAny(handler: (event: MapEvent) => void): () => void;
emit(event: MapEvent): void;
clear(type?: MapEventType | "*"): void;
}interface Logger {
debug(msg: string, data?: unknown): void;
warn(msg: string, data?: unknown): void;
error(msg: string, error: unknown, context?: unknown): void;
}
// Built-in implementations
const consoleLogger: Logger; // Logs with "[MapLib]" prefix
const noopLogger: Logger; // Silent — all methods are no-ops