import { getProp, parseSequence, getColor, circleToPolygon } from './util' import { point, lineString, polygon } from '@turf/helpers'; import transformTranslate from '@turf/transform-translate'; import clone from '@turf/clone'; import proj4 from 'proj4'; export const LANDXML_PIPE_NETWORK_PIPE = 'pipe_network_pipe'; export const LANDXML_PIPE_NETWORK_STRUCT = 'pipe_network_struct'; export function convertPipeNetworks(xmlObj, projection, opts) { const features = []; const pipeNetworksNodes = getProp(xmlObj, "LandXML", "PipeNetworks"); if (!pipeNetworksNodes) return features; pipeNetworksNodes.forEach(pn => { const pipeNetworkNodes = getProp(pn, "PipeNetwork"); if (!Array.isArray(pipeNetworkNodes)) return; let structs = {}; pipeNetworkNodes.forEach(pn => { const baseProps = { name: pn.$.name, description: pn.$.desc, }; const localStructs = convertStructs(pn, projection, baseProps, opts); features.push(...Object.values(localStructs).map(s => s.geometry)); Object.assign(structs, localStructs); }); pipeNetworkNodes.forEach(pn => { const baseProps = { name: pn.$.name, description: pn.$.desc, landxmlType: LANDXML_PIPE_NETWORK_PIPE, }; const pipes = convertPipes(pn, structs, projection, baseProps, opts); features.push(...pipes); }); }); return features; } function convertPipes(pipeNetwork, structs, projection, baseProps, opts) { const pipes = []; const pipeNodes = getProp(pipeNetwork, "Pipes", 0, "Pipe"); if (!Array.isArray(pipeNodes)) return; pipeNodes.forEach(p => { const circPipeNode = getProp(p, "CircPipe", 0); const name = baseProps.name ? baseProps.name + ' - ' + p.$.name : p.$.name; const featureProps = Object.assign({}, baseProps, { name: name, description: baseProps.description ? baseProps.description + ' - ' + p.$.desc : p.$.desc, landxmlColor: getColor(`pipe-network-pipe-${name}`, opts.colorOpts) }); if (circPipeNode !== undefined) { const attrs = circPipeNode.$; if ('diameter' in attrs) featureProps.landxmlDiameter = parseFloat(attrs.diameter); if ('thickness' in attrs) featureProps.landxmlThickness = parseFloat(attrs.thickness); if ('material' in attrs) featureProps.landxmlMaterial = attrs.material; } const refStart = structs[p.$.refStart]; const refEnd = structs[p.$.refEnd]; if (!refStart) throw new Error(`Cannot find pipe "${featureProps.name}" refStart "${p.$.refStart}" !`); if (!refEnd) throw new Error(`Cannot find pipe "${featureProps.name}" refEnd "${p.$.refEnd}" !`); const pipe = lineString([ refStart.center, refEnd.center, ], featureProps); pipes.push(pipe); }); return pipes; } function convertStructs(pipeNetwork, projection, baseProps, opts) { const structs = {}; const structNodes = getProp(pipeNetwork, "Structs", 0, "Struct"); if (!Array.isArray(structNodes)) return; structNodes.forEach(s => { const centerNode = getProp(s, "Center", 0); const rectStructNode = getProp(s, "RectStruct", 0); const circStructNode = getProp(s, "CircStruct", 0); const inletStructNode = getProp(s, "InletStruct", 0); const outletStructNode = getProp(s, "OutletStruct", 0); const connectionNode = getProp(s, "Connection", 0); const structType = rectStructNode !== undefined ? "rect" : ( ( circStructNode !== undefined ? "circ" : ( inletStructNode !== undefined ? "inlet" : ( outletStructNode !== undefined ? "outlet" : ( connectionNode !== undefined ? "connection" : ( null ) ))))); const name = baseProps.name ? baseProps.name + ' - ' + s.$.name : s.$.name; const featureProps = Object.assign({}, baseProps, { name: name, description: baseProps.description ? baseProps.description + ' - ' + s.$.desc : s.$.desc, landxmlType: LANDXML_PIPE_NETWORK_STRUCT, landxmlStruct: structType, landxmlElevRim: 'elevRim' in s.$ ? parseFloat(s.$.elevRim) : undefined, landxmlElevSump: 'elevSump' in s.$ ? parseFloat(s.$.elevSump) : undefined, landxmlColor: getColor(`pipe-network-struct-${name}`, opts.colorOpts) }); if (circStructNode !== undefined) { const attrs = circStructNode.$; if ('diameter' in attrs) featureProps.landxmlDiameter = parseFloat(attrs.diameter); if ('thickness' in attrs) featureProps.landxmlThickness = parseFloat(attrs.thickness); } if (rectStructNode !== undefined) { const attrs = rectStructNode.$; if ('thickness' in attrs) featureProps.landxmlThickness = parseFloat(attrs.thickness); if ('length' in attrs) featureProps.landxmlLength = parseFloat(attrs.length); if ('width' in attrs) featureProps.landxmlWidth = parseFloat(attrs.width); } // Parse and convert center coordinates const seq = parseSequence(centerNode).map(v => parseFloat(v)); let center = [seq[1], seq[0], seq[2]]; center = proj4(projection, 'WGS84', center); let geometry; if (rectStructNode) { const length = featureProps.length ? featureProps.length : 1; const width = featureProps.width ? featureProps.width : 1; geometry = squareAround(center, length, width, 'meters', featureProps); } else { const diameter = featureProps.landxmlDiameter ? featureProps.landxmlDiameter : 1; geometry = circleToPolygon(center, diameter/2, 128, featureProps); } if (!geometry) { throw new Error("Unsupported struct type") }; structs[s.$.name] = { geometry, center }; }); return structs; } function squareAround(centerCoords, length, width, units, properties) { const center = point(centerCoords); const northEast = clone(center); transformTranslate(northEast, width/2, 270, { units, mutate: true }); transformTranslate(northEast, length/2, 0, { units, mutate: true }); const northWest = clone(center); transformTranslate(northWest, width/2, 90, { units, mutate: true }); transformTranslate(northWest, length/2, 0, { units, mutate: true }); const southWest = clone(center); transformTranslate(southWest, width/2, 90, { units, mutate: true }); transformTranslate(southWest, length/2, 180, { units, mutate: true }); const southEast = clone(center); transformTranslate(southEast, width/2, 270, { units, mutate: true }); transformTranslate(southEast, length/2, 180, { units, mutate: true }); const points = [ northEast.geometry.coordinates, northWest.geometry.coordinates, southWest.geometry.coordinates, southEast.geometry.coordinates, northEast.geometry.coordinates ]; return polygon([points], properties); }