UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

161 lines (160 loc) 6.22 kB
// SPDX-License-Identifier: Apache-2.0 import GML3 from 'ol/format/GML3.js'; import GML32 from 'ol/format/GML32.js'; import LineString from 'ol/geom/LineString.js'; import MultiLineString from 'ol/geom/MultiLineString.js'; import { makeReplacer } from 'ol/xml.js'; const GML31_NS = 'http://www.opengis.net/gml'; const GML32_NS = 'http://www.opengis.net/gml/3.2'; const XLINK_NS = 'http://www.w3.org/1999/xlink'; export class CompositeCurveGML3 extends GML3 { constructor(options) { super(options); registerCompositeCurveParser(this, GML31_NS); } readCompositeCurve(node, objectStack) { return readCompositeCurve(this, node, objectStack); } readOrientableCurve(node, objectStack) { return readOrientableCurve(this, node, objectStack); } } export class CompositeCurveGML32 extends GML32 { constructor(options) { super(options); registerCompositeCurveParser(this, GML32_NS); } readCompositeCurve(node, objectStack) { return readCompositeCurve(this, node, objectStack); } readOrientableCurve(node, objectStack) { return readOrientableCurve(this, node, objectStack); } } function registerCompositeCurveParser(parser, namespace) { parser.GEOMETRY_PARSERS[namespace] = { ...parser.GEOMETRY_PARSERS[namespace], CompositeCurve: makeReplacer(parser.readCompositeCurve, parser) }; parser.CURVEMEMBER_PARSERS[namespace] = { ...parser.CURVEMEMBER_PARSERS[namespace], CompositeCurve: makeReplacer(parser.readCompositeCurve, parser), OrientableCurve: makeReplacer(parser.readOrientableCurve, parser) }; } function readCompositeCurve(parser, node, objectStack) { const pieces = []; for (const member of Array.from(node.children)) { if (member.localName !== 'curveMember' && member.localName !== 'curveMembers') { continue; } pieces.push(...readCompositeCurveMember(parser, member, objectStack)); } if (pieces.length === 0) { console.warn(`CompositeCurve at ${describeNode(node)} did not contain convertible curve members.`); return new MultiLineString([]); } return buildCompositeGeometry(pieces); } function readCompositeCurveMember(parser, member, objectStack) { const href = member.getAttributeNS(XLINK_NS, 'href') ?? member.getAttribute('xlink:href'); if (href) { console.warn(`CompositeCurve member reference ${href} cannot be resolved locally.`); return []; } const pieces = []; for (const child of Array.from(member.children)) { pieces.push(...readCurveElement(parser, child, objectStack)); } if (pieces.length === 0) { console.warn(`CompositeCurve member at ${describeNode(member)} did not contain a convertible curve.`); } return pieces; } function readCurveElement(parser, node, objectStack) { if (node.localName === 'LineString') { return lineStringToPieces(parser.readLineString(node, objectStack)); } if (node.localName === 'Curve') { warnIfCurveHasUnsupportedSegments(node); return lineStringToPieces(parser.readCurve(node, objectStack)); } if (node.localName === 'CompositeCurve') { return geometryToPieces(parser.readCompositeCurve(node, objectStack)); } if (node.localName === 'OrientableCurve') { return geometryToPieces(parser.readOrientableCurve(node, objectStack)); } console.warn(`CompositeCurve member contains unsupported ${describeNode(node)}.`); return []; } function readOrientableCurve(parser, node, objectStack) { const baseCurve = Array.from(node.children).find((child) => child.localName === 'baseCurve'); if (!baseCurve) { console.warn(`OrientableCurve at ${describeNode(node)} does not contain baseCurve.`); return new MultiLineString([]); } const pieces = Array.from(baseCurve.children).flatMap((child) => readCurveElement(parser, child, objectStack)); if (node.getAttribute('orientation') === '-') { pieces.reverse(); for (const piece of pieces) { piece.reverse(); } } return buildCompositeGeometry(pieces); } function warnIfCurveHasUnsupportedSegments(node) { const segments = Array.from(node.children).find((child) => child.localName === 'segments'); if (!segments) { return; } const unsupportedSegment = Array.from(segments.children).find((child) => child.localName !== 'LineStringSegment'); if (unsupportedSegment) { console.warn(`CompositeCurve member contains unsupported ${describeNode(unsupportedSegment)} segment.`); } } function lineStringToPieces(lineString) { if (!lineString) { return []; } const coordinates = lineString.getCoordinates(); return coordinates.length > 0 ? [coordinates] : []; } function geometryToPieces(geometry) { if (geometry instanceof LineString) { return lineStringToPieces(geometry); } return geometry.getCoordinates(); } function buildCompositeGeometry(pieces) { const normalizedPieces = pieces.filter((piece) => piece.length > 0); if (normalizedPieces.length === 0) { return new MultiLineString([]); } const groups = []; for (const piece of normalizedPieces) { const current = clonePiece(piece); const previous = groups.at(-1); if (previous && coordinatesEqual(previous[previous.length - 1], current[0])) { previous.push(...current.slice(1)); } else { groups.push(current); } } return groups.length === 1 ? new LineString(groups[0]) : new MultiLineString(groups); } function clonePiece(piece) { return piece.map((coordinate) => [...coordinate]); } function coordinatesEqual(first, second) { if (first.length !== second.length) { console.warn('CompositeCurve contains members with inconsistent coordinate dimensions.'); return false; } return first.every((value, index) => value === second[index]); } function describeNode(node) { const id = node.getAttribute('gml:id') ?? node.getAttribute('id'); return id ? `${node.localName}#${id}` : node.localName; }