/** * XY snapping (spec: issue #34 Part A). Two-stage, deliberately NOT a * spatial index: deck.gl's own hover/click pick already tells us WHICH * feature is under the cursor (stage 1 — free, runs every frame regardless * of snapping) — this module refines that ONE feature's own geometry to * the nearest vertex/edge/midpoint within a pixel tolerance (stage 2, CPU, * only when snapping is on). OnlyMapJS never calls `pickObject` itself for * this; the resolver runs on the SAME `PickingInfo` runtime-core.ts's * existing `onHover`/`onClick` callbacks already receive every frame, at * the exact point they currently discard everything but a bare * `[lng, lat]` (see `coordOf`'s call sites). * * Agent vocabulary follows ArcGIS (the lingua franca every surveyor * already knows): Vertex, Edge, Midpoint here; Endpoint/Intersection/ * Point-cloud/Snap-to-sketch are documented follow-ups (see the module's * own README section on issue #34), Tangent skipped outright (CAD * heritage, no geospatial use). "Closest wins" is NOT the tie-break when * two agents both have a candidate within tolerance — Vertex beats * Midpoint beats Edge (ArcGIS's own conflict order: a midpoint sitting * near an edge should snap to the more specific point, not the general * one), ties WITHIN one agent broken by distance. */ export type SnapAgent = "vertex" | "edge" | "midpoint"; export declare const SNAP_AGENTS: readonly SnapAgent[]; export declare const DEFAULT_SNAP_TOLERANCE_PX = 12; export interface SnapConfig { agents: ReadonlySet; tolerancePx: number; } export interface SnapResult { /** The snapped [lng, lat] — interpolated for edge/midpoint, exact for vertex. */ position: [number, number]; agent: SnapAgent; /** The snapped point's elevation in meters, when the matched geometry carried one — for ANCHORING UI at the visible point (the snap tip); the position contract itself stays XY. */ elevation?: number; } /** Minimal viewport contract this module needs — matches deck.gl's real `Viewport.project`. */ export interface SnapViewport { project(coordinate: number[]): number[]; } interface GeoJsonGeometryLike { type: string; coordinates: unknown; } /** * `snap="vertex edge midpoint"` / `snap-tolerance="12"` on `` → * `SnapConfig`, or `null` when `snap` is absent/empty (the common case, * must stay free — no pickingRadius change, no per-pick resolver call). * Unrecognized tokens (a typo, or a future agent not implemented yet) are * silently dropped rather than rejecting the whole attribute — validation.ts's * `validateSnap` carries the loud version of that same check. */ export declare function parseSnapAttrs(getAttr: (name: string) => string | null): SnapConfig | null; /** * The geometry a picked object can be snapped against. GeoJSON-shaped * objects (GeoJsonLayer, MVT, draw features) carry it directly — but MOST * OnlyMapJS data is flat rows (CSV/JSON + `get-position` accessors), whose * picked object is just the row, with no `.geometry` at all: snapping * silently found nothing on them (reported: "doesn't detect the vertices in * most of my data"). For those, the layer's own resolved accessors ARE the * geometry — evaluate them against the picked row exactly the way the * layer's shader binding did: `get-polygon` → Polygon, `get-path` → * LineString, `get-source-position`+`get-target-position` → a two-point * LineString, `get-position` → Point. * * Guarded to geographic layers: a layer in an offset/cartesian coordinate * system (a BIM mesh sublayer, a local-frame point cloud) yields raw local * meters from these accessors, not lng/lat — callers handle those through * their own conversion branches (see resolveMapPoint's BIM-edges path). */ export declare function geometryFromPick(layerProps: Record, object: unknown): GeoJsonGeometryLike | null; /** * Resolves a hover/click's cursor against ONE already-picked feature's * geometry. `cursorPx`/`viewport` project every candidate to screen for * pixel-accurate proximity; the winning MAP-space position is derived by * interpolating the geometry's own [lng, lat] vertices with the SAME * fraction found in screen space (an approximation for edge/midpoint on a * long, steeply-projected segment, but exact for the vertex agent, and * consistent with this codebase's own documented sphere-vs-ellipsoid, * screen-space-drag-math tradeoffs elsewhere — e.g. geodesy.ts, the * clip-box gizmo). Returns `null` when nothing is within tolerance, or the * geometry type isn't one `extractRings` understands (e.g. a raw * meter-offsets row, which this module does not attempt to project itself * — see this file's own header comment on BIM/PathLayer content being a * documented follow-up). */ export declare function resolveSnap(geometry: GeoJsonGeometryLike | null | undefined, cursorPx: readonly number[], viewport: SnapViewport, config: SnapConfig): SnapResult | null; export {};