From 795cee876e42a5604a30805962f04c8b3e7ca46d Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 4 Aug 2016 10:23:00 -0600 Subject: [PATCH 1/2] Add a method to scale geometries --- src/ol/geom/flat/transformflatgeom.js | 33 +++++++++++++++++++++++ src/ol/geom/geometry.js | 14 ++++++++++ src/ol/geom/geometrycollection.js | 13 +++++++++ src/ol/geom/simplegeometry.js | 24 +++++++++++++++++ test/spec/ol/geom/linestring.test.js | 25 +++++++++++++++++ test/spec/ol/geom/multilinestring.test.js | 25 +++++++++++++++++ test/spec/ol/geom/multipoint.test.js | 25 +++++++++++++++++ test/spec/ol/geom/point.test.js | 25 +++++++++++++++++ 8 files changed, 184 insertions(+) diff --git a/src/ol/geom/flat/transformflatgeom.js b/src/ol/geom/flat/transformflatgeom.js index ae097afe57..12b2b203e3 100644 --- a/src/ol/geom/flat/transformflatgeom.js +++ b/src/ol/geom/flat/transformflatgeom.js @@ -60,6 +60,39 @@ ol.geom.flat.transform.rotate = function(flatCoordinates, offset, end, stride, a }; +/** + * Scale the coordinates. + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} sx Scale factor in the x-direction. + * @param {number} sy Scale factor in the y-direction. + * @param {Array.} anchor Scale anchor point. + * @param {Array.=} opt_dest Destination. + * @return {Array.} Transformed coordinates. + */ +ol.geom.flat.transform.scale = function(flatCoordinates, offset, end, stride, sx, sy, anchor, opt_dest) { + var dest = opt_dest ? opt_dest : []; + var anchorX = anchor[0]; + var anchorY = anchor[1]; + var i = 0; + for (var j = offset; j < end; j += stride) { + var deltaX = flatCoordinates[j] - anchorX; + var deltaY = flatCoordinates[j + 1] - anchorY; + dest[i++] = anchorX + sx * deltaX; + dest[i++] = anchorY + sy * deltaY; + for (var k = j + 2; k < j + stride; ++k) { + dest[i++] = flatCoordinates[k]; + } + } + if (opt_dest && dest.length != i) { + dest.length = i; + } + return dest; +}; + + /** * @param {Array.} flatCoordinates Flat coordinates. * @param {number} offset Offset. diff --git a/src/ol/geom/geometry.js b/src/ol/geom/geometry.js index 69e9914477..344bb07036 100644 --- a/src/ol/geom/geometry.js +++ b/src/ol/geom/geometry.js @@ -180,6 +180,20 @@ ol.geom.Geometry.prototype.getExtent = function(opt_extent) { ol.geom.Geometry.prototype.rotate = function(angle, anchor) {}; +/** + * Scale the geometry (with an optional origin). This modifies the geometry + * coordinates in place. + * @abstract + * @param {number} sx The scaling factor in the x-direction. + * @param {number=} opt_sy The scaling factor in the y-direction (defaults to + * sx). + * @param {ol.Coordinate=} opt_anchor The scale origin (defaults to the center + * of the geometry extent). + * @api + */ +ol.geom.Geometry.prototype.scale = function(sx, opt_sy, opt_anchor) {}; + + /** * Create a simplified version of this geometry. For linestrings, this uses * the the {@link diff --git a/src/ol/geom/geometrycollection.js b/src/ol/geom/geometrycollection.js index 793141b373..0c6a6d52ce 100644 --- a/src/ol/geom/geometrycollection.js +++ b/src/ol/geom/geometrycollection.js @@ -244,6 +244,19 @@ ol.geom.GeometryCollection.prototype.rotate = function(angle, anchor) { }; +/** + * @inheritDoc + * @api + */ +ol.geom.GeometryCollection.prototype.scale = function(sx, opt_sy, opt_anchor) { + var geometries = this.geometries_; + for (var i = 0, ii = geometries.length; i < ii; ++i) { + geometries[i].scale(sx, opt_sy, opt_anchor); + } + this.changed(); +}; + + /** * Set the geometries that make up this geometry collection. * @param {Array.} geometries Geometries. diff --git a/src/ol/geom/simplegeometry.js b/src/ol/geom/simplegeometry.js index 7324386404..9089173d4d 100644 --- a/src/ol/geom/simplegeometry.js +++ b/src/ol/geom/simplegeometry.js @@ -278,6 +278,30 @@ ol.geom.SimpleGeometry.prototype.rotate = function(angle, anchor) { }; +/** + * @inheritDoc + * @api + */ +ol.geom.SimpleGeometry.prototype.scale = function(sx, opt_sy, opt_anchor) { + var sy = opt_sy; + if (sy === undefined) { + sy = sx; + } + var anchor = opt_anchor; + if (!anchor) { + anchor = ol.extent.getCenter(this.getExtent()); + } + var flatCoordinates = this.getFlatCoordinates(); + if (flatCoordinates) { + var stride = this.getStride(); + ol.geom.flat.transform.scale( + flatCoordinates, 0, flatCoordinates.length, + stride, sx, sy, anchor, flatCoordinates); + this.changed(); + } +}; + + /** * @inheritDoc * @api stable diff --git a/test/spec/ol/geom/linestring.test.js b/test/spec/ol/geom/linestring.test.js index 123b53010a..c3e133bfd4 100644 --- a/test/spec/ol/geom/linestring.test.js +++ b/test/spec/ol/geom/linestring.test.js @@ -242,6 +242,31 @@ describe('ol.geom.LineString', function() { }); + describe('#scale()', function() { + + it('scales a linestring', function() { + var geom = new ol.geom.LineString([[-10, -20], [10, 20]]); + geom.scale(10); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[-100, -200], [100, 200]]); + }); + + it('accepts sx and sy', function() { + var geom = new ol.geom.LineString([[-10, -20], [10, 20]]); + geom.scale(2, 3); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[-20, -60], [20, 60]]); + }); + + it('accepts an anchor', function() { + var geom = new ol.geom.LineString([[-10, -20], [10, 20]]); + geom.scale(3, 2, [10, 20]); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[-50, -60], [10, 20]]); + }); + + }); + describe('with a simple line string', function() { var lineString; diff --git a/test/spec/ol/geom/multilinestring.test.js b/test/spec/ol/geom/multilinestring.test.js index 555e14213e..a32dc7a96a 100644 --- a/test/spec/ol/geom/multilinestring.test.js +++ b/test/spec/ol/geom/multilinestring.test.js @@ -313,6 +313,31 @@ describe('ol.geom.MultiLineString', function() { }); + describe('#scale()', function() { + + it('scales a multi-linestring', function() { + var geom = new ol.geom.MultiLineString([[[-10, -20], [10, 20]], [[5, -10], [-5, 10]]]); + geom.scale(10); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[[-100, -200], [100, 200]], [[50, -100], [-50, 100]]]); + }); + + it('accepts sx and sy', function() { + var geom = new ol.geom.MultiLineString([[[-10, -20], [10, 20]], [[5, -10], [-5, 10]]]); + geom.scale(2, 3); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[[-20, -60], [20, 60]], [[10, -30], [-10, 30]]]); + }); + + it('accepts an anchor', function() { + var geom = new ol.geom.MultiLineString([[[-10, -20], [10, 20]], [[5, -10], [-5, 10]]]); + geom.scale(3, 2, [10, 20]); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[[-50, -60], [10, 20]], [[-5, -40], [-35, 0]]]); + }); + + }); + describe('#setLineStrings', function() { it('sets the line strings', function() { diff --git a/test/spec/ol/geom/multipoint.test.js b/test/spec/ol/geom/multipoint.test.js index 3b0fc57abf..44ae2e36bb 100644 --- a/test/spec/ol/geom/multipoint.test.js +++ b/test/spec/ol/geom/multipoint.test.js @@ -203,6 +203,31 @@ describe('ol.geom.MultiPoint', function() { }); + describe('#scale()', function() { + + it('scales a multi-point', function() { + var geom = new ol.geom.MultiPoint([[-10, -20], [10, 20]]); + geom.scale(10); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[-100, -200], [100, 200]]); + }); + + it('accepts sx and sy', function() { + var geom = new ol.geom.MultiPoint([[-10, -20], [10, 20]]); + geom.scale(2, 3); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[-20, -60], [20, 60]]); + }); + + it('accepts an anchor', function() { + var geom = new ol.geom.MultiPoint([[-10, -20], [10, 20]]); + geom.scale(3, 2, [-10, -20]); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[-10, -20], [50, 60]]); + }); + + }); + describe('#applyTransform()', function() { var multi, transform; diff --git a/test/spec/ol/geom/point.test.js b/test/spec/ol/geom/point.test.js index 89f7b7f58e..18843125fe 100644 --- a/test/spec/ol/geom/point.test.js +++ b/test/spec/ol/geom/point.test.js @@ -129,6 +129,31 @@ describe('ol.geom.Point', function() { }); + describe('#scale()', function() { + + it('scales a point', function() { + var geom = new ol.geom.Point([1, 2]); + geom.scale(10e6); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([1, 2]); + }); + + it('accepts sx and sy', function() { + var geom = new ol.geom.Point([1, 2]); + geom.scale(1e6, -42); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([1, 2]); + }); + + it('accepts an anchor', function() { + var geom = new ol.geom.Point([1, 2]); + geom.scale(10, 15, [0, 0]); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([10, 30]); + }); + + }); + describe('#applyTransform()', function() { var point, transform; From 69bf9254a5df0f9e8b83885340dd4dc75743602b Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 4 Aug 2016 11:27:36 -0600 Subject: [PATCH 2/2] Correctly set default anchor for collections --- src/ol/geom/geometrycollection.js | 6 +++- test/spec/ol/geom/geometrycollection.test.js | 37 ++++++++++++++++++++ test/spec/ol/geom/multipolygon.test.js | 31 ++++++++++++++++ test/spec/ol/geom/polygon.test.js | 31 ++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/ol/geom/geometrycollection.js b/src/ol/geom/geometrycollection.js index 0c6a6d52ce..774ff75978 100644 --- a/src/ol/geom/geometrycollection.js +++ b/src/ol/geom/geometrycollection.js @@ -249,9 +249,13 @@ ol.geom.GeometryCollection.prototype.rotate = function(angle, anchor) { * @api */ ol.geom.GeometryCollection.prototype.scale = function(sx, opt_sy, opt_anchor) { + var anchor = opt_anchor; + if (!anchor) { + anchor = ol.extent.getCenter(this.getExtent()); + } var geometries = this.geometries_; for (var i = 0, ii = geometries.length; i < ii; ++i) { - geometries[i].scale(sx, opt_sy, opt_anchor); + geometries[i].scale(sx, opt_sy, anchor); } this.changed(); }; diff --git a/test/spec/ol/geom/geometrycollection.test.js b/test/spec/ol/geom/geometrycollection.test.js index b2b3b45bdd..6fc5816d1e 100644 --- a/test/spec/ol/geom/geometrycollection.test.js +++ b/test/spec/ol/geom/geometrycollection.test.js @@ -170,6 +170,43 @@ describe('ol.geom.GeometryCollection', function() { }); + describe('#scale()', function() { + + it('scales a collection', function() { + var geom = new ol.geom.GeometryCollection([ + new ol.geom.Point([-1, -2]), + new ol.geom.LineString([[0, 0], [1, 2]]) + ]); + geom.scale(10); + var geometries = geom.getGeometries(); + expect(geometries[0].getCoordinates()).to.eql([-10, -20]); + expect(geometries[1].getCoordinates()).to.eql([[0, 0], [10, 20]]); + }); + + it('accepts sx and sy', function() { + var geom = new ol.geom.GeometryCollection([ + new ol.geom.Point([-1, -2]), + new ol.geom.LineString([[0, 0], [1, 2]]) + ]); + geom.scale(2, 3); + var geometries = geom.getGeometries(); + expect(geometries[0].getCoordinates()).to.eql([-2, -6]); + expect(geometries[1].getCoordinates()).to.eql([[0, 0], [2, 6]]); + }); + + it('accepts an anchor', function() { + var geom = new ol.geom.GeometryCollection([ + new ol.geom.Point([-1, -2]), + new ol.geom.LineString([[0, 0], [1, 2]]) + ]); + geom.scale(10, 15, [-1, -2]); + var geometries = geom.getGeometries(); + expect(geometries[0].getCoordinates()).to.eql([-1, -2]); + expect(geometries[1].getCoordinates()).to.eql([[9, 28], [19, 58]]); + }); + + }); + describe('#transform()', function() { var line, multi, point; diff --git a/test/spec/ol/geom/multipolygon.test.js b/test/spec/ol/geom/multipolygon.test.js index aa91d56db8..2189cb4326 100644 --- a/test/spec/ol/geom/multipolygon.test.js +++ b/test/spec/ol/geom/multipolygon.test.js @@ -55,6 +55,37 @@ describe('ol.geom.MultiPolygon', function() { }); + describe('#scale()', function() { + + it('scales a multi-polygon', function() { + var geom = new ol.geom.MultiPolygon([[ + [[-1, -2], [1, -2], [1, 2], [-1, 2], [-1, -2]] + ]]); + geom.scale(10); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[[[-10, -20], [10, -20], [10, 20], [-10, 20], [-10, -20]]]]); + }); + + it('accepts sx and sy', function() { + var geom = new ol.geom.MultiPolygon([[ + [[-1, -2], [1, -2], [1, 2], [-1, 2], [-1, -2]] + ]]); + geom.scale(2, 3); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[[[-2, -6], [2, -6], [2, 6], [-2, 6], [-2, -6]]]]); + }); + + it('accepts an anchor', function() { + var geom = new ol.geom.MultiPolygon([[ + [[-1, -2], [1, -2], [1, 2], [-1, 2], [-1, -2]] + ]]); + geom.scale(3, 2, [-1, -2]); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[[[-1, -2], [5, -2], [5, 6], [-1, 6], [-1, -2]]]]); + }); + + }); + describe('with a simple MultiPolygon', function() { var multiPolygon; diff --git a/test/spec/ol/geom/polygon.test.js b/test/spec/ol/geom/polygon.test.js index a067a4c097..4756814038 100644 --- a/test/spec/ol/geom/polygon.test.js +++ b/test/spec/ol/geom/polygon.test.js @@ -505,6 +505,37 @@ describe('ol.geom.Polygon', function() { }); }); + describe('#scale()', function() { + + it('scales a polygon', function() { + var geom = new ol.geom.Polygon([ + [[-1, -2], [1, -2], [1, 2], [-1, 2], [-1, -2]] + ]); + geom.scale(10); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[[-10, -20], [10, -20], [10, 20], [-10, 20], [-10, -20]]]); + }); + + it('accepts sx and sy', function() { + var geom = new ol.geom.Polygon([ + [[-1, -2], [1, -2], [1, 2], [-1, 2], [-1, -2]] + ]); + geom.scale(2, 3); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[[-2, -6], [2, -6], [2, 6], [-2, 6], [-2, -6]]]); + }); + + it('accepts an anchor', function() { + var geom = new ol.geom.Polygon([ + [[-1, -2], [1, -2], [1, 2], [-1, 2], [-1, -2]] + ]); + geom.scale(3, 2, [-1, -2]); + var coordinates = geom.getCoordinates(); + expect(coordinates).to.eql([[[-1, -2], [5, -2], [5, 6], [-1, 6], [-1, -2]]]); + }); + + }); + describe('ol.geom.Polygon.fromExtent', function() { it('creates the correct polygon', function() { var extent = [1, 2, 3, 5];