landxml/lib/surface.js

127 lines
3.5 KiB
JavaScript

import { getProp, parseSequence, getColor } from './util'
import { polygon } from '@turf/helpers';
import union from '@turf/union';
import proj4 from 'proj4';
export const LANDXML_SURFACE = 'surface';
export function convertSurfaces(xmlObj, projection, opts) {
const features = [];
const surfacesContainerNodes = getProp(xmlObj, "LandXML", "Surfaces");
if (!surfacesContainerNodes) return features;
surfacesContainerNodes.forEach(sc => {
const attrs = (sc.$ || {});
const baseProps = {
name: attrs.name || "",
description: attrs.desc,
};
const surfaceNodes = getProp(sc, "Surface");
if (!Array.isArray(surfaceNodes)) return;
surfaceNodes.forEach(s => {
const name = baseProps.name ? ( baseProps.name + ' - ' + s.$.name ) : s.$.name;
const description = baseProps.description ? ( baseProps.description + ' - ' + s.$.desc ) : s.$.desc;
const featureProps = {
name: name,
description: description,
landxmlType: LANDXML_SURFACE,
landxmlColor: getColor(`surface-${name}`, opts.colorOpts),
};
let surfaceFeatures = convertBoundaries(s, projection, featureProps, opts);
if (surfaceFeatures.length !== 0) {
features.push(...surfaceFeatures);
} else {
surfaceFeatures = convertFaces(s, projection, featureProps, opts);
let merged = surfaceFeatures[0];
console.error("Applying union to %s faces (%d faces to merge)", merged.properties.name, surfaceFeatures.length);
for (let i = 1; i < surfaceFeatures.length-1; i++) {
const next = surfaceFeatures[i+1];
if (!next) return;
try {
merged = union(merged, next);
} catch(err) {
console.error("--> Could not apply union to %s (#%d)", next.properties.name, i+1);
// Ignore
}
}
merged.properties = featureProps;
features.push(merged);
}
});
});
return features;
}
function convertFaces(surface, projection, featureProps, opts) {
const pointNodes = getProp(surface, "Definition", 0, "Pnts", 0, "P");
const faceNodes = getProp(surface, "Definition", 0, "Faces", 0, "F");
const points = {};
pointNodes.forEach(p => {
let pointCoords = parseSequence(p._).map(v => parseFloat(v));
pointCoords = [pointCoords[1], pointCoords[0]];
pointCoords = proj4(projection, 'WGS84', pointCoords);
const pointId = p.$.id;
points[pointId] = { name: p.$.name, coords: pointCoords };
});
const faces = [];
faceNodes.forEach(f => {
const pointIds = parseSequence(f);
const coords = [];
pointIds.forEach(id => {
const p = points[id];
coords.push(p.coords);
});
coords.push(points[pointIds[0]].coords);
faces.push(polygon([coords], featureProps));
});
return [...faces];
}
function convertBoundaries(surface, projection, featureProps, opts) {
const boundaryNodes = getProp(surface, "SourceData", 0, "Boundaries", 0, "Boundary");
if (!boundaryNodes) return [];
const polygons = [];
boundaryNodes.forEach(boundary => {
const pntList3DNode = getProp(boundary, 'PntList3D', 0);
const seq = parseSequence(pntList3DNode).map(p => parseFloat(p));
const coords = [];
for (let i = 0; i < seq.length; i += 3) {
let c = [seq[i+1], seq[i]];
c = proj4(projection, 'WGS84', c);
coords.push(c);
}
coords.push(coords[0]);
polygons.push(polygon([coords], featureProps));
});
return polygons;
}