diff --git a/src/ol/format/gmlformat.js b/src/ol/format/gmlformat.js new file mode 100644 index 0000000000..c430399067 --- /dev/null +++ b/src/ol/format/gmlformat.js @@ -0,0 +1,349 @@ +goog.provide('ol.format.GML'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.NodeType'); +goog.require('goog.dom.TagName'); +goog.require('ol.format.XML'); +goog.require('ol.geom.GeometryCollection'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.xml'); + + + +/** + * @constructor + * @extends {ol.format.XML} + * @todo stability experimental + */ +ol.format.GML = function() { + goog.base(this); +}; +goog.inherits(ol.format.GML, ol.format.XML); + + +/** + * @const + * @private + * @type {Array.} + */ +ol.format.GML.NAMESPACE_URIS_ = [ + 'http://www.opengis.net/gml' +]; + + +/** + * @inheritDoc + */ +ol.format.GML.prototype.readGeometryFromDocument = function(doc) { + var node; + if (doc.nodeType == goog.dom.NodeType.DOCUMENT) { + // TODO intermediate element node - revisit later + node = goog.dom.createElement(goog.dom.TagName.PRE); + node.appendChild(doc.documentElement); + } else { + node = doc; + } + return this.readGeometryFromNode(node); +}; + + +/** + * @inheritDoc + */ +ol.format.GML.prototype.readGeometryFromNode = function(node) { + var objectStack = []; + var geometries = ol.xml.pushParseAndPop( + /** @type {Array.} */ ([]), + ol.format.GML.GEOMETRY_PARSERS_, node, objectStack); + if (!goog.isDef(geometries)) { + return null; + } + if (geometries.length === 0) { + return new ol.geom.GeometryCollection(geometries); + } + return geometries[0]; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.Point|undefined} Point. + */ +ol.format.GML.readPoint_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT); + goog.asserts.assert(node.localName == 'Point'); + var flatCoordinates = + ol.format.GML.readFlatCoordinatesFromNode_(node, objectStack); + if (goog.isDefAndNotNull(flatCoordinates)) { + var point = new ol.geom.Point(null); + goog.asserts.assert(flatCoordinates.length == 3); + point.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); + return point; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.LineString|undefined} LineString. + */ +ol.format.GML.readLineString_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT); + goog.asserts.assert(node.localName == 'LineString'); + var flatCoordinates = + ol.format.GML.readFlatCoordinatesFromNode_(node, objectStack); + if (goog.isDefAndNotNull(flatCoordinates)) { + var lineString = new ol.geom.LineString(null); + lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); + return lineString; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GML.interiorParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT); + goog.asserts.assert(node.localName == 'interior'); + var flatLinearRing = ol.xml.pushParseAndPop( + /** @type {Array.|undefined} */ (undefined), + ol.format.GML.INTERIOR_PARSERS_, node, objectStack); + if (goog.isDef(flatLinearRing)) { + var flatLinearRings = /** @type {Array.>} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(goog.isArray(flatLinearRings)); + goog.asserts.assert(flatLinearRings.length > 0); + flatLinearRings.push(flatLinearRing); + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GML.exteriorParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT); + goog.asserts.assert(node.localName == 'exterior'); + var flatLinearRing = ol.xml.pushParseAndPop( + /** @type {Array.|undefined} */ (undefined), + ol.format.GML.EXTERIOR_PARSERS_, node, objectStack); + if (goog.isDef(flatLinearRing)) { + var flatLinearRings = /** @type {Array.>} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(goog.isArray(flatLinearRings)); + goog.asserts.assert(flatLinearRings.length > 0); + flatLinearRings[0] = flatLinearRing; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.} LinearRing flat coordinates. + */ +ol.format.GML.readFlatLinearRing_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT); + goog.asserts.assert(node.localName == 'LinearRing'); + return /** @type {Array.} */ (ol.xml.pushParseAndPop( + null, ol.format.GML.FLAT_LINEAR_RING_PARSERS_, node, objectStack)); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.Polygon|undefined} Polygon. + */ +ol.format.GML.readLinearRing_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT); + goog.asserts.assert(node.localName == 'LinearRing'); + var flatCoordinates = + ol.format.GML.readFlatCoordinatesFromNode_(node, objectStack); + if (goog.isDef(flatCoordinates)) { + var polygon = new ol.geom.Polygon(null); + polygon.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates, + [flatCoordinates.length]); + return polygon; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.Polygon|undefined} Polygon. + */ +ol.format.GML.readPolygon_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT); + goog.asserts.assert(node.localName == 'Polygon'); + var flatLinearRings = ol.xml.pushParseAndPop( + /** @type {Array.>} */ ([null]), + ol.format.GML.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack); + if (goog.isDefAndNotNull(flatLinearRings) && + !goog.isNull(flatLinearRings[0])) { + var polygon = new ol.geom.Polygon(null); + var flatCoordinates = flatLinearRings[0]; + var ends = [flatCoordinates.length]; + var i, ii; + for (i = 1, ii = flatLinearRings.length; i < ii; ++i) { + goog.array.extend(flatCoordinates, flatLinearRings[i]); + ends.push(flatCoordinates.length); + } + polygon.setFlatCoordinates( + ol.geom.GeometryLayout.XYZ, flatCoordinates, ends); + return polygon; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.} Flat coordinates. + */ +ol.format.GML.readFlatCoordinatesFromNode_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == goog.dom.NodeType.ELEMENT); + return /** @type {Array.} */ (ol.xml.pushParseAndPop(null, + ol.format.GML.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, objectStack)); +}; + + +/** + * @param {Node} node Node. + * @private + * @return {Array.|undefined} Flat coordinates. + */ +ol.format.GML.readFlatPos_ = function(node) { + var s = ol.xml.getAllTextContent(node, false).replace(/^\s*|\s*$/g, ''); + var flatCoordinates = goog.array.map(s.split(/\s+/), parseFloat); + // TODO handle axis order + var len = flatCoordinates.length; + if (len == 2) { + flatCoordinates.push(0); + } + if (len === 0) { + return undefined; + } + return flatCoordinates; +}; + + +/** + * @param {Node} node Node. + * @private + * @return {Array.|undefined} Flat coordinates. + */ +ol.format.GML.readFlatPosList_ = function(node) { + var s = ol.xml.getAllTextContent(node, false).replace(/^\s*|\s*$/g, ''); + var coords = s.split(/\s+/); + // The "dimension" attribute is from the GML 3.0.1 spec. + var dim = parseInt(node.getAttribute('srsDimension') || + node.getAttribute('dimension'), 10) || 2; + var x, y, z; + var flatCoordinates = []; + for (var i = 0, ii = coords.length; i < ii; i += dim) { + x = parseFloat(coords[i]); + y = parseFloat(coords[i + 1]); + z = (dim === 3) ? parseFloat(coords[i + 2]) : 0; + // TODO axis orientation + flatCoordinates.push(x, y, z); + } + return flatCoordinates; +}; + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.GML.GEOMETRY_PARSERS_ = ol.xml.makeParsersNS( + ol.format.GML.NAMESPACE_URIS_, { + 'Point': ol.xml.makeArrayPusher(ol.format.GML.readPoint_), + 'LineString': ol.xml.makeArrayPusher(ol.format.GML.readLineString_), + 'LinearRing' : ol.xml.makeArrayPusher(ol.format.GML.readLinearRing_), + 'Polygon': ol.xml.makeArrayPusher(ol.format.GML.readPolygon_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.GML.GEOMETRY_FLAT_COORDINATES_PARSERS_ = ol.xml.makeParsersNS( + ol.format.GML.NAMESPACE_URIS_, { + 'pos': ol.xml.makeReplacer(ol.format.GML.readFlatPos_), + 'posList': ol.xml.makeReplacer(ol.format.GML.readFlatPosList_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.GML.FLAT_LINEAR_RINGS_PARSERS_ = ol.xml.makeParsersNS( + ol.format.GML.NAMESPACE_URIS_, { + 'interior': ol.format.GML.interiorParser_, + 'exterior': ol.format.GML.exteriorParser_ + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.GML.INTERIOR_PARSERS_ = ol.xml.makeParsersNS( + ol.format.GML.NAMESPACE_URIS_, { + 'LinearRing': ol.xml.makeReplacer(ol.format.GML.readFlatLinearRing_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.GML.EXTERIOR_PARSERS_ = ol.xml.makeParsersNS( + ol.format.GML.NAMESPACE_URIS_, { + 'LinearRing': ol.xml.makeReplacer(ol.format.GML.readFlatLinearRing_) + }); + + +/** + * @const + * @type {Object.>} + * @private + */ +ol.format.GML.FLAT_LINEAR_RING_PARSERS_ = ol.xml.makeParsersNS( + ol.format.GML.NAMESPACE_URIS_, { + 'posList': ol.xml.makeReplacer(ol.format.GML.readFlatPosList_) + }); diff --git a/test/spec/ol/format/gmlformat.test.js b/test/spec/ol/format/gmlformat.test.js new file mode 100644 index 0000000000..7809c56672 --- /dev/null +++ b/test/spec/ol/format/gmlformat.test.js @@ -0,0 +1,80 @@ +goog.provide('ol.test.format.GML'); + +describe('ol.format.GML', function() { + + var format; + beforeEach(function() { + format = new ol.format.GML(); + }); + + describe('#readGeometry', function() { + + describe('point', function() { + + it('can read a point geometry', function() { + var text = + '' + + ' 1 2' + + ''; + var g = format.readGeometry(text); + expect(g).to.be.an(ol.geom.Point); + expect(g.getCoordinates()).to.eql([1, 2, 0]); + }); + + }); + + describe('linestring', function() { + + it('can read a linestring geometry', function() { + var text = + '' + + ' 1 2 3 4' + + ''; + var g = format.readGeometry(text); + expect(g).to.be.an(ol.geom.LineString); + expect(g.getCoordinates()).to.eql([[1, 2, 0], [3, 4, 0]]); + }); + + }); + + describe('polygon', function() { + + it('can read a polygon geometry', function() { + var 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' + + ' ' + + ' ' + + ''; + var g = format.readGeometry(text); + expect(g).to.be.an(ol.geom.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]]]); + }); + + }); + + }); + +}); + + +goog.require('ol.format.GML'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon');