UNPKG

@maplat/transform

Version:

A JavaScript library that performs coordinate transformation between two plane coordinate systems using transformation definitions generated by Maplat.

214 lines (200 loc) 5.95 kB
import { featureCollection, point } from "@turf/turf"; import type { Position } from "geojson"; import { normalizeEdges } from "./edgeutils.ts"; import { indexesToTri, normalizeNodeKey } from "./triangulation.ts"; import type { PropertyTriKey } from "./geometry.ts"; import type { BiDirectionKey, Compiled, CompiledLegacy, LegacyStatePayload, ModernStatePayload, PointSet, StrictStatus, TinsBD, VerticesParamsBD, WeightBufferBD } from "./types.ts"; import type { WeightBuffer } from "./geometry.ts"; export const FORMAT_VERSION = 2.00703; export const FORMAT_VERSION_V3 = 3; /** * Type guard for discriminating modern compiled payloads. */ export function isModernCompiled( compiled: Compiled | CompiledLegacy ): compiled is Compiled { return Boolean( compiled.version !== undefined || (!(compiled as CompiledLegacy).tins && compiled.points && compiled.tins_points) ); } /** * Restore the in-memory state produced by modern compiled payloads. */ export function restoreModernState(compiled: Compiled): ModernStatePayload { return { points: compiled.points, pointsWeightBuffer: normalizeWeightBuffer(compiled), strictStatus: deriveStrictStatus(compiled), verticesParams: buildVerticesParams(compiled), centroid: buildCentroid(compiled), edges: normalizeEdges(compiled.edges || []), edgeNodes: compiled.edgeNodes || [], tins: buildTins(compiled), kinks: buildKinks(compiled.kinks_points), yaxisMode: compiled.yaxisMode ?? "invert", strictMode: compiled.strictMode ?? "auto", vertexMode: compiled.vertexMode, bounds: compiled.bounds, boundsPolygon: compiled.boundsPolygon, wh: compiled.wh, xy: compiled.xy ?? [0, 0] }; } /** * Restore the in-memory state produced by the legacy payloads. */ export function restoreLegacyState( rawCompiled: CompiledLegacy ): LegacyStatePayload { const normalized = normalizeLegacyStructure(rawCompiled); const tins = normalized.tins!; return { compiled: normalized, tins, points: rebuildLegacyPoints(tins), strictStatus: normalized.strict_status, pointsWeightBuffer: normalized.weight_buffer, verticesParams: normalized.vertices_params as VerticesParamsBD, centroid: normalized.centroid, kinks: normalized.kinks }; } function normalizeWeightBuffer(compiled: Compiled): WeightBufferBD { if (!compiled.version || compiled.version < FORMAT_VERSION) { return (["forw", "bakw"] as BiDirectionKey[]).reduce((bd, forb) => { const base = compiled.weight_buffer[forb]; if (base) { bd[forb] = Object.keys(base).reduce((buffer, key) => { const normKey = normalizeNodeKey(key); buffer[normKey] = base[key]; return buffer; }, {} as WeightBuffer); } return bd; }, {} as WeightBufferBD); } return compiled.weight_buffer; } function deriveStrictStatus(compiled: Compiled): StrictStatus { if (compiled.strict_status) return compiled.strict_status; if (compiled.kinks_points) return "strict_error"; if (compiled.tins_points.length === 2) return "loose"; return "strict"; } function buildVerticesParams(compiled: Compiled): VerticesParamsBD { const params: VerticesParamsBD = { forw: [compiled.vertices_params[0]], bakw: [compiled.vertices_params[1]] }; params.forw![1] = buildVertexTins(compiled, false); params.bakw![1] = buildVertexTins(compiled, true); return params; } function buildVertexTins(compiled: Compiled, bakw: boolean) { const N = compiled.vertices_points.length; return Array.from({ length: N }, (_, idx) => { const idxNxt = (idx + 1) % N; const tri = indexesToTri( ["c", `b${idx}`, `b${idxNxt}`], compiled.points, compiled.edgeNodes || [], compiled.centroid_point, compiled.vertices_points, bakw, FORMAT_VERSION ); return featureCollection([tri]); }); } function buildCentroid(compiled: Compiled) { return { forw: point(compiled.centroid_point[0], { target: { geom: compiled.centroid_point[1], index: "c" } }), bakw: point(compiled.centroid_point[1], { target: { geom: compiled.centroid_point[0], index: "c" } }) }; } function buildTins(compiled: Compiled) { const bakwIndex = compiled.tins_points.length === 1 ? 0 : 1; return { forw: featureCollection( compiled.tins_points[0].map(idxes => indexesToTri( idxes, compiled.points, compiled.edgeNodes || [], compiled.centroid_point, compiled.vertices_points, false, compiled.version ) ) ), bakw: featureCollection( compiled.tins_points[bakwIndex].map(idxes => indexesToTri( idxes, compiled.points, compiled.edgeNodes || [], compiled.centroid_point, compiled.vertices_points, true, compiled.version ) ) ) }; } function buildKinks(kinksPoints?: Position[]) { if (!kinksPoints) return undefined; return { bakw: featureCollection( kinksPoints.map((coord: Position) => point(coord)) ) }; } function normalizeLegacyStructure( compiled: CompiledLegacy ): CompiledLegacy { return JSON.parse( JSON.stringify(compiled) .replace('"cent"', '"c"') .replace(/"bbox(\d+)"/g, '"b$1"') ); } function rebuildLegacyPoints(tins: TinsBD): PointSet[] { const points: PointSet[] = []; const features = tins.forw!.features; for (let i = 0; i < features.length; i++) { const tri = features[i]; (["a", "b", "c"] as PropertyTriKey[]).forEach((key, idx) => { const forw = tri.geometry!.coordinates[0][idx]; const bakw = tri.properties![key].geom; const pIdx = tri.properties![key].index; if (typeof pIdx === "number") { points[pIdx] = [forw, bakw]; } }); } return points; }