diff --git a/externs/olx.js b/externs/olx.js index 71b5ed8044..0510ba77a5 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -2299,6 +2299,7 @@ olx.format.WFSWriteGetFeatureOptions.prototype.resultType; * featureType: string, * srsName: (string|undefined), * handle: (string|undefined), + * is3D: (boolean|undefined), * nativeElements: Array., * gmlOptions: (olx.format.GMLOptions|undefined), * version: (string|undefined)}} @@ -2347,6 +2348,15 @@ olx.format.WFSWriteTransactionOptions.prototype.srsName; olx.format.WFSWriteTransactionOptions.prototype.handle; +/** + * Must be set to true if the transaction is for a 3D layer. This will allow + * the Z coordinate to be included in the transaction. + * @type {boolean|undefined} + * @api + */ +olx.format.WFSWriteTransactionOptions.prototype.is3D; + + /** * Native elements. Currently not supported. * @type {Array.} diff --git a/src/ol/format/gml2.js b/src/ol/format/gml2.js index 8c1fb481fb..134e208353 100644 --- a/src/ol/format/gml2.js +++ b/src/ol/format/gml2.js @@ -352,6 +352,7 @@ ol.format.GML2.prototype.createCoordinatesNode_ = function(namespaceURI) { */ ol.format.GML2.prototype.writeCoordinates_ = function(node, value, objectStack) { var context = objectStack[objectStack.length - 1]; + var is3D = context['is3D']; var srsName = context['srsName']; // only 2d for simple features profile var points = value.getCoordinates(); @@ -360,7 +361,7 @@ ol.format.GML2.prototype.writeCoordinates_ = function(node, value, objectStack) var point; for (var i = 0; i < len; ++i) { point = points[i]; - parts[i] = this.getCoords_(point, srsName); + parts[i] = this.getCoords_(point, srsName, is3D); } ol.format.XSD.writeStringTextNode(node, parts.join(' ')); }; @@ -388,6 +389,7 @@ ol.format.GML2.prototype.writeCurveSegments_ = function(node, line, objectStack) */ ol.format.GML2.prototype.writeSurfaceOrPolygon_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; + var is3D = context['is3D']; var srsName = context['srsName']; if (node.nodeName !== 'PolygonPatch' && srsName) { node.setAttribute('srsName', srsName); @@ -395,7 +397,7 @@ ol.format.GML2.prototype.writeSurfaceOrPolygon_ = function(node, geometry, objec if (node.nodeName === 'Polygon' || node.nodeName === 'PolygonPatch') { var rings = geometry.getLinearRings(); ol.xml.pushSerializeAndPop( - {node: node, srsName: srsName}, + {node: node, is3D: is3D, srsName: srsName}, ol.format.GML2.RING_SERIALIZERS_, this.RING_NODE_FACTORY_, rings, objectStack, undefined, this); @@ -456,17 +458,23 @@ ol.format.GML2.prototype.writeRing_ = function(node, ring, objectStack) { /** * @param {Array.} point Point geometry. * @param {string=} opt_srsName Optional srsName + * @param {boolean=} opt_is3D whether the geometry is 3D or not. * @return {string} The coords string. * @private */ -ol.format.GML2.prototype.getCoords_ = function(point, opt_srsName) { +ol.format.GML2.prototype.getCoords_ = function(point, opt_srsName, opt_is3D) { var axisOrientation = 'enu'; if (opt_srsName) { axisOrientation = ol.proj.get(opt_srsName).getAxisOrientation(); } - return ((axisOrientation.substr(0, 2) === 'en') ? + var coords = ((axisOrientation.substr(0, 2) === 'en') ? point[0] + ',' + point[1] : point[1] + ',' + point[0]); + if (opt_is3D) { + coords += ',' + point[2]; + } + + return coords; }; @@ -478,13 +486,14 @@ ol.format.GML2.prototype.getCoords_ = function(point, opt_srsName) { */ ol.format.GML2.prototype.writeMultiCurveOrLineString_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; + var is3D = context['is3D']; var srsName = context['srsName']; var curve = context['curve']; if (srsName) { node.setAttribute('srsName', srsName); } var lines = geometry.getLineStrings(); - ol.xml.pushSerializeAndPop({node: node, srsName: srsName, curve: curve}, + ol.xml.pushSerializeAndPop({node: node, is3D: is3D, srsName: srsName, curve: curve}, ol.format.GML2.LINESTRINGORCURVEMEMBER_SERIALIZERS_, this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, lines, objectStack, undefined, this); @@ -499,6 +508,7 @@ ol.format.GML2.prototype.writeMultiCurveOrLineString_ = function(node, geometry, */ ol.format.GML2.prototype.writePoint_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; + var is3D = context['is3D']; var srsName = context['srsName']; if (srsName) { node.setAttribute('srsName', srsName); @@ -506,7 +516,7 @@ ol.format.GML2.prototype.writePoint_ = function(node, geometry, objectStack) { var coordinates = this.createCoordinatesNode_(node.namespaceURI); node.appendChild(coordinates); var point = geometry.getCoordinates(); - var coord = this.getCoords_(point, srsName); + var coord = this.getCoords_(point, srsName, is3D); ol.format.XSD.writeStringTextNode(coordinates, coord); }; @@ -520,12 +530,13 @@ ol.format.GML2.prototype.writePoint_ = function(node, geometry, objectStack) { ol.format.GML2.prototype.writeMultiPoint_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; + var is3D = context['is3D']; var srsName = context['srsName']; if (srsName) { node.setAttribute('srsName', srsName); } var points = geometry.getPoints(); - ol.xml.pushSerializeAndPop({node: node, srsName: srsName}, + ol.xml.pushSerializeAndPop({node: node, is3D: is3D, srsName: srsName}, ol.format.GML2.POINTMEMBER_SERIALIZERS_, ol.xml.makeSimpleNodeFactory('pointMember'), points, objectStack, undefined, this); @@ -586,13 +597,14 @@ ol.format.GML2.prototype.writeLinearRing_ = function(node, geometry, objectStack */ ol.format.GML2.prototype.writeMultiSurfaceOrPolygon_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; + var is3D = context['is3D']; var srsName = context['srsName']; var surface = context['surface']; if (srsName) { node.setAttribute('srsName', srsName); } var polygons = geometry.getPolygons(); - ol.xml.pushSerializeAndPop({node: node, srsName: srsName, surface: surface}, + ol.xml.pushSerializeAndPop({node: node, is3D: is3D, srsName: srsName, surface: surface}, ol.format.GML2.SURFACEORPOLYGONMEMBER_SERIALIZERS_, this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, polygons, objectStack, undefined, this); diff --git a/src/ol/format/gml3.js b/src/ol/format/gml3.js index 2036d94c3e..be9a256d23 100644 --- a/src/ol/format/gml3.js +++ b/src/ol/format/gml3.js @@ -565,6 +565,7 @@ ol.format.GML3.prototype.SEGMENTS_PARSERS_ = { */ ol.format.GML3.prototype.writePos_ = function(node, value, objectStack) { var context = objectStack[objectStack.length - 1]; + var is3D = context['is3D']; var srsName = context['srsName']; var axisOrientation = 'enu'; if (srsName) { @@ -578,6 +579,9 @@ ol.format.GML3.prototype.writePos_ = function(node, value, objectStack) { } else { coords = (point[1] + ' ' + point[0]); } + if (is3D) { + coords += ' ' + point[2]; + } ol.format.XSD.writeStringTextNode(node, coords); }; @@ -585,17 +589,23 @@ ol.format.GML3.prototype.writePos_ = function(node, value, objectStack) { /** * @param {Array.} point Point geometry. * @param {string=} opt_srsName Optional srsName + * @param {boolean=} opt_is3D whether the geometry is 3D or not. * @return {string} The coords string. * @private */ -ol.format.GML3.prototype.getCoords_ = function(point, opt_srsName) { +ol.format.GML3.prototype.getCoords_ = function(point, opt_srsName, opt_is3D) { var axisOrientation = 'enu'; if (opt_srsName) { axisOrientation = ol.proj.get(opt_srsName).getAxisOrientation(); } - return ((axisOrientation.substr(0, 2) === 'en') ? + var coords = ((axisOrientation.substr(0, 2) === 'en') ? point[0] + ' ' + point[1] : point[1] + ' ' + point[0]); + if (opt_is3D) { + coords += ' ' + point[2]; + } + + return coords; }; @@ -607,6 +617,7 @@ ol.format.GML3.prototype.getCoords_ = function(point, opt_srsName) { */ ol.format.GML3.prototype.writePosList_ = function(node, value, objectStack) { var context = objectStack[objectStack.length - 1]; + var is3D = context['is3D']; var srsName = context['srsName']; // only 2d for simple features profile var points = value.getCoordinates(); @@ -615,7 +626,7 @@ ol.format.GML3.prototype.writePosList_ = function(node, value, objectStack) { var point; for (var i = 0; i < len; ++i) { point = points[i]; - parts[i] = this.getCoords_(point, srsName); + parts[i] = this.getCoords_(point, srsName, is3D); } ol.format.XSD.writeStringTextNode(node, parts.join(' ')); }; @@ -717,6 +728,7 @@ ol.format.GML3.prototype.RING_NODE_FACTORY_ = function(value, objectStack, opt_n */ ol.format.GML3.prototype.writeSurfaceOrPolygon_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; + var is3D = context['is3D']; var srsName = context['srsName']; if (node.nodeName !== 'PolygonPatch' && srsName) { node.setAttribute('srsName', srsName); @@ -724,7 +736,7 @@ ol.format.GML3.prototype.writeSurfaceOrPolygon_ = function(node, geometry, objec if (node.nodeName === 'Polygon' || node.nodeName === 'PolygonPatch') { var rings = geometry.getLinearRings(); ol.xml.pushSerializeAndPop( - {node: node, srsName: srsName}, + {node: node, is3D: is3D, srsName: srsName}, ol.format.GML3.RING_SERIALIZERS_, this.RING_NODE_FACTORY_, rings, objectStack, undefined, this); @@ -771,13 +783,14 @@ ol.format.GML3.prototype.writeCurveOrLineString_ = function(node, geometry, obje */ ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; + var is3D = context['is3D']; var srsName = context['srsName']; var surface = context['surface']; if (srsName) { node.setAttribute('srsName', srsName); } var polygons = geometry.getPolygons(); - ol.xml.pushSerializeAndPop({node: node, srsName: srsName, surface: surface}, + ol.xml.pushSerializeAndPop({node: node, is3D: is3D, srsName: srsName, surface: surface}, ol.format.GML3.SURFACEORPOLYGONMEMBER_SERIALIZERS_, this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, polygons, objectStack, undefined, this); @@ -794,11 +807,12 @@ ol.format.GML3.prototype.writeMultiPoint_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; var srsName = context['srsName']; + var is3D = context['is3D']; if (srsName) { node.setAttribute('srsName', srsName); } var points = geometry.getPoints(); - ol.xml.pushSerializeAndPop({node: node, srsName: srsName}, + ol.xml.pushSerializeAndPop({node: node, is3D: is3D, srsName: srsName}, ol.format.GML3.POINTMEMBER_SERIALIZERS_, ol.xml.makeSimpleNodeFactory('pointMember'), points, objectStack, undefined, this); @@ -813,13 +827,14 @@ ol.format.GML3.prototype.writeMultiPoint_ = function(node, geometry, */ ol.format.GML3.prototype.writeMultiCurveOrLineString_ = function(node, geometry, objectStack) { var context = objectStack[objectStack.length - 1]; + var is3D = context['is3D']; var srsName = context['srsName']; var curve = context['curve']; if (srsName) { node.setAttribute('srsName', srsName); } var lines = geometry.getLineStrings(); - ol.xml.pushSerializeAndPop({node: node, srsName: srsName, curve: curve}, + ol.xml.pushSerializeAndPop({node: node, is3D: is3D, srsName: srsName, curve: curve}, ol.format.GML3.LINESTRINGORCURVEMEMBER_SERIALIZERS_, this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, lines, objectStack, undefined, this); @@ -1168,7 +1183,7 @@ ol.format.GML3.prototype.GEOMETRY_NODE_FACTORY_ = function(value, objectStack, o ol.format.GML3.prototype.writeGeometryNode = function(geometry, opt_options) { opt_options = this.adaptOptions(opt_options); var geom = ol.xml.createElementNS('http://www.opengis.net/gml', 'geom'); - var context = {node: geom, srsName: this.srsName, + var context = {node: geom, is3D: this.is3D, srsName: this.srsName, curve: this.curve_, surface: this.surface_, multiSurface: this.multiSurface_, multiCurve: this.multiCurve_}; if (opt_options) { @@ -1208,6 +1223,7 @@ ol.format.GML3.prototype.writeFeaturesNode = function(features, opt_options) { 'xsi:schemaLocation', this.schemaLocation); var context = { srsName: this.srsName, + is3D: this.is3D, curve: this.curve_, surface: this.surface_, multiSurface: this.multiSurface_, diff --git a/src/ol/format/wfs.js b/src/ol/format/wfs.js index 5b0b19c0a5..d666940b1a 100644 --- a/src/ol/format/wfs.js +++ b/src/ol/format/wfs.js @@ -469,7 +469,7 @@ ol.format.WFS.writeUpdate_ = function(node, feature, objectStack) { } ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ ( {'gmlVersion': context['gmlVersion'], node: node, - 'srsName': context['srsName']}), + 'is3D': context['is3D'], 'srsName': context['srsName']}), ol.format.WFS.TRANSACTION_SERIALIZERS_, ol.xml.makeSimpleNodeFactory('Property'), values, objectStack); @@ -934,7 +934,7 @@ ol.format.WFS.prototype.writeTransaction = function(inserts, updates, deletes, if (inserts) { obj = {node: node, 'featureNS': options.featureNS, 'featureType': options.featureType, 'featurePrefix': options.featurePrefix, - 'gmlVersion': gmlVersion, 'srsName': options.srsName}; + 'gmlVersion': gmlVersion, 'is3D': options.is3D, 'srsName': options.srsName}; ol.obj.assign(obj, baseObj); ol.xml.pushSerializeAndPop(obj, ol.format.WFS.TRANSACTION_SERIALIZERS_, @@ -944,7 +944,7 @@ ol.format.WFS.prototype.writeTransaction = function(inserts, updates, deletes, if (updates) { obj = {node: node, 'featureNS': options.featureNS, 'featureType': options.featureType, 'featurePrefix': options.featurePrefix, - 'gmlVersion': gmlVersion, 'srsName': options.srsName}; + 'gmlVersion': gmlVersion, 'is3D': options.is3D, 'srsName': options.srsName}; ol.obj.assign(obj, baseObj); ol.xml.pushSerializeAndPop(obj, ol.format.WFS.TRANSACTION_SERIALIZERS_, diff --git a/test/spec/ol/format/wfs.test.js b/test/spec/ol/format/wfs.test.js index 0d9fe3ebc5..5c2f15f45f 100644 --- a/test/spec/ol/format/wfs.test.js +++ b/test/spec/ol/format/wfs.test.js @@ -880,6 +880,86 @@ describe('ol.format.WFS', function() { }); }); + describe('when writing out a transaction request', function() { + var text; + var filename = 'spec/ol/format/wfs/TransactionMultiVersion100_3D.xml'; + before(function(done) { + afterLoadText(filename, function(xml) { + text = xml; + done(); + }); + }); + + it('handles 3D in WFS 1.0.0', function() { + var format = new ol.format.WFS(); + var insertFeature = new ol.Feature({ + the_geom: new ol.geom.LineString([[1.1, 2, 4], [3, 4.2, 5]]), + foo: 'bar', + nul: null + }); + insertFeature.setGeometryName('the_geom'); + var inserts = [insertFeature]; + var updateFeature = new ol.Feature({ + the_geom: new ol.geom.LineString([[1.1, 2, 6], [3, 4.2, 7]]), + foo: 'bar', + // null value gets Property element with no Value + nul: null, + // undefined value means don't create a Property element + unwritten: undefined + }); + updateFeature.setId('fid.42'); + var updates = [updateFeature]; + + var serialized = format.writeTransaction(inserts, updates, null, { + featureNS: 'http://www.openplans.org/topp', + featureType: 'states', + featurePrefix: 'topp', + is3D: true, + version: '1.0.0' + }); + + expect(serialized).to.xmleql(ol.xml.parse(text)); + }); + }); + + describe('when writing out a Transaction request', function() { + var text; + before(function(done) { + afterLoadText('spec/ol/format/wfs/TransactionMulti_3D.xml', function(xml) { + text = xml; + done(); + }); + }); + + it('handles 3D in WFS 1.1.0', function() { + var format = new ol.format.WFS(); + var insertFeature = new ol.Feature({ + the_geom: new ol.geom.MultiPoint([[1, 2, 3]]), + foo: 'bar', + nul: null + }); + insertFeature.setGeometryName('the_geom'); + var inserts = [insertFeature]; + var updateFeature = new ol.Feature({ + the_geom: new ol.geom.MultiPoint([[1, 2, 3]]), + foo: 'bar', + // null value gets Property element with no Value + nul: null, + // undefined value means don't create a Property element + unwritten: undefined + }); + updateFeature.setId('fid.42'); + var updates = [updateFeature]; + + var serialized = format.writeTransaction(inserts, updates, null, { + featureNS: 'http://www.openplans.org/topp', + featureType: 'states', + is3D: true, + featurePrefix: 'topp' + }); + expect(serialized).to.xmleql(ol.xml.parse(text)); + }); + }); describe('when writing out a GetFeature request', function() { var text; diff --git a/test/spec/ol/format/wfs/TransactionMultiVersion100_3D.xml b/test/spec/ol/format/wfs/TransactionMultiVersion100_3D.xml new file mode 100644 index 0000000000..e3483a30c8 --- /dev/null +++ b/test/spec/ol/format/wfs/TransactionMultiVersion100_3D.xml @@ -0,0 +1,32 @@ + + + + + + 1.1,2,4 3,4.2,5 + + + bar + + + + + the_geom + + + 1.1,2,6 3,4.2,7 + + + + + foo + bar + + + nul + + + + + + diff --git a/test/spec/ol/format/wfs/TransactionMulti_3D.xml b/test/spec/ol/format/wfs/TransactionMulti_3D.xml new file mode 100644 index 0000000000..cb0ab3dd31 --- /dev/null +++ b/test/spec/ol/format/wfs/TransactionMulti_3D.xml @@ -0,0 +1,40 @@ + + + + + + + + 1 2 3 + + + + + bar + + + + + the_geom + + + + + 1 2 3 + + + + + + + foo + bar + + + nul + + + + + +