@geogirafe/lib-geoportal
Version:
GeoGirafe is a flexible application to build online geoportals.
161 lines (160 loc) • 6.22 kB
JavaScript
// 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;
}