2020-12-10 16:54:02 +01:00
|
|
|
|
2021-06-30 15:14:00 +02:00
|
|
|
import xml2js from 'xml2js';
|
2020-12-10 16:54:02 +01:00
|
|
|
|
|
|
|
import { featureCollection } from '@turf/helpers';
|
|
|
|
import area from '@turf/area';
|
|
|
|
import proj4 from 'proj4';
|
|
|
|
import { Proj4Defs } from './projection';
|
|
|
|
import { getProp } from './util';
|
|
|
|
|
|
|
|
import { convertSurfaces } from './surface';
|
|
|
|
import { convertParcels } from './parcel';
|
|
|
|
import { convertPipeNetworks } from './pipe-network';
|
2021-01-22 15:56:02 +01:00
|
|
|
import { convertCGPoints } from './cg-point';
|
2020-12-10 16:54:02 +01:00
|
|
|
|
|
|
|
export const PROJECTION_AUTO_DETECT = 'auto_detect';
|
|
|
|
export const CONVERT_SURFACES = 'surfaces';
|
|
|
|
export const CONVERT_PARCELS = 'parcels';
|
|
|
|
export const CONVERT_PIPE_NETWORKS = 'pipe_networks';
|
2021-01-22 15:56:02 +01:00
|
|
|
export const CONVERT_CG_POINTS = 'cg_points';
|
2020-12-10 16:54:02 +01:00
|
|
|
|
|
|
|
const converters = {
|
|
|
|
[CONVERT_PARCELS]: convertParcels,
|
|
|
|
[CONVERT_SURFACES]: convertSurfaces,
|
|
|
|
[CONVERT_PIPE_NETWORKS]: convertPipeNetworks,
|
2021-01-22 15:56:02 +01:00
|
|
|
[CONVERT_CG_POINTS]: convertCGPoints,
|
2020-12-10 16:54:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Based on alexgleith's preliminary work
|
|
|
|
// github.com/alexgleith/land-xml-to-geojson
|
|
|
|
export class Converter {
|
|
|
|
|
|
|
|
defaultOptions = {
|
|
|
|
projection: PROJECTION_AUTO_DETECT,
|
2021-01-22 15:56:02 +01:00
|
|
|
enabledConverters: [ CONVERT_SURFACES, CONVERT_PARCELS, CONVERT_PIPE_NETWORKS, CONVERT_CG_POINTS ],
|
2020-12-10 16:54:02 +01:00
|
|
|
parser: {
|
|
|
|
normalize: true
|
|
|
|
},
|
|
|
|
proj4Defs: [
|
|
|
|
[
|
|
|
|
"EPSG:28355",
|
|
|
|
"+proj=utm +zone=55 +south +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs",
|
|
|
|
],
|
|
|
|
...Proj4Defs,
|
|
|
|
],
|
|
|
|
featureMapper: (feature, opts) => feature,
|
|
|
|
colorOpts: {
|
2021-01-25 10:26:48 +01:00
|
|
|
hue: 'blue',
|
2020-12-10 16:54:02 +01:00
|
|
|
luminosity: 'dark',
|
|
|
|
seed: 1,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
toGeoJSON(xml, opts = {}) {
|
|
|
|
const parserOptions = Object.assign({}, this.defaultOptions.parser, opts.parser);
|
|
|
|
const parser = new xml2js.Parser(parserOptions);
|
|
|
|
|
|
|
|
this.configureProj4(opts.proj4Defs);
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
parser.parseString(xml, (err, xmlObj) => {
|
|
|
|
if (err) return reject(err);
|
|
|
|
|
|
|
|
let geojson;
|
|
|
|
try {
|
|
|
|
geojson = this.convertToGeoJSON(xmlObj, opts);
|
|
|
|
} catch(err) {
|
|
|
|
return reject(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
return resolve(geojson);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
convertToGeoJSON(xmlObj, opts = {}) {
|
|
|
|
|
|
|
|
const options = Object.assign({}, this.defaultOptions, opts);
|
|
|
|
|
|
|
|
// Use specified projection or try to find one in the LandXML file
|
|
|
|
const projection = this.getLandXMLProjection(xmlObj, options.projection || this.defaultOptions.projection);
|
|
|
|
if (!projection) throw new Error("Source projection can not be found !");
|
|
|
|
|
|
|
|
// List enabled converters
|
|
|
|
const enabledConverters = Array.isArray(opts.enabledConverters) ?
|
|
|
|
opts.enabledConverters :
|
|
|
|
this.defaultOptions.enabledConverters
|
|
|
|
;
|
|
|
|
|
|
|
|
let features = [];
|
|
|
|
|
|
|
|
// Convert LandXML entities to GeoJSON features
|
|
|
|
enabledConverters.forEach(c => {
|
|
|
|
if (!(c in converters)) return;
|
|
|
|
console.log("Executing convert '%s'...", c);
|
|
|
|
const newFeatures = converters[c](xmlObj, projection, options);
|
|
|
|
features.push(...newFeatures);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Apply features mapper function if defined
|
|
|
|
if (typeof options.featureMapper === 'function') {
|
|
|
|
features = features.map(f => {
|
|
|
|
return options.featureMapper(f, options);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort feature by reverse area
|
|
|
|
// to ease rendering
|
|
|
|
features.sort((a, b) => {
|
|
|
|
if (area(a) > area(b)) return -1;
|
|
|
|
if (area(a) < area(b)) return 1;
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
// Return extracted features as a feature collection
|
|
|
|
return featureCollection(features);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
configureProj4(additionalProj4Defs) {
|
|
|
|
const proj4Defs = this.defaultOptions.proj4Defs.slice(0)
|
|
|
|
if (Array.isArray(additionalProj4Defs)) proj4Defs.push(...additionalProj4Defs);
|
|
|
|
proj4.defs(proj4Defs);
|
|
|
|
}
|
|
|
|
|
|
|
|
getLandXMLProjection(xmlObj, manualProjection) {
|
|
|
|
if (manualProjection !== PROJECTION_AUTO_DETECT) return manualProjection;
|
|
|
|
let projection = null;
|
|
|
|
if(!projection) projection = this.findUTMProjection(xmlObj);
|
|
|
|
return projection;
|
|
|
|
}
|
|
|
|
|
|
|
|
findUTMProjection(xmlObj) {
|
|
|
|
const projection = getProp(xmlObj, "LandXML", "CoordinateSystem", 0, "$", "datum");
|
|
|
|
const zone = getProp(xmlObj, "LandXML", "CgPoints", 0, "$", "zoneNumber");
|
|
|
|
let utmProjection = null
|
|
|
|
if (projection && zone) {
|
|
|
|
utmProjection = `+proj=utm +zone=${zone} +south +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs `;
|
|
|
|
}
|
|
|
|
return utmProjection;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|