UNPKG

@maplat/tin

Version:

JavaScript library which performs homeomorphic conversion mutually between the coordinate systems of two planes based on the control points.

115 lines (95 loc) 3.54 kB
import type { PropertyTriKey, TinsBD, Tri, WeightBufferBD, } from "@maplat/transform"; interface WeightBufferOptions { tins: TinsBD; targets: Array<keyof TinsBD>; includeReciprocals: boolean; /** Number of boundary vertices (b0..b{N-1}). Defaults to 4 for v2 format. */ numBoundaryVertices?: number; } /** * Calculate per-point stretch ratios used for non-linear interpolation. */ export function buildPointsWeightBuffer( options: WeightBufferOptions, ): WeightBufferBD { const { tins, targets, includeReciprocals, numBoundaryVertices = 4 } = options; const edgeRatios: WeightBufferBD = {}; targets.forEach((target) => { const triCollection = tins[target]; if (!triCollection || !triCollection.features) return; edgeRatios[target as keyof WeightBufferBD] = {}; const seenEdges: Record<string, boolean> = {}; triCollection.features.forEach((tri: Tri) => { const props = ["a", "b", "c"] as PropertyTriKey[]; for (let i = 0; i < 3; i++) { const j = (i + 1) % 3; const prop_i = props[i]; const prop_j = props[j]; const index_i = tri.properties![prop_i].index; const index_j = tri.properties![prop_j].index; const key = [index_i, index_j].sort().join("-"); if (seenEdges[key]) continue; seenEdges[key] = true; const i_xy = tri.geometry!.coordinates[0][i]; const j_xy = tri.geometry!.coordinates[0][j]; const i_merc = tri.properties![prop_i].geom; const j_merc = tri.properties![prop_j].geom; const ratio = Math.sqrt( Math.pow(i_merc[0] - j_merc[0], 2) + Math.pow(i_merc[1] - j_merc[1], 2), ) / Math.sqrt( Math.pow(i_xy[0] - j_xy[0], 2) + Math.pow(i_xy[1] - j_xy[1], 2), ); const targetBuffer = edgeRatios[target as keyof WeightBufferBD]!; targetBuffer[`${index_i}:${key}`] = ratio; targetBuffer[`${index_j}:${key}`] = ratio; } }); }); const pointsWeightBuffer: WeightBufferBD = {}; if (includeReciprocals) { pointsWeightBuffer.bakw = {}; } targets.forEach((target) => { const targetBuffer = edgeRatios[target as keyof WeightBufferBD]; pointsWeightBuffer[target as keyof WeightBufferBD] = {}; if (!targetBuffer) { return; } const pointWeights: Record<string, number[]> = {}; Object.keys(targetBuffer).forEach((key) => { const [pointId] = key.split(":"); if (!pointWeights[pointId]) { pointWeights[pointId] = []; } pointWeights[pointId].push(targetBuffer[key]); }); Object.keys(pointWeights).forEach((pointId) => { const weights = pointWeights[pointId]; const avgWeight = weights.reduce((sum, w) => sum + w, 0) / weights.length; pointsWeightBuffer[target as keyof WeightBufferBD]![pointId] = avgWeight; if (includeReciprocals && pointsWeightBuffer.bakw) { pointsWeightBuffer.bakw[pointId] = 1 / avgWeight; } }); let centroidSum = 0; for (let i = 0; i < numBoundaryVertices; i++) { const key = `b${i}`; const weight = pointsWeightBuffer[target as keyof WeightBufferBD]![key] || 0; centroidSum += weight; } pointsWeightBuffer[target as keyof WeightBufferBD]!["c"] = centroidSum / numBoundaryVertices; if (includeReciprocals && pointsWeightBuffer.bakw) { pointsWeightBuffer.bakw["c"] = 1 / pointsWeightBuffer[target as keyof WeightBufferBD]!["c"]; } }); return pointsWeightBuffer; }