/** * CityJSON / CityJSONSeq ingestion (spec: "CityJSON ingestion"; issue #25). * * A LAZY chunk — reached only through `CITYJSON_FORMAT`'s dynamic import in * data-layer.ts, so neither this decoder nor proj4 weighs on the eager * bundle (the src/raster.ts precedent). proj4 itself is imported lazily * AGAIN inside `resolveCrs`, so a file already in lon/lat never pays for it. * * Two output shapes share one geometry computation (`computeCityObjectGeometry`): * * - DEFAULT — one GeoJSON `Feature` per CityObject: a 2D footprint polygon * plus DERIVED height properties, rendered through GeoJsonLayer's * `extruded` + `get-elevation`. Lit, pickable, GPU-filterable, * terrain-aware — and, because extrusion is a flat-topped prism, it * cannot draw the actual shape of a pitched/hipped LoD2.2 roof no matter * how accurate the derived height is. * - `?om-surfaces=1` — one flat row PER FACE (wall, roof, ground), each * carrying its real 3D ring coordinates (real per-vertex height, not a * uniform extrusion) for `SolidPolygonLayer` with `extruded="false"` and * `full3d`. This draws the true reconstructed shape. The tradeoff: deck's * solid-polygon vertex shader computes lighting only inside its * `if (solidPolygon.extruded)` branch (verified against * solid-polygon-layer-vertex-main.glsl.js), so this path is unavoidably * unlit — flat per-face fill color, no shading from surface orientation. * `_full3d` is what makes near-vertical wall polygons triangulate at all: * without it earcut works in the flat xy plane, where a wall's projected * area is ~zero and the face silently vanishes. * * Semantics are not discarded in either mode: they drive which vertices * count as roof (so the derived heights are real roof heights, not * bounding-box heights) and survive as a `surface_type` per-face property in * surfaces mode. */ /** [x, y, z] in the source CRS — decompressed, not yet reprojected. */ type Vec3 = [number, number, number]; /** A face is a list of rings; ring[0] is the outer boundary, the rest are holes. */ type Face = number[][]; interface CityJsonTransform { scale?: number[]; translate?: number[]; } interface CityJsonSemantics { surfaces?: { type?: string; }[]; values?: unknown; } interface CityJsonGeometry { type?: string; lod?: string | number; boundaries?: unknown; semantics?: CityJsonSemantics; } interface CityObject { type?: string; attributes?: Record; geometry?: CityJsonGeometry[]; parents?: string[]; children?: string[]; } export declare function decompressVertices(vertices: number[][] | undefined, transform: CityJsonTransform | undefined): Vec3[]; /** * Geometry → flat face list. Surface-less geometry types (points, lines) and * anything unrecognized yield no faces, so the CityObject is skipped rather * than emitted with a broken footprint. */ export declare function flattenToFaces(geometry: CityJsonGeometry): Face[]; /** One semantic surface type per face (`"RoofSurface"`, …), aligned by index. */ export declare function faceSemantics(geometry: CityJsonGeometry, faceCount: number): (string | undefined)[]; /** * Highest-LoD geometry that actually has surfaces. Higher LoD wins even though * the output is an extrusion: an LoD2 solid carries real roof vertices and * semantics, so its derived heights beat LoD1's single stated height. * `?om-lod=` pins a specific one (see `parseCityJsonDocument`). */ export declare function chooseGeometry(geometries: CityJsonGeometry[] | undefined, lodPin?: number): CityJsonGeometry | undefined; /** `"https://www.opengis.net/def/crs/EPSG/0/7415"` / `"urn:ogc:def:crs:EPSG::7415"` / `"EPSG:7415"` → 7415. */ export declare function parseEpsgCode(referenceSystem: string | undefined): number | undefined; /** Source [x, y] → WGS84 [lon, lat]. */ export interface CrsAdapter { toLngLat(x: number, y: number): [number, number]; /** * True when source coordinates are already metres (a projected grid), so * horizontal areas can be measured on them directly. Geographic sources are * in degrees and need a local metric frame first — see `buildFeature`. */ projected: boolean; } /** * Build the reprojection adapter for a document. An unsupported EPSG code * throws rather than silently misplacing a whole city — the failure surfaces * through the Data Layer's own `Failed to fetch data="…"` path, the same as * any other format error. */ export declare function resolveCrs(referenceSystem: string | undefined, url: string): Promise; /** Derived property names. Documented as reserved — they win over same-named source attributes. */ export interface DerivedProperties { cityobject_id: string; cityobject_type: string; parent_id?: string; lod?: string; ground_height: number; roof_height: number; eaves_height: number; ridge_height: number; roof_area: number; surface_count: number; } export declare function defaultFillColor(surfaceType: string | undefined, cityObjectType: string | undefined): string; /** * CityObjects → features (default) or per-face surface rows (`mode: * "surfaces"`, see `parseSurfacesFlag`). Geometry usually hangs off child * objects (`BuildingPart`, `BridgePart`), so a parent's attributes are * inherited by its children — the child's own values win, and `parent_id` * keeps the hierarchy walkable for selection. */ export declare function decodeCityObjects(cityObjects: Record | undefined, vertices: Vec3[], crs: CrsAdapter, lodPin: number | undefined, mode?: "footprint" | "surfaces"): unknown[]; /** `?om-lod=1.2` → 1.2. The DataFormat contract sees only the URL, so this is the option channel. */ export declare function parseLodPin(url: string): number | undefined; /** * `?om-surfaces=1` → per-face `SolidPolygonLayer` rows instead of the default * footprint-extrusion `GeoJsonLayer` Features. Same option-channel idiom as * `om-lod` — and the same URL-keyed cache benefit: a plain URL and its * `?om-surfaces=1` counterpart parse and cache independently, so one manifest * can point a `GeoJsonLayer` at one and a `SolidPolygonLayer` at the other. */ export declare function parseSurfacesFlag(url: string): boolean; /** A whole `.city.json` document. One JSON parse — a single JSON value has no incremental path. */ export declare function parseCityJsonDocument(res: Response, url: string): Promise; /** * Streaming `.city.jsonl`. `push` hands the Data Layer a fresh array reference * every batch so deck.gl's shallow `data` diff fires and the city fills in as * it downloads; the returned array is the complete set. */ export declare function parseCityJsonSeq(res: Response, url: string, push: (features: unknown[]) => void): Promise; /** Test hook — resets the once-per-page missing-CRS warning. */ export declare function resetCityJsonWarningsForTests(): void; export {};