diff --git a/src/ol/format/GML3.js b/src/ol/format/GML3.js index acbc25d586..fb96ad509d 100644 --- a/src/ol/format/GML3.js +++ b/src/ol/format/GML3.js @@ -828,7 +828,7 @@ class GML3 extends GMLBase { */ MULTIGEOMETRY_MEMBER_NODE_FACTORY_(value, objectStack, opt_nodeName) { const parentNode = objectStack[objectStack.length - 1].node; - return createElementNS('http://www.opengis.net/gml', + return createElementNS(this.namespace, MULTIGEOMETRY_TO_MEMBER_NODENAME[parentNode.nodeName]); } @@ -861,7 +861,7 @@ class GML3 extends GMLBase { } else { nodeName = 'Envelope'; } - return createElementNS('http://www.opengis.net/gml', + return createElementNS(this.namespace, nodeName); } @@ -876,7 +876,7 @@ class GML3 extends GMLBase { */ writeGeometryNode(geometry, opt_options) { opt_options = this.adaptOptions(opt_options); - const geom = createElementNS('http://www.opengis.net/gml', 'geom'); + const geom = createElementNS(this.namespace, 'geom'); const context = {node: geom, hasZ: this.hasZ, srsName: this.srsName, curve: this.curve_, surface: this.surface_, multiSurface: this.multiSurface_, multiCurve: this.multiCurve_}; @@ -898,7 +898,7 @@ class GML3 extends GMLBase { */ writeFeaturesNode(features, opt_options) { opt_options = this.adaptOptions(opt_options); - const node = createElementNS('http://www.opengis.net/gml', 'featureMembers'); + const node = createElementNS(this.namespace, 'featureMembers'); node.setAttributeNS(XML_SCHEMA_INSTANCE_URI, 'xsi:schemaLocation', this.schemaLocation); const context = { srsName: this.srsName, diff --git a/src/ol/format/GML32.js b/src/ol/format/GML32.js new file mode 100644 index 0000000000..2d30d4b776 --- /dev/null +++ b/src/ol/format/GML32.js @@ -0,0 +1,387 @@ +/** + * @module ol/format/GML32 + */ +import GML3 from './GML3.js'; +import GMLBase from './GML3.js'; +import {makeArrayPusher, makeReplacer, makeChildAppender} from '../xml.js'; +import {writeStringTextNode} from '../format/xsd.js'; + +/** + * @classdesc Feature format for reading and writing data in the GML format + * version 3.2.1. + * @api + */ +class GML32 extends GML3 { + + /** + * @param {module:ol/format/GMLBase~Options=} opt_options Optional configuration object. + */ + constructor(opt_options) { + const options = /** @type {olx.format.GMLOptions} */ + (opt_options ? opt_options : {}); + + super(options); + + /** + * @inheritDoc + */ + this.schemaLocation = options.schemaLocation ? + options.schemaLocation : this.namespace + ' http://schemas.opengis.net/gml/3.2.1/gml.xsd'; + + } +} + +GML32.prototype.namespace = 'http://www.opengis.net/gml/3.2'; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'pos': makeReplacer(GML3.prototype.readFlatPos_), + 'posList': makeReplacer(GML3.prototype.readFlatPosList_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.FLAT_LINEAR_RINGS_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'interior': GML3.prototype.interiorParser_, + 'exterior': GML3.prototype.exteriorParser_ + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.GEOMETRY_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'Point': makeReplacer(GMLBase.prototype.readPoint), + 'MultiPoint': makeReplacer( + GMLBase.prototype.readMultiPoint), + 'LineString': makeReplacer( + GMLBase.prototype.readLineString), + 'MultiLineString': makeReplacer( + GMLBase.prototype.readMultiLineString), + 'LinearRing': makeReplacer( + GMLBase.prototype.readLinearRing), + 'Polygon': makeReplacer(GMLBase.prototype.readPolygon), + 'MultiPolygon': makeReplacer( + GMLBase.prototype.readMultiPolygon), + 'Surface': makeReplacer(GML32.prototype.readSurface_), + 'MultiSurface': makeReplacer( + GML3.prototype.readMultiSurface_), + 'Curve': makeReplacer(GML32.prototype.readCurve_), + 'MultiCurve': makeReplacer( + GML3.prototype.readMultiCurve_), + 'Envelope': makeReplacer(GML32.prototype.readEnvelope_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.MULTICURVE_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'curveMember': makeArrayPusher( + GML3.prototype.curveMemberParser_), + 'curveMembers': makeArrayPusher( + GML3.prototype.curveMemberParser_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.MULTISURFACE_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'surfaceMember': makeArrayPusher( + GML3.prototype.surfaceMemberParser_), + 'surfaceMembers': makeArrayPusher( + GML3.prototype.surfaceMemberParser_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.CURVEMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'LineString': makeArrayPusher( + GMLBase.prototype.readLineString), + 'Curve': makeArrayPusher(GML3.prototype.readCurve_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.SURFACEMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'Polygon': makeArrayPusher(GMLBase.prototype.readPolygon), + 'Surface': makeArrayPusher(GML3.prototype.readSurface_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.SURFACE_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'patches': makeReplacer(GML3.prototype.readPatch_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.CURVE_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'segments': makeReplacer(GML3.prototype.readSegment_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.ENVELOPE_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'lowerCorner': makeArrayPusher( + GML3.prototype.readFlatPosList_), + 'upperCorner': makeArrayPusher( + GML3.prototype.readFlatPosList_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.PATCHES_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'PolygonPatch': makeReplacer( + GML3.prototype.readPolygonPatch_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.SEGMENTS_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'LineStringSegment': makeReplacer( + GML3.prototype.readLineStringSegment_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.MULTIPOINT_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'pointMember': makeArrayPusher( + GMLBase.prototype.pointMemberParser_), + 'pointMembers': makeArrayPusher( + GMLBase.prototype.pointMemberParser_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.MULTILINESTRING_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'lineStringMember': makeArrayPusher( + GMLBase.prototype.lineStringMemberParser_), + 'lineStringMembers': makeArrayPusher( + GMLBase.prototype.lineStringMemberParser_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.MULTIPOLYGON_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'polygonMember': makeArrayPusher( + GMLBase.prototype.polygonMemberParser_), + 'polygonMembers': makeArrayPusher( + GMLBase.prototype.polygonMemberParser_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.POINTMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'Point': makeArrayPusher( + GMLBase.prototype.readFlatCoordinatesFromNode_) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.LINESTRINGMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'LineString': makeArrayPusher( + GMLBase.prototype.readLineString) + } +}; + +/** + * @const + * @type {Object.>} + * @private + */ +GML32.prototype.POLYGONMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'Polygon': makeArrayPusher( + GMLBase.prototype.readPolygon) + } +}; + +/** + * @const + * @type {Object.>} + * @protected + */ +GML32.prototype.RING_PARSERS = { + 'http://www.opengis.net/gml/3.2': { + 'LinearRing': makeReplacer( + GMLBase.prototype.readFlatLinearRing_) + } +}; + +/** + * @type {Object.>} + * @private + */ +GML32.prototype.RING_SERIALIZERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'exterior': makeChildAppender(GML3.prototype.writeRing_), + 'interior': makeChildAppender(GML3.prototype.writeRing_) + } +}; + + +/** + * @type {Object.>} + * @private + */ +GML32.prototype.ENVELOPE_SERIALIZERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'lowerCorner': makeChildAppender(writeStringTextNode), + 'upperCorner': makeChildAppender(writeStringTextNode) + } +}; + + +/** + * @type {Object.>} + * @private + */ +GML32.prototype.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'surfaceMember': makeChildAppender( + GML3.prototype.writeSurfaceOrPolygonMember_), + 'polygonMember': makeChildAppender( + GML3.prototype.writeSurfaceOrPolygonMember_) + } +}; + + +/** + * @type {Object.>} + * @private + */ +GML32.prototype.POINTMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'pointMember': makeChildAppender( + GML3.prototype.writePointMember_) + } +}; + + +/** + * @type {Object.>} + * @private + */ +GML32.prototype.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'lineStringMember': makeChildAppender( + GML3.prototype.writeLineStringOrCurveMember_), + 'curveMember': makeChildAppender( + GML3.prototype.writeLineStringOrCurveMember_) + } +}; + +/** + * @type {Object.>} + * @private + */ +GML32.prototype.GEOMETRY_SERIALIZERS_ = { + 'http://www.opengis.net/gml/3.2': { + 'Curve': makeChildAppender( + GML3.prototype.writeCurveOrLineString_), + 'MultiCurve': makeChildAppender( + GML3.prototype.writeMultiCurveOrLineString_), + 'Point': makeChildAppender(GML32.prototype.writePoint_), + 'MultiPoint': makeChildAppender( + GML3.prototype.writeMultiPoint_), + 'LineString': makeChildAppender( + GML3.prototype.writeCurveOrLineString_), + 'MultiLineString': makeChildAppender( + GML3.prototype.writeMultiCurveOrLineString_), + 'LinearRing': makeChildAppender( + GML3.prototype.writeLinearRing_), + 'Polygon': makeChildAppender( + GML3.prototype.writeSurfaceOrPolygon_), + 'MultiPolygon': makeChildAppender( + GML3.prototype.writeMultiSurfaceOrPolygon_), + 'Surface': makeChildAppender( + GML3.prototype.writeSurfaceOrPolygon_), + 'MultiSurface': makeChildAppender( + GML3.prototype.writeMultiSurfaceOrPolygon_), + 'Envelope': makeChildAppender( + GML3.prototype.writeEnvelope) + } +}; + +export default GML32; diff --git a/src/ol/format/GMLBase.js b/src/ol/format/GMLBase.js index 067231e3de..c85107bca3 100644 --- a/src/ol/format/GMLBase.js +++ b/src/ol/format/GMLBase.js @@ -123,7 +123,7 @@ class GMLBase extends XMLFeature { * @type {Object>} */ this.FEATURE_COLLECTION_PARSERS = {}; - this.FEATURE_COLLECTION_PARSERS[GMLNS] = { + this.FEATURE_COLLECTION_PARSERS[this.namespace] = { 'featureMember': makeArrayPusher(this.readFeaturesInternal), 'featureMembers': makeReplacer(this.readFeaturesInternal) }; @@ -466,6 +466,9 @@ class GMLBase extends XMLFeature { } +GMLBase.prototype.namespace = GMLNS; + + /** * @const * @type {Object>} diff --git a/test/spec/ol/format/gml.test.js b/test/spec/ol/format/gml.test.js index db26b86ea6..d0355c6f6e 100644 --- a/test/spec/ol/format/gml.test.js +++ b/test/spec/ol/format/gml.test.js @@ -1,6 +1,7 @@ import Feature from '../../../../src/ol/Feature.js'; import GML from '../../../../src/ol/format/GML.js'; import GML2 from '../../../../src/ol/format/GML2.js'; +import GML32 from '../../../../src/ol/format/GML32.js'; import LineString from '../../../../src/ol/geom/LineString.js'; import LinearRing from '../../../../src/ol/geom/LinearRing.js'; import MultiLineString from '../../../../src/ol/geom/MultiLineString.js'; @@ -1601,3 +1602,861 @@ describe('ol.format.GML3', function() { }); }); + + +describe('ol.format.GML32', function() { + + let format, formatWGS84, formatNoSrs; + beforeEach(function() { + format = new GML32({srsName: 'CRS:84'}); + formatWGS84 = new GML32({ + srsName: 'urn:x-ogc:def:crs:EPSG:4326' + }); + formatNoSrs = new GML32(); + }); + + describe('#readGeometry', function() { + + describe('point', function() { + + it('can read and write a point geometry', function() { + const text = + '' + + ' 1 2' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(Point); + expect(g.getCoordinates()).to.eql([1, 2, 0]); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + it('can read a point geometry with scientific notation', function() { + let text = + '' + + ' 1E7 2' + + ''; + let g = readGeometry(format, text); + expect(g).to.be.an(Point); + expect(g.getCoordinates()).to.eql([10000000, 2, 0]); + text = + '' + + ' 1e7 2' + + ''; + g = readGeometry(format, text); + expect(g).to.be.an(Point); + expect(g.getCoordinates()).to.eql([10000000, 2, 0]); + }); + + it('can read, transform and write a point geometry', function() { + const config = { + featureProjection: 'EPSG:3857' + }; + const text = + '' + + ' 1 2' + + ''; + const g = readGeometry(format, text, config); + expect(g).to.be.an(Point); + const coordinates = g.getCoordinates(); + expect(coordinates.splice(0, 2)).to.eql( + transform([1, 2], 'CRS:84', 'EPSG:3857')); + config.dataProjection = 'CRS:84'; + const serialized = format.writeGeometryNode(g, config); + const pos = serialized.firstElementChild.firstElementChild.textContent; + const coordinate = pos.split(' '); + expect(coordinate[0]).to.roughlyEqual(1, 1e-9); + expect(coordinate[1]).to.roughlyEqual(2, 1e-9); + }); + + it('can detect SRS, read and transform a point geometry', function() { + const config = { + featureProjection: 'EPSG:3857' + }; + const text = + '' + + ' 1 2' + + ''; + const g = readGeometry(formatNoSrs, text, config); + expect(g).to.be.an(Point); + const coordinates = g.getCoordinates(); + expect(coordinates.splice(0, 2)).to.eql( + transform([1, 2], 'CRS:84', 'EPSG:3857')); + }); + + it('can read and write a point geometry in EPSG:4326', function() { + const text = + '' + + ' 2 1' + + ''; + const g = readGeometry(formatWGS84, text); + expect(g).to.be.an(Point); + expect(g.getCoordinates()).to.eql([1, 2, 0]); + const serialized = formatWGS84.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + }); + + describe('linestring', function() { + + it('can read and write a linestring geometry', function() { + const text = + '' + + ' 1 2 3 4' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(LineString); + expect(g.getCoordinates()).to.eql([[1, 2, 0], [3, 4, 0]]); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + it('can read, transform and write a linestring geometry', function() { + const config = { + dataProjection: 'CRS:84', + featureProjection: 'EPSG:3857' + }; + const text = + '' + + ' 1 2 3 4' + + ''; + const g = readGeometry(format, text, config); + expect(g).to.be.an(LineString); + const coordinates = g.getCoordinates(); + expect(coordinates[0].slice(0, 2)).to.eql( + transform([1, 2], 'CRS:84', 'EPSG:3857')); + expect(coordinates[1].slice(0, 2)).to.eql( + transform([3, 4], 'CRS:84', 'EPSG:3857')); + const serialized = format.writeGeometryNode(g, config); + const poss = serialized.firstElementChild.firstElementChild.textContent; + const coordinate = poss.split(' '); + expect(coordinate[0]).to.roughlyEqual(1, 1e-9); + expect(coordinate[1]).to.roughlyEqual(2, 1e-9); + expect(coordinate[2]).to.roughlyEqual(3, 1e-9); + expect(coordinate[3]).to.roughlyEqual(4, 1e-9); + }); + + it('can read and write a linestring geometry in EPSG:4326', function() { + const text = + '' + + ' 2 1 4 3' + + ''; + const g = readGeometry(formatWGS84, text); + expect(g).to.be.an(LineString); + expect(g.getCoordinates()).to.eql([[1, 2, 0], [3, 4, 0]]); + const serialized = formatWGS84.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + }); + + describe('axis order', function() { + + it('can read and write a linestring geometry with ' + + 'correct axis order', + function() { + const text = + '' + + ' -90 -180 90 180' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(LineString); + expect(g.getCoordinates()).to.eql([[-180, -90, 0], [180, 90, 0]]); + const serialized = formatWGS84.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + it('can read and write a point geometry with correct axis order', + function() { + const text = + '' + + ' -90 -180' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(Point); + expect(g.getCoordinates()).to.eql([-180, -90, 0]); + const serialized = formatWGS84.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + it('can read and write a surface geometry with right axis order', + function() { + const text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 38.9661 -77.0081 38.9931 -77.0421 ' + + ' 38.9321 -77.1221 38.9151 -77.0781 38.8861 ' + + ' -77.0671 38.8621 -77.0391 38.8381 -77.0401 ' + + ' 38.8291 -77.0451 38.8131 -77.0351 38.7881 ' + + ' -77.0451 38.8891 -76.9111 38.9661 -77.0081' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g.getCoordinates()[0][0][0][0]).to.equal(-77.0081); + expect(g.getCoordinates()[0][0][0][1]).to.equal(38.9661); + format = new GML32({ + srsName: 'urn:x-ogc:def:crs:EPSG:4326', + surface: false}); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + }); + + describe('linestring 3D', function() { + + it('can read a linestring 3D geometry', function() { + const text = + '' + + ' 1 2 3 4 5 6' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(LineString); + expect(g.getCoordinates()).to.eql([[1, 2, 3], [4, 5, 6]]); + }); + + }); + + describe('linearring', function() { + + it('can read and write a linearring geometry', function() { + const text = + '' + + ' 1 2 3 4 5 6 1 2' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(LinearRing); + expect(g.getCoordinates()).to.eql( + [[1, 2, 0], [3, 4, 0], [5, 6, 0], [1, 2, 0]]); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + }); + + describe('polygon', function() { + + it('can read and write a polygon geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' 1 2 3 2 3 4 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 2 3 2 5 4 5 2 3' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3 4 3 6 5 6 3 4' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(Polygon); + expect(g.getCoordinates()).to.eql([[[1, 2, 0], [3, 2, 0], [3, 4, 0], + [1, 2, 0]], [[2, 3, 0], [2, 5, 0], [4, 5, 0], [2, 3, 0]], + [[3, 4, 0], [3, 6, 0], [5, 6, 0], [3, 4, 0]]]); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + }); + + describe('surface', function() { + + it('can read and write a surface geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1 2 3 2 3 4 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 2 3 2 5 4 5 2 3' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3 4 3 6 5 6 3 4' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(Polygon); + expect(g.getCoordinates()).to.eql([[[1, 2, 0], [3, 2, 0], [3, 4, 0], + [1, 2, 0]], [[2, 3, 0], [2, 5, 0], [4, 5, 0], [2, 3, 0]], + [[3, 4, 0], [3, 6, 0], [5, 6, 0], [3, 4, 0]]]); + format = new GML32({srsName: 'CRS:84', surface: true}); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + }); + + describe('curve', function() { + + it('can read and write a curve geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' 1 2 3 4' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(LineString); + expect(g.getCoordinates()).to.eql([[1, 2, 0], [3, 4, 0]]); + format = new GML32({srsName: 'CRS:84', curve: true}); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + }); + + describe('envelope', function() { + + it('can read an envelope geometry', function() { + const text = + '' + + ' 1 2' + + ' 3 4' + + ''; + const g = readGeometry(format, text); + expect(g).to.eql([1, 2, 3, 4]); + }); + + }); + + describe('multipoint', function() { + + it('can read and write a singular multipoint geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 2 3' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3 4' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(MultiPoint); + expect(g.getCoordinates()).to.eql([[1, 2, 0], [2, 3, 0], [3, 4, 0]]); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + it('can read a plural multipoint geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' 1 2' + + ' ' + + ' ' + + ' 2 3' + + ' ' + + ' ' + + ' 3 4' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(MultiPoint); + expect(g.getCoordinates()).to.eql([[1, 2, 0], [2, 3, 0], [3, 4, 0]]); + }); + + }); + + describe('multilinestring', function() { + + it('can read and write a singular multilinestring geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' 1 2 2 3' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3 4 4 5' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(MultiLineString); + expect(g.getCoordinates()).to.eql( + [[[1, 2, 0], [2, 3, 0]], [[3, 4, 0], [4, 5, 0]]]); + format = new GML32({srsName: 'CRS:84', multiCurve: false}); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + it('can read a plural multilinestring geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' 1 2 2 3' + + ' ' + + ' ' + + ' 3 4 4 5' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(MultiLineString); + expect(g.getCoordinates()).to.eql( + [[[1, 2, 0], [2, 3, 0]], [[3, 4, 0], [4, 5, 0]]]); + }); + + }); + + describe('multipolygon', function() { + + it('can read and write a singular multipolygon geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1 2 3 2 3 4 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 2 3 2 5 4 5 2 3' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3 4 3 6 5 6 3 4' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1 2 3 2 3 4 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(MultiPolygon); + expect(g.getCoordinates()).to.eql([ + [[[1, 2, 0], [3, 2, 0], [3, 4, 0], + [1, 2, 0]], [[2, 3, 0], [2, 5, 0], [4, 5, 0], [2, 3, 0]], + [[3, 4, 0], [3, 6, 0], [5, 6, 0], [3, 4, 0]]], + [[[1, 2, 0], [3, 2, 0], [3, 4, 0], [1, 2, 0]]]]); + format = new GML32({srsName: 'CRS:84', multiSurface: false}); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + it('can read a plural multipolygon geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1 2 3 2 3 4 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 2 3 2 5 4 5 2 3' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3 4 3 6 5 6 3 4' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1 2 3 2 3 4 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(MultiPolygon); + expect(g.getCoordinates()).to.eql([ + [[[1, 2, 0], [3, 2, 0], [3, 4, 0], + [1, 2, 0]], [[2, 3, 0], [2, 5, 0], [4, 5, 0], [2, 3, 0]], + [[3, 4, 0], [3, 6, 0], [5, 6, 0], [3, 4, 0]]], + [[[1, 2, 0], [3, 2, 0], [3, 4, 0], [1, 2, 0]]]]); + }); + + }); + + describe('multicurve', function() { + + it('can read and write a singular multicurve-linestring geometry', + function() { + const text = + '' + + ' ' + + ' ' + + ' 1 2 2 3' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3 4 4 5' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(MultiLineString); + expect(g.getCoordinates()).to.eql( + [[[1, 2, 0], [2, 3, 0]], [[3, 4, 0], [4, 5, 0]]]); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + it('can read and write a singular multicurve-curve geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1 2 2 3' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3 4 4 5' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(MultiLineString); + expect(g.getCoordinates()).to.eql( + [[[1, 2, 0], [2, 3, 0]], [[3, 4, 0], [4, 5, 0]]]); + format = new GML32({srsName: 'CRS:84', curve: true}); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + }); + + describe('multisurface', function() { + + it('can read and write a singular multisurface geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1 2 3 2 3 4 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 2 3 2 5 4 5 2 3' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3 4 3 6 5 6 3 4' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1 2 3 2 3 4 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(MultiPolygon); + expect(g.getCoordinates()).to.eql([ + [[[1, 2, 0], [3, 2, 0], [3, 4, 0], + [1, 2, 0]], [[2, 3, 0], [2, 5, 0], [4, 5, 0], [2, 3, 0]], + [[3, 4, 0], [3, 6, 0], [5, 6, 0], [3, 4, 0]]], + [[[1, 2, 0], [3, 2, 0], [3, 4, 0], [1, 2, 0]]]]); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + it('can read a plural multisurface geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1 2 3 2 3 4 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 2 3 2 5 4 5 2 3' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3 4 3 6 5 6 3 4' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1 2 3 2 3 4 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(MultiPolygon); + expect(g.getCoordinates()).to.eql([ + [[[1, 2, 0], [3, 2, 0], [3, 4, 0], + [1, 2, 0]], [[2, 3, 0], [2, 5, 0], [4, 5, 0], [2, 3, 0]], + [[3, 4, 0], [3, 6, 0], [5, 6, 0], [3, 4, 0]]], + [[[1, 2, 0], [3, 2, 0], [3, 4, 0], [1, 2, 0]]]]); + }); + + it('can read and write a multisurface-surface geometry', function() { + const text = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1 2 3 2 3 4 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 2 3 2 5 4 5 2 3' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 3 4 3 6 5 6 3 4' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' 1 2 3 2 3 4 1 2' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + const g = readGeometry(format, text); + expect(g).to.be.an(MultiPolygon); + expect(g.getCoordinates()).to.eql([ + [[[1, 2, 0], [3, 2, 0], [3, 4, 0], + [1, 2, 0]], [[2, 3, 0], [2, 5, 0], [4, 5, 0], [2, 3, 0]], + [[3, 4, 0], [3, 6, 0], [5, 6, 0], [3, 4, 0]]], + [[[1, 2, 0], [3, 2, 0], [3, 4, 0], [1, 2, 0]]]]); + format = new GML32({srsName: 'CRS:84', surface: true}); + const serialized = format.writeGeometryNode(g); + expect(serialized.firstElementChild).to.xmleql(parse(text)); + }); + + }); + + }); + + describe('when parsing empty attribute', function() { + it('generates undefined value', function() { + const text = + '' + + ' ' + + ' Aflu' + + ' ' + + ' ' + + ' 34.12 2.09' + + ' ' + + ' ' + + ' 84683' + + ' Algeria' + + ' place' + + ' Aflu' + + ' ' + + ' ' + + ''; + const config = { + 'featureNS': 'http://www.openplans.org/topp', + 'featureType': 'gnis_pop' + }; + const features = new GML32(config).readFeatures(text); + const feature = features[0]; + expect(feature.get('empty')).to.be(undefined); + }); + }); + + describe('when parsing CDATA attribute', function() { + let features; + before(function(done) { + try { + const text = + '' + + ' ' + + ' Aflu' + + ' ' + + ' ' + + ' 34.12 2.09' + + ' ' + + ' ' + + ' 84683' + + ' Algeria' + + ' place' + + ' Aflu' + + ' b]]>' + + ' ' + + ''; + const config = { + 'featureNS': 'http://www.openplans.org/topp', + 'featureType': 'gnis_pop' + }; + features = new GML32(config).readFeatures(text); + } catch (e) { + done(e); + } + done(); + }); + + it('creates 1 feature', function() { + expect(features).to.have.length(1); + }); + + it('converts XML attribute to text', function() { + expect(features[0].get('cdata')).to.be('b'); + }); + }); + +});