diff --git a/externs/olx.js b/externs/olx.js index c483ea1280..bdae363646 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -1538,7 +1538,8 @@ olx.format; /** * @typedef {{dataProjection: ol.proj.ProjectionLike, - * featureProjection: ol.proj.ProjectionLike}} + * featureProjection: ol.proj.ProjectionLike, + * rightHanded: (boolean|undefined)}} * @api */ olx.format.ReadOptions; @@ -1567,7 +1568,8 @@ olx.format.ReadOptions.prototype.featureProjection; /** * @typedef {{dataProjection: ol.proj.ProjectionLike, - * featureProjection: ol.proj.ProjectionLike}} + * featureProjection: ol.proj.ProjectionLike, + * rightHanded: (boolean|undefined)}} * @api */ olx.format.WriteOptions; @@ -1593,6 +1595,22 @@ olx.format.WriteOptions.prototype.dataProjection; olx.format.WriteOptions.prototype.featureProjection; +/** + * When writing geometries, follow the right-hand rule for linear ring + * orientation. This means that polygons will have counter-clockwise exterior + * rings and clockwise interior rings. By default, coordinates are serialized + * as they are provided at construction. If `true`, the right-hand rule will + * be applied. If `false`, the left-hand rule will be applied (clockwise for + * exterior and counter-clockwise for interior rings). Note that not all + * formats support this. The GeoJSON format does use this property when writing + * geometries. + * + * @type {boolean|undefined} + * @api + */ +olx.format.WriteOptions.prototype.rightHanded; + + /** * @typedef {{defaultDataProjection: ol.proj.ProjectionLike, * geometryName: (string|undefined)}} diff --git a/src/ol/format/featureformat.js b/src/ol/format/featureformat.js index a553dc1a70..d06f7a1b51 100644 --- a/src/ol/format/featureformat.js +++ b/src/ol/format/featureformat.js @@ -68,7 +68,8 @@ ol.format.Feature.prototype.adaptOptions = function(options) { updatedOptions = { featureProjection: options.featureProjection, dataProjection: goog.isDefAndNotNull(options.dataProjection) ? - options.dataProjection : this.defaultDataProjection + options.dataProjection : this.defaultDataProjection, + rightHanded: options.rightHanded }; } return updatedOptions; diff --git a/src/ol/format/geojsonformat.js b/src/ol/format/geojsonformat.js index ffe142ede1..0a0d86689d 100644 --- a/src/ol/format/geojsonformat.js +++ b/src/ol/format/geojsonformat.js @@ -178,7 +178,8 @@ ol.format.GeoJSON.writeGeometry_ = function(geometry, opt_options) { var geometryWriter = ol.format.GeoJSON.GEOMETRY_WRITERS_[geometry.getType()]; goog.asserts.assert(goog.isDef(geometryWriter)); return geometryWriter(/** @type {ol.geom.Geometry} */ ( - ol.format.Feature.transformWithOptions(geometry, true, opt_options))); + ol.format.Feature.transformWithOptions(geometry, true, opt_options)), + opt_options); }; @@ -217,10 +218,11 @@ ol.format.GeoJSON.writeGeometryCollectionGeometry_ = function( /** * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. * @private * @return {GeoJSONGeometry} GeoJSON geometry. */ -ol.format.GeoJSON.writeLineStringGeometry_ = function(geometry) { +ol.format.GeoJSON.writeLineStringGeometry_ = function(geometry, opt_options) { goog.asserts.assertInstanceof(geometry, ol.geom.LineString); return /** @type {GeoJSONGeometry} */ ({ 'type': 'LineString', @@ -231,10 +233,12 @@ ol.format.GeoJSON.writeLineStringGeometry_ = function(geometry) { /** * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. * @private * @return {GeoJSONGeometry} GeoJSON geometry. */ -ol.format.GeoJSON.writeMultiLineStringGeometry_ = function(geometry) { +ol.format.GeoJSON.writeMultiLineStringGeometry_ = + function(geometry, opt_options) { goog.asserts.assertInstanceof(geometry, ol.geom.MultiLineString); goog.asserts.assert( geometry.getType() == ol.geom.GeometryType.MULTI_LINE_STRING); @@ -247,10 +251,11 @@ ol.format.GeoJSON.writeMultiLineStringGeometry_ = function(geometry) { /** * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. * @private * @return {GeoJSONGeometry} GeoJSON geometry. */ -ol.format.GeoJSON.writeMultiPointGeometry_ = function(geometry) { +ol.format.GeoJSON.writeMultiPointGeometry_ = function(geometry, opt_options) { goog.asserts.assertInstanceof(geometry, ol.geom.MultiPoint); return /** @type {GeoJSONGeometry} */ ({ 'type': 'MultiPoint', @@ -261,24 +266,30 @@ ol.format.GeoJSON.writeMultiPointGeometry_ = function(geometry) { /** * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. * @private * @return {GeoJSONGeometry} GeoJSON geometry. */ -ol.format.GeoJSON.writeMultiPolygonGeometry_ = function(geometry) { +ol.format.GeoJSON.writeMultiPolygonGeometry_ = function(geometry, opt_options) { goog.asserts.assertInstanceof(geometry, ol.geom.MultiPolygon); + var right; + if (goog.isDef(opt_options)) { + right = opt_options.rightHanded; + } return /** @type {GeoJSONGeometry} */ ({ 'type': 'MultiPolygon', - 'coordinates': geometry.getCoordinates() + 'coordinates': geometry.getCoordinates(right) }); }; /** * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. * @private * @return {GeoJSONGeometry} GeoJSON geometry. */ -ol.format.GeoJSON.writePointGeometry_ = function(geometry) { +ol.format.GeoJSON.writePointGeometry_ = function(geometry, opt_options) { goog.asserts.assertInstanceof(geometry, ol.geom.Point); return /** @type {GeoJSONGeometry} */ ({ 'type': 'Point', @@ -289,14 +300,19 @@ ol.format.GeoJSON.writePointGeometry_ = function(geometry) { /** * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. * @private * @return {GeoJSONGeometry} GeoJSON geometry. */ -ol.format.GeoJSON.writePolygonGeometry_ = function(geometry) { +ol.format.GeoJSON.writePolygonGeometry_ = function(geometry, opt_options) { goog.asserts.assertInstanceof(geometry, ol.geom.Polygon); + var right; + if (goog.isDef(opt_options)) { + right = opt_options.rightHanded; + } return /** @type {GeoJSONGeometry} */ ({ 'type': 'Polygon', - 'coordinates': geometry.getCoordinates() + 'coordinates': geometry.getCoordinates(right) }); }; @@ -320,7 +336,7 @@ ol.format.GeoJSON.GEOMETRY_READERS_ = { /** * @const * @private - * @type {Object.} + * @type {Object.} */ ol.format.GeoJSON.GEOMETRY_WRITERS_ = { 'Point': ol.format.GeoJSON.writePointGeometry_, diff --git a/src/ol/geom/flat/orientflatgeom.js b/src/ol/geom/flat/orientflatgeom.js index 48c7dd5008..688b6a3f97 100644 --- a/src/ol/geom/flat/orientflatgeom.js +++ b/src/ol/geom/flat/orientflatgeom.js @@ -29,22 +29,34 @@ ol.geom.flat.orient.linearRingIsClockwise = /** + * Determines if linear rings are oriented. By default, left-hand orientation + * is tested (first ring must be clockwise, remaining rings counter-clockwise). + * To test for right-hand orientation, use the `opt_right` argument. + * * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. - * @param {Array.} ends Ends. + * @param {Array.} ends Array of end indexes. * @param {number} stride Stride. - * @return {boolean} `true` if all rings are correctly oriented, `false` - * otherwise. + * @param {boolean=} opt_right Test for right-hand orientation + * (counter-clockwise exterior ring and clockwise interior rings). + * @return {boolean} Rings are correctly oriented. */ ol.geom.flat.orient.linearRingsAreOriented = - function(flatCoordinates, offset, ends, stride) { + function(flatCoordinates, offset, ends, stride, opt_right) { + var right = goog.isDef(opt_right) ? opt_right : false; var i, ii; for (i = 0, ii = ends.length; i < ii; ++i) { var end = ends[i]; var isClockwise = ol.geom.flat.orient.linearRingIsClockwise( flatCoordinates, offset, end, stride); - if (i === 0 ? !isClockwise : isClockwise) { - return false; + if (i === 0) { + if ((right && isClockwise) || (!right && !isClockwise)) { + return false; + } + } else { + if ((right && !isClockwise) || (!right && isClockwise)) { + return false; + } } offset = end; } @@ -53,19 +65,24 @@ ol.geom.flat.orient.linearRingsAreOriented = /** + * Determines if linear rings are oriented. By default, left-hand orientation + * is tested (first ring must be clockwise, remaining rings counter-clockwise). + * To test for right-hand orientation, use the `opt_right` argument. + * * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. - * @param {Array.>} endss Endss. + * @param {Array.>} endss Array of array of end indexes. * @param {number} stride Stride. - * @return {boolean} `true` if all rings are correctly oriented, `false` - * otherwise. + * @param {boolean=} opt_right Test for right-hand orientation + * (counter-clockwise exterior ring and clockwise interior rings). + * @return {boolean} Rings are correctly oriented. */ -ol.geom.flat.linearRingssAreOriented = - function(flatCoordinates, offset, endss, stride) { +ol.geom.flat.orient.linearRingssAreOriented = + function(flatCoordinates, offset, endss, stride, opt_right) { var i, ii; for (i = 0, ii = endss.length; i < ii; ++i) { if (!ol.geom.flat.orient.linearRingsAreOriented( - flatCoordinates, offset, endss[i], stride)) { + flatCoordinates, offset, endss[i], stride, opt_right)) { return false; } } @@ -74,20 +91,29 @@ ol.geom.flat.linearRingssAreOriented = /** + * Orient coordinates in a flat array of linear rings. By default, rings + * are oriented following the left-hand rule (clockwise for exterior and + * counter-clockwise for interior rings). To orient according to the + * right-hand rule, use the `opt_right` argument. + * * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. * @param {Array.} ends Ends. * @param {number} stride Stride. + * @param {boolean=} opt_right Follow the right-hand rule for orientation. * @return {number} End. */ ol.geom.flat.orient.orientLinearRings = - function(flatCoordinates, offset, ends, stride) { + function(flatCoordinates, offset, ends, stride, opt_right) { + var right = goog.isDef(opt_right) ? opt_right : false; var i, ii; for (i = 0, ii = ends.length; i < ii; ++i) { var end = ends[i]; var isClockwise = ol.geom.flat.orient.linearRingIsClockwise( flatCoordinates, offset, end, stride); - var reverse = i === 0 ? !isClockwise : isClockwise; + var reverse = i === 0 ? + (right && isClockwise) || (!right && !isClockwise) : + (right && !isClockwise) || (!right && isClockwise); if (reverse) { ol.geom.flat.reverse.coordinates(flatCoordinates, offset, end, stride); } @@ -98,18 +124,24 @@ ol.geom.flat.orient.orientLinearRings = /** + * Orient coordinates in a flat array of linear rings. By default, rings + * are oriented following the left-hand rule (clockwise for exterior and + * counter-clockwise for interior rings). To orient according to the + * right-hand rule, use the `opt_right` argument. + * * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. - * @param {Array.>} endss Endss. + * @param {Array.>} endss Array of array of end indexes. * @param {number} stride Stride. + * @param {boolean=} opt_right Follow the right-hand rule for orientation. * @return {number} End. */ ol.geom.flat.orient.orientLinearRingss = - function(flatCoordinates, offset, endss, stride) { + function(flatCoordinates, offset, endss, stride, opt_right) { var i, ii; for (i = 0, ii = endss.length; i < ii; ++i) { offset = ol.geom.flat.orient.orientLinearRings( - flatCoordinates, offset, endss[i], stride); + flatCoordinates, offset, endss[i], stride, opt_right); } return offset; }; diff --git a/src/ol/geom/multipolygon.js b/src/ol/geom/multipolygon.js index 3dafa464d6..7655c81d78 100644 --- a/src/ol/geom/multipolygon.js +++ b/src/ol/geom/multipolygon.js @@ -162,12 +162,30 @@ ol.geom.MultiPolygon.prototype.getArea = function() { /** + * Get the coordinate array for this geometry. This array has the structure + * of a GeoJSON coordinate array for multi-polygons. + * + * @param {boolean=} opt_right Orient coordinates according to the right-hand + * rule (counter-clockwise for exterior and clockwise for interior rings). + * If `false`, coordinates will be oriented according to the left-hand rule + * (clockwise for exterior and counter-clockwise for interior rings). + * By default, coordinate orientation will depend on how the geometry was + * constructed. * @return {Array.>>} Coordinates. * @api stable */ -ol.geom.MultiPolygon.prototype.getCoordinates = function() { +ol.geom.MultiPolygon.prototype.getCoordinates = function(opt_right) { + var flatCoordinates; + if (goog.isDef(opt_right)) { + flatCoordinates = this.getOrientedFlatCoordinates().slice(); + ol.geom.flat.orient.orientLinearRingss( + flatCoordinates, 0, this.endss_, this.stride, opt_right); + } else { + flatCoordinates = this.flatCoordinates; + } + return ol.geom.flat.inflate.coordinatesss( - this.flatCoordinates, 0, this.endss_, this.stride); + flatCoordinates, 0, this.endss_, this.stride); }; @@ -213,7 +231,7 @@ ol.geom.MultiPolygon.prototype.getInteriorPoints = function() { ol.geom.MultiPolygon.prototype.getOrientedFlatCoordinates = function() { if (this.orientedRevision_ != this.getRevision()) { var flatCoordinates = this.flatCoordinates; - if (ol.geom.flat.linearRingssAreOriented( + if (ol.geom.flat.orient.linearRingssAreOriented( flatCoordinates, 0, this.endss_, this.stride)) { this.orientedFlatCoordinates_ = flatCoordinates; } else { diff --git a/src/ol/geom/polygon.js b/src/ol/geom/polygon.js index 4eea4150e3..9c8d8ca020 100644 --- a/src/ol/geom/polygon.js +++ b/src/ol/geom/polygon.js @@ -151,12 +151,30 @@ ol.geom.Polygon.prototype.getArea = function() { /** + * Get the coordinate array for this geometry. This array has the structure + * of a GeoJSON coordinate array for polygons. + * + * @param {boolean=} opt_right Orient coordinates according to the right-hand + * rule (counter-clockwise for exterior and clockwise for interior rings). + * If `false`, coordinates will be oriented according to the left-hand rule + * (clockwise for exterior and counter-clockwise for interior rings). + * By default, coordinate orientation will depend on how the geometry was + * constructed. * @return {Array.>} Coordinates. * @api stable */ -ol.geom.Polygon.prototype.getCoordinates = function() { +ol.geom.Polygon.prototype.getCoordinates = function(opt_right) { + var flatCoordinates; + if (goog.isDef(opt_right)) { + flatCoordinates = this.getOrientedFlatCoordinates().slice(); + ol.geom.flat.orient.orientLinearRings( + flatCoordinates, 0, this.ends_, this.stride, opt_right); + } else { + flatCoordinates = this.flatCoordinates; + } + return ol.geom.flat.inflate.coordinatess( - this.flatCoordinates, 0, this.ends_, this.stride); + flatCoordinates, 0, this.ends_, this.stride); }; diff --git a/test/spec/ol/format/geojsonformat.test.js b/test/spec/ol/format/geojsonformat.test.js index bf3356825c..df3c46342a 100644 --- a/test/spec/ol/format/geojsonformat.test.js +++ b/test/spec/ol/format/geojsonformat.test.js @@ -558,6 +558,106 @@ describe('ol.format.GeoJSON', function() { format.readGeometry(geojson).getCoordinates()); }); + it('maintains coordinate order by default', function() { + + var cw = [[-180, -90], [-180, 90], [180, 90], [180, -90], [-180, -90]]; + var ccw = [[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]; + + var right = new ol.geom.Polygon([ccw, cw]); + var rightMulti = new ol.geom.MultiPolygon([[ccw, cw]]); + var left = new ol.geom.Polygon([cw, ccw]); + var leftMulti = new ol.geom.MultiPolygon([[cw, ccw]]); + + var rightObj = { + type: 'Polygon', + coordinates: [ccw, cw] + }; + + var rightMultiObj = { + type: 'MultiPolygon', + coordinates: [[ccw, cw]] + }; + + var leftObj = { + type: 'Polygon', + coordinates: [cw, ccw] + }; + + var leftMultiObj = { + type: 'MultiPolygon', + coordinates: [[cw, ccw]] + }; + + expect(JSON.parse(format.writeGeometry(right))).to.eql(rightObj); + expect( + JSON.parse(format.writeGeometry(rightMulti))).to.eql(rightMultiObj); + expect(JSON.parse(format.writeGeometry(left))).to.eql(leftObj); + expect(JSON.parse(format.writeGeometry(leftMulti))).to.eql(leftMultiObj); + + }); + + it('allows serializing following the right-hand rule', function() { + + var cw = [[-180, -90], [-180, 90], [180, 90], [180, -90], [-180, -90]]; + var ccw = [[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]; + var right = new ol.geom.Polygon([ccw, cw]); + var rightMulti = new ol.geom.MultiPolygon([[ccw, cw]]); + var left = new ol.geom.Polygon([cw, ccw]); + var leftMulti = new ol.geom.MultiPolygon([[cw, ccw]]); + + var rightObj = { + type: 'Polygon', + coordinates: [ccw, cw] + }; + + var rightMultiObj = { + type: 'MultiPolygon', + coordinates: [[ccw, cw]] + }; + + var json = format.writeGeometry(right, {rightHanded: true}); + expect(JSON.parse(json)).to.eql(rightObj); + json = format.writeGeometry(rightMulti, {rightHanded: true}); + expect(JSON.parse(json)).to.eql(rightMultiObj); + + json = format.writeGeometry(left, {rightHanded: true}); + expect(JSON.parse(json)).to.eql(rightObj); + json = format.writeGeometry(leftMulti, {rightHanded: true}); + expect(JSON.parse(json)).to.eql(rightMultiObj); + + }); + + it('allows serializing following the left-hand rule', function() { + + var cw = [[-180, -90], [-180, 90], [180, 90], [180, -90], [-180, -90]]; + var ccw = [[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]; + var right = new ol.geom.Polygon([ccw, cw]); + var rightMulti = new ol.geom.MultiPolygon([[ccw, cw]]); + var left = new ol.geom.Polygon([cw, ccw]); + var leftMulti = new ol.geom.MultiPolygon([[cw, ccw]]); + + var leftObj = { + type: 'Polygon', + coordinates: [cw, ccw] + }; + + var leftMultiObj = { + type: 'MultiPolygon', + coordinates: [[cw, ccw]] + }; + + var json = format.writeGeometry(right, {rightHanded: false}); + expect(JSON.parse(json)).to.eql(leftObj); + json = format.writeGeometry(rightMulti, {rightHanded: false}); + expect(JSON.parse(json)).to.eql(leftMultiObj); + + json = format.writeGeometry(left, {rightHanded: false}); + expect(JSON.parse(json)).to.eql(leftObj); + json = format.writeGeometry(leftMulti, {rightHanded: false}); + expect(JSON.parse(json)).to.eql(leftMultiObj); + + }); + it('encodes geometry collection', function() { var collection = new ol.geom.GeometryCollection([ new ol.geom.Point([10, 20]), @@ -611,6 +711,7 @@ goog.require('ol.geom.Circle'); goog.require('ol.geom.GeometryCollection'); goog.require('ol.geom.LineString'); goog.require('ol.geom.LinearRing'); +goog.require('ol.geom.MultiPolygon'); goog.require('ol.geom.Point'); goog.require('ol.geom.Polygon'); goog.require('ol.proj'); diff --git a/test/spec/ol/geom/flat/orientflatgeom.test.js b/test/spec/ol/geom/flat/orientflatgeom.test.js index c8842eb5b2..f113eee8f4 100644 --- a/test/spec/ol/geom/flat/orientflatgeom.test.js +++ b/test/spec/ol/geom/flat/orientflatgeom.test.js @@ -2,7 +2,7 @@ goog.provide('ol.test.geom.flat.orient'); describe('ol.geom.flat.orient', function() { - describe('ol.geom.flat.orient.linearRingIsClockwise', function() { + describe('ol.geom.flat.orient.linearRingIsClockwise()', function() { it('identifies clockwise rings', function() { var flatCoordinates = [0, 1, 1, 4, 4, 3, 3, 0]; @@ -20,6 +20,143 @@ describe('ol.geom.flat.orient', function() { }); + describe('ol.geom.flat.orient.linearRingsAreOriented()', function() { + var oriented = ol.geom.flat.orient.linearRingsAreOriented; + + var rightCoords = [ + -180, -90, 180, -90, 180, 90, -180, 90, -180, -90, + -100, -45, -100, 45, 100, 45, 100, -45, -100, -45 + ]; + + var leftCoords = [ + -180, -90, -180, 90, 180, 90, 180, -90, -180, -90, + -100, -45, 100, -45, 100, 45, -100, 45, -100, -45 + ]; + + var ends = [10, 20]; + + it('checks for left-hand orientation by default', function() { + expect(oriented(rightCoords, 0, ends, 2)).to.be(false); + expect(oriented(leftCoords, 0, ends, 2)).to.be(true); + }); + + it('can check for right-hand orientation', function() { + expect(oriented(rightCoords, 0, ends, 2, true)).to.be(true); + expect(oriented(leftCoords, 0, ends, 2, true)).to.be(false); + }); + + }); + + describe('ol.geom.flat.orient.linearRingssAreOriented()', function() { + var oriented = ol.geom.flat.orient.linearRingssAreOriented; + + var rightCoords = [ + -180, -90, 180, -90, 180, 90, -180, 90, -180, -90, + -100, -45, -100, 45, 100, 45, 100, -45, -100, -45, + -180, -90, 180, -90, 180, 90, -180, 90, -180, -90, + -100, -45, -100, 45, 100, 45, 100, -45, -100, -45 + ]; + + var leftCoords = [ + -180, -90, -180, 90, 180, 90, 180, -90, -180, -90, + -100, -45, 100, -45, 100, 45, -100, 45, -100, -45, + -180, -90, -180, 90, 180, 90, 180, -90, -180, -90, + -100, -45, 100, -45, 100, 45, -100, 45, -100, -45 + ]; + + var ends = [[10, 20], [30, 40]]; + + it('checks for left-hand orientation by default', function() { + expect(oriented(rightCoords, 0, ends, 2)).to.be(false); + expect(oriented(leftCoords, 0, ends, 2)).to.be(true); + }); + + it('can check for right-hand orientation', function() { + expect(oriented(rightCoords, 0, ends, 2, true)).to.be(true); + expect(oriented(leftCoords, 0, ends, 2, true)).to.be(false); + }); + + }); + + describe('ol.geom.flat.orient.orientLinearRings()', function() { + var orient = ol.geom.flat.orient.orientLinearRings; + + var rightCoords = [ + -180, -90, 180, -90, 180, 90, -180, 90, -180, -90, + -100, -45, -100, 45, 100, 45, 100, -45, -100, -45 + ]; + + var leftCoords = [ + -180, -90, -180, 90, 180, 90, 180, -90, -180, -90, + -100, -45, 100, -45, 100, 45, -100, 45, -100, -45 + ]; + + var ends = [10, 20]; + + it('orients using the left-hand rule by default', function() { + var rightClone = rightCoords.slice(); + orient(rightClone, 0, ends, 2); + expect(rightClone).to.eql(leftCoords); + + var leftClone = leftCoords.slice(); + orient(leftClone, 0, ends, 2); + expect(leftClone).to.eql(leftCoords); + }); + + it('can orient using the right-hand rule', function() { + var rightClone = rightCoords.slice(); + orient(rightClone, 0, ends, 2, true); + expect(rightClone).to.eql(rightCoords); + + var leftClone = leftCoords.slice(); + orient(leftClone, 0, ends, 2, true); + expect(leftClone).to.eql(rightCoords); + }); + + }); + + describe('ol.geom.flat.orient.orientLinearRingss()', function() { + var orient = ol.geom.flat.orient.orientLinearRingss; + + var rightCoords = [ + -180, -90, 180, -90, 180, 90, -180, 90, -180, -90, + -100, -45, -100, 45, 100, 45, 100, -45, -100, -45, + -180, -90, 180, -90, 180, 90, -180, 90, -180, -90, + -100, -45, -100, 45, 100, 45, 100, -45, -100, -45 + ]; + + var leftCoords = [ + -180, -90, -180, 90, 180, 90, 180, -90, -180, -90, + -100, -45, 100, -45, 100, 45, -100, 45, -100, -45, + -180, -90, -180, 90, 180, 90, 180, -90, -180, -90, + -100, -45, 100, -45, 100, 45, -100, 45, -100, -45 + ]; + + var ends = [[10, 20], [30, 40]]; + + it('orients using the left-hand rule by default', function() { + var rightClone = rightCoords.slice(); + orient(rightClone, 0, ends, 2); + expect(rightClone).to.eql(leftCoords); + + var leftClone = leftCoords.slice(); + orient(leftClone, 0, ends, 2); + expect(leftClone).to.eql(leftCoords); + }); + + it('can orient using the right-hand rule', function() { + var rightClone = rightCoords.slice(); + orient(rightClone, 0, ends, 2, true); + expect(rightClone).to.eql(rightCoords); + + var leftClone = leftCoords.slice(); + orient(leftClone, 0, ends, 2, true); + expect(leftClone).to.eql(rightCoords); + }); + + }); + + }); goog.require('ol.geom.flat.orient'); diff --git a/test/spec/ol/geom/multipolygon.test.js b/test/spec/ol/geom/multipolygon.test.js index e04f59962f..64ca5e494b 100644 --- a/test/spec/ol/geom/multipolygon.test.js +++ b/test/spec/ol/geom/multipolygon.test.js @@ -89,6 +89,32 @@ describe('ol.geom.MultiPolygon', function() { [[[3, 0], [4, 1], [5, 2], [5, 0]]]); }); + describe('#getCoordinates()', function() { + + var cw = [[-180, -90], [-180, 90], [180, 90], [180, -90], [-180, -90]]; + var cw2 = [[-140, -60], [-140, 60], [140, 60], [140, -60], [-140, -60]]; + var ccw = [[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]; + var ccw2 = [[-140, -60], [140, -60], [140, 60], [-140, 60], [-140, -60]]; + var right = new ol.geom.MultiPolygon([[ccw, cw], [ccw2, cw2]]); + var left = new ol.geom.MultiPolygon([[cw, ccw], [cw2, ccw2]]); + + it('returns coordinates as they were constructed', function() { + expect(right.getCoordinates()).to.eql([[ccw, cw], [ccw2, cw2]]); + expect(left.getCoordinates()).to.eql([[cw, ccw], [cw2, ccw2]]); + }); + + it('can return coordinates with right-hand orientation', function() { + expect(right.getCoordinates(true)).to.eql([[ccw, cw], [ccw2, cw2]]); + expect(left.getCoordinates(true)).to.eql([[ccw, cw], [ccw2, cw2]]); + }); + + it('can return coordinates with left-hand orientation', function() { + expect(right.getCoordinates(false)).to.eql([[cw, ccw], [cw2, ccw2]]); + expect(left.getCoordinates(false)).to.eql([[cw, ccw], [cw2, ccw2]]); + }); + + }); + describe('#getSimplifiedGeometry', function() { it('returns the expected result', function() { diff --git a/test/spec/ol/geom/polygon.test.js b/test/spec/ol/geom/polygon.test.js index 163d36ce87..5ca233f260 100644 --- a/test/spec/ol/geom/polygon.test.js +++ b/test/spec/ol/geom/polygon.test.js @@ -120,6 +120,30 @@ describe('ol.geom.Polygon', function() { expect(polygon.containsCoordinate(insideInner)).to.be(false); }); + describe('#getCoordinates()', function() { + + var cw = [[-180, -90], [-180, 90], [180, 90], [180, -90], [-180, -90]]; + var ccw = [[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]; + var right = new ol.geom.Polygon([ccw, cw]); + var left = new ol.geom.Polygon([cw, ccw]); + + it('returns coordinates as they were constructed', function() { + expect(right.getCoordinates()).to.eql([ccw, cw]); + expect(left.getCoordinates()).to.eql([cw, ccw]); + }); + + it('can return coordinates with right-hand orientation', function() { + expect(right.getCoordinates(true)).to.eql([ccw, cw]); + expect(left.getCoordinates(true)).to.eql([ccw, cw]); + }); + + it('can return coordinates with left-hand orientation', function() { + expect(right.getCoordinates(false)).to.eql([cw, ccw]); + expect(left.getCoordinates(false)).to.eql([cw, ccw]); + }); + + }); + describe('#getOrientedFlatCoordinates', function() { it('reverses the outer ring if necessary', function() {