import type { GeoJsonBbox, GeoJsonFeature, GeoJsonFeatureCollection, GeoJsonGeometry, GeoJsonPosition } from "@trackunit/geo-json-utils"; import type { AdaptiveMarkerResolution, ClusterConfig, ClusterRenderConfig, RenderConfig, RouteStyle, ShapeInteractiveMode, ShapeStyle } from "@trackunit/react-map-adapter-shared"; import type { CategoryKey, ControlConfig } from "../controls/types"; import type { ShapeDecoration } from "./useShapes/shapeDecorations"; import type { ShapesUnderCursorHit } from "./useShapes/shapeFillTiling"; import type { ShapeLabelResolutionContext } from "./useShapes/shapeLabelResolution"; import type { ResolveShapeStackOrder } from "./useShapes/shapeStackOrder"; export type { ShapesUnderCursorHit }; export type { AdaptiveMarkerResolution, AdaptiveRenderConfig, AdaptiveRenderState, AdaptiveResolutionContext, CircleSymbolDescriptor, ClientClusterConfig, ClusterConfig, ClusterDomRenderConfig, ClusterInfo, ClusterRenderConfig, ClusterRenderState, ClusterSymbolRenderConfig, ClusterSymbolStyle, CommonRenderState, DomPortalStackingInput, DomPortalStackingResolver, DomPortalStackingResult, DomRenderConfig, DomRenderState, GeoJsonFeature, GeoJsonFeatureCollection, GeoJsonGeometry, MarkerAnchor, MarkerDomPortalStackGeometry, MarkerDomPortalStackPhase, PixelOffset, RenderConfig, RenderMedium, ResolutionContext, RouteStyle, ServerClusterConfig, ShapeInteractiveMode, ShapeStyle, ShapeStyleOverrides, SymbolDescriptor, SymbolRenderConfig, SymbolRenderState, } from "@trackunit/react-map-adapter-shared"; export { computeMarkerDomPortalZIndex } from "@trackunit/react-map-adapter-shared"; /** * Controls whether a layer's bounds are included in fit-to-content operations. * * - `"all"` — participates in initial auto-fit, manual `fitNow()`, and future always-fit mode. * - `"initial"` — only participates in the initial auto-fit on first load. * - `"none"` — never participates in any fit operation. */ export type FitParticipation = "all" | "initial" | "none"; /** * Opt-in fill-tiling configuration for a shape layer (ADR-0021). * * When set, overlapping polygon fills are clipped so every pixel is painted by * exactly one feature (no compounded translucency). Strokes always stay full. * Ownership of shared regions is decided by `resolveStackOrder`, biased toward * the feature the cursor is most inside. * * Orthogonal to `interactive`: this controls the *visual* tiling only and never * implies hit-testing behaviour. */ export type ShapeOverlapConfig = Readonly<{ mode: "tile"; /** Swappable ranking policy. Defaults to `defaultShapeStackOrder`. */ resolveStackOrder?: ResolveShapeStackOrder; }>; /** * Metadata common to all layer types. * Every layer hook returns an object extending this shape. */ export type LayerMeta = Readonly<{ /** Unique identifier for this layer */ id: string; /** Human-readable name */ name: string; /** Whether this layer fetches its own data */ loadsData: boolean; /** Whether data is currently loading */ loading: boolean; /** Resolves when initial data is available (or immediately if loadsData is false) */ ready: Promise; /** * Lazily compute bounding box of all features. * Cached internally until source data reference changes. */ getBounds: () => GeoJsonBbox | null; /** Summary counts -- always cheap (derived from .length). Semantics depend on layer type. */ counts: Readonly>; /** * Controls this layer wants to contribute to the map UI, keyed by semantic area. * Each key maps to an array of controls for that area (navigation, settings, tools, statistics). * Omit a key (or provide an empty array) to contribute nothing to that area. */ controls: Readonly>>>; /** * Controls whether this layer's bounds are included in fit-to-content operations. * Default: `"all"`. */ fitParticipation: FitParticipation; }>; /** * Marker layer handle -- returned by `useMarkers`. * Contains all data and configuration needed to render markers and clusters. */ export interface MarkerLayerHandle extends LayerMeta { readonly layerType: "markers"; /** GeoJSON points for all positioned markers */ readonly features: GeoJsonFeatureCollection; /** Marker render configuration */ readonly markerRender: RenderConfig; /** Cluster configuration, if clustering is enabled */ readonly clusterConfig: ClusterConfig | null; /** Cluster render configuration, if clustering is enabled */ readonly clusterRender: ClusterRenderConfig | null; /** Server-side cluster features (GeoJSON points), if using server-side clustering */ readonly clusterFeatures: GeoJsonFeatureCollection | null; /** * Per-cluster `memberItems` cross-referenced from the hook's `data` array via * `getMarkerIds`. Keyed by cluster ID. `null` value when any member ID is * missing from the data index. Null map when server clustering is not in * use. Consumed by `` to build `ClusterRenderState` for DOM * cluster portals. */ readonly clusterMemberItems: ReadonlyMap | null> | null; /** * Filled by `` when `markerRender.mode === "adaptive"` — per-feature * resolved display modes for the map adapter (plan §8). */ readonly adaptiveResolution?: AdaptiveMarkerResolution; } /** * Shape layer handle -- returned by `useShapes`. * Contains GeoJSON features (polygons, multipolygons, lines) with styling. */ export interface ShapeLayerHandle extends LayerMeta { readonly layerType: "shapes"; /** GeoJSON features for all shapes */ readonly features: GeoJsonFeatureCollection; /** Base shape style (static value). When per-feature styling is used, this is `{}`. */ readonly style: ShapeStyle; /** Resolves shape style for static and viewport-aware rendering passes. */ readonly resolveStyle: (feature: GeoJsonFeature, ctx?: ShapeLabelResolutionContext) => ShapeStyle; /** Per-feature resolved styles, keyed by `String(feature.id)`. */ readonly featureStyles?: ReadonlyMap; /** Which parts of the shape respond to interaction */ readonly interactive: ShapeInteractiveMode; /** * Fill-tiling configuration. When present (`mode: "tile"`), overlapping fills * are clipped and ranked. Absent = legacy compounding fills. Orthogonal to * `interactive`. */ readonly overlap?: ShapeOverlapConfig; /** * Per-feature clipped fill geometry keyed by `String(feature.id)`, filled by * `` for layers with `overlap.mode === "tile"`. Strokes keep the full * geometry; only the fill consumes this clipped geometry. */ readonly featureFillGeometries?: ReadonlyMap; /** * Per-feature z-index override for fill rendering, filled by `` for * layers with `overlap.mode === "tile"` (ADR-0021 Path B). Higher value = visually * on top. Only features in an overlap group receive an entry; the adapter uses * the feature's default z-index for absent entries. The hook keeps resting values * stable across hovers; only the promoted feature's value is boosted on hover. */ readonly featureZIndexOverrides?: ReadonlyMap; /** * Returns all decorations for a feature, including auto-generated ones * (e.g. multi-geometry "+" badges). Merging of custom and auto decorations * is handled by `useShapes` at construction time. */ readonly getDecorations: (feature: GeoJsonFeature) => ReadonlyArray; /** * Geometric hit-test at the given map position. Returns all features whose * fill or stroke region contains `position`, with fill/stroke flags and * distance-to-boundary for tiebreaks. * * Injected by `` with live viewport context (zoom + tile-size). * `undefined` when the handle is used outside `` (e.g. in unit tests). */ readonly queryShapesAt?: (position: GeoJsonPosition) => ReadonlyArray; /** * Called by `` after the pointer settle debounce fires and the * stroke hit-test completes for this layer. Receives all fill + stroke hits * (sorted by feature id). Wired from `UseShapesOptions.onShapesUnderCursor`; * always present but is a no-op when that option is not provided. * * Only fires for layers with `overlap: { mode: "tile" }`. */ readonly onShapesUnderCursor?: (hits: ReadonlyArray, ctx: Readonly<{ position: GeoJsonPosition; }>) => void; } /** * Route layer handle -- returned by `useRoute`. * Contains a polyline with waypoints and styling. */ export interface RouteLayerHandle extends LayerMeta { readonly layerType: "route"; /** GeoJSON LineString feature for the route */ readonly features: GeoJsonFeatureCollection; /** Waypoint positions (original input) */ readonly waypoints: ReadonlyArray; /** Route style */ readonly style: RouteStyle; /** Whether route is interactive (clickable/hoverable). Default: false */ readonly interactive: boolean; } /** * Image overlay handle -- returned by `useImageOverlay`. * Contains a georeferenced image to display on the map. */ export interface ImageOverlayHandle extends LayerMeta { readonly layerType: "image-overlay"; /** URL of the image to display */ readonly url: string; /** Geographic bounds the image covers */ readonly imageBounds: GeoJsonBbox; /** Opacity from 0 to 1 */ readonly opacity: number; } /** * Discriminated union of all layer handle types. * Discriminated on `layerType`. */ export type LayerHandle = MarkerLayerHandle | ShapeLayerHandle | RouteLayerHandle | ImageOverlayHandle;