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, "stroke-width": 0, "color": "transparent", "fill": getColor(`surface-${name}`, opts.colorOpts), "fill-opacity": 0.3, }; 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; }