From b5db9db453e21e8d5079024af60c419b4b323222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 11 Sep 2014 18:12:17 +0200 Subject: [PATCH 01/16] Add ol.extent.forEachCorner --- src/ol/extent.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/ol/extent.js b/src/ol/extent.js index 97f52c2611..d9d592a03f 100644 --- a/src/ol/extent.js +++ b/src/ol/extent.js @@ -426,6 +426,38 @@ ol.extent.extendXY = function(extent, x, y) { }; +/** + * This function calls `callback` for each corner of the extent. If the + * callback returns a truthy value the function returns that value + * immediately. Otherwise the function returns `false`. + * @param {ol.Extent} extent Extent. + * @param {function(this:T, ol.Coordinate): S} callback Callback. + * @param {T=} opt_this Value to use as `this` when executing `callback`. + * @return {S|boolean} Value. + * @template S, T + */ +ol.extent.forEachCorner = function(extent, callback, opt_this) { + var val; + val = callback.call(opt_this, ol.extent.getBottomLeft(extent)); + if (val) { + return val; + } + val = callback.call(opt_this, ol.extent.getBottomRight(extent)); + if (val) { + return val; + } + val = callback.call(opt_this, ol.extent.getTopRight(extent)); + if (val) { + return val; + } + val = callback.call(opt_this, ol.extent.getBottomRight(extent)); + if (val) { + return val; + } + return false; +}; + + /** * @param {ol.Extent} extent Extent. * @return {number} Area. From 24321f6feb6d80a1a274e8e9867fbf186c7ebb75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 17 Sep 2014 07:56:54 +0200 Subject: [PATCH 02/16] Add ol.extent.containsXY --- src/ol/extent.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/ol/extent.js b/src/ol/extent.js index d9d592a03f..3e5c92e2e3 100644 --- a/src/ol/extent.js +++ b/src/ol/extent.js @@ -159,8 +159,7 @@ ol.extent.closestSquaredDistanceXY = function(extent, x, y) { * @api stable */ ol.extent.containsCoordinate = function(extent, coordinate) { - return extent[0] <= coordinate[0] && coordinate[0] <= extent[2] && - extent[1] <= coordinate[1] && coordinate[1] <= extent[3]; + return ol.extent.containsXY(extent, coordinate[0], coordinate[1]); }; @@ -178,6 +177,20 @@ ol.extent.containsExtent = function(extent1, extent2) { }; +/** + * Checks if the passed coordinate is contained or on the edge of the extent. + * + * @param {ol.Extent} extent Extent. + * @param {number} x X coordinate. + * @param {number} y Y coordinate. + * @return {boolean} Contains. + * @api stable + */ +ol.extent.containsXY = function(extent, x, y) { + return extent[0] <= x && x <= extent[2] && extent[1] <= y && y <= extent[3]; +}; + + /** * Get the relationship between a coordinate and extent. * @param {ol.Extent} extent The extent. From 3ce6229d34f54c890b5aa973e648c6021b04c7bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 8 Jul 2014 16:18:16 +0200 Subject: [PATCH 03/16] Add ol.geom.flat.segments.forEach --- src/ol/geom/flat/segmentsflatgeom.js | 33 +++++++++++ .../ol/geom/flat/segmentsflatgeom.test.js | 57 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/ol/geom/flat/segmentsflatgeom.js create mode 100644 test/spec/ol/geom/flat/segmentsflatgeom.test.js diff --git a/src/ol/geom/flat/segmentsflatgeom.js b/src/ol/geom/flat/segmentsflatgeom.js new file mode 100644 index 0000000000..8c6aac24fd --- /dev/null +++ b/src/ol/geom/flat/segmentsflatgeom.js @@ -0,0 +1,33 @@ +goog.provide('ol.geom.flat.segments'); + + +/** + * This function calls `callback` for each segment of the flat coordinates + * array. If the callback returns a truthy value the function returns that + * value immediately. Otherwise the function returns `false`. + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {function(ol.Coordinate, ol.Coordinate): T} callback Function + * called for each segment. + * @return {T|boolean} Value. + * @template T + */ +ol.geom.flat.segments.forEach = + function(flatCoordinates, offset, end, stride, callback) { + var point1 = [flatCoordinates[offset], flatCoordinates[offset + 1]]; + var point2 = []; + var ret; + for (; (offset + stride) < end; offset += stride) { + point2[0] = flatCoordinates[offset + stride]; + point2[1] = flatCoordinates[offset + stride + 1]; + ret = callback(point1, point2); + if (ret) { + return ret; + } + point1[0] = point2[0]; + point1[1] = point2[1]; + } + return false; +}; diff --git a/test/spec/ol/geom/flat/segmentsflatgeom.test.js b/test/spec/ol/geom/flat/segmentsflatgeom.test.js new file mode 100644 index 0000000000..18a70f56e7 --- /dev/null +++ b/test/spec/ol/geom/flat/segmentsflatgeom.test.js @@ -0,0 +1,57 @@ +goog.provide('ol.test.geom.flat.segments'); + +describe('ol.geom.flat.segments', function() { + + describe('ol.geom.flat.segments.forEach', function() { + var flatCoordinates, offset, end, stride; + beforeEach(function() { + flatCoordinates = [0, 0, 1, 1, 2, 2, 3, 3]; + offset = 0; + end = 8; + stride = 2; + }); + describe('callback returns undefined', function() { + it('executes the callback for each segment', function() { + var args = []; + var spy = sinon.spy(function(point1, point2) { + args.push([point1[0], point1[1], point2[0], point2[1]]); + }); + var ret = ol.geom.flat.segments.forEach( + flatCoordinates, offset, end, stride, spy); + expect(spy.callCount).to.be(3); + expect(args[0][0]).to.be(0); + expect(args[0][1]).to.be(0); + expect(args[0][2]).to.be(1); + expect(args[0][3]).to.be(1); + expect(args[1][0]).to.be(1); + expect(args[1][1]).to.be(1); + expect(args[1][2]).to.be(2); + expect(args[1][3]).to.be(2); + expect(args[2][0]).to.be(2); + expect(args[2][1]).to.be(2); + expect(args[2][2]).to.be(3); + expect(args[2][3]).to.be(3); + expect(ret).to.be(false); + }); + }); + describe('callback returns true', function() { + it('executes the callback for the first segment', function() { + var args = []; + var spy = sinon.spy(function(point1, point2) { + args.push([point1[0], point1[1], point2[0], point2[1]]); + return true; + }); + var ret = ol.geom.flat.segments.forEach( + flatCoordinates, offset, end, stride, spy); + expect(spy.callCount).to.be(1); + expect(args[0][0]).to.be(0); + expect(args[0][1]).to.be(0); + expect(args[0][2]).to.be(1); + expect(args[0][3]).to.be(1); + expect(ret).to.be(true); + }); + }); + }); +}); + +goog.require('ol.geom.flat.segments'); From 49fa7c01d4a299c2ce142b93ed26308fae16630c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 11 Sep 2014 18:13:27 +0200 Subject: [PATCH 04/16] Add ol.geom.flat.contains.linearRingContainsExtent --- src/ol/geom/flat/containsflatgeom.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/ol/geom/flat/containsflatgeom.js b/src/ol/geom/flat/containsflatgeom.js index 5b5c93aacb..18379450ac 100644 --- a/src/ol/geom/flat/containsflatgeom.js +++ b/src/ol/geom/flat/containsflatgeom.js @@ -1,6 +1,29 @@ goog.provide('ol.geom.flat.contains'); goog.require('goog.asserts'); +goog.require('ol.extent'); + + +/** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {ol.Extent} extent Extent. + * @return {boolean} Contains extent. + */ +ol.geom.flat.contains.linearRingContainsExtent = + function(flatCoordinates, offset, end, stride, extent) { + var outside = ol.extent.forEachCorner(extent, + /** + * @param {ol.Coordinate} coordinate Coordinate. + */ + function(coordinate) { + return !ol.geom.flat.contains.linearRingContainsXY(flatCoordinates, + offset, end, stride, coordinate[0], coordinate[1]); + }); + return !outside; +}; /** From cdbc1a07be2a63ac8bac31be9985d6140132e0f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 8 Jul 2014 16:19:13 +0200 Subject: [PATCH 05/16] Add ol.geom.flat.intersectsextent Includes functions for testing if an extent and a geometry intersect. --- src/ol/geom/flat/intersectsextent.js | 154 ++++++++++++++++++ .../flat/intersectsextentflatgeom.test.js | 92 +++++++++++ 2 files changed, 246 insertions(+) create mode 100644 src/ol/geom/flat/intersectsextent.js create mode 100644 test/spec/ol/geom/flat/intersectsextentflatgeom.test.js diff --git a/src/ol/geom/flat/intersectsextent.js b/src/ol/geom/flat/intersectsextent.js new file mode 100644 index 0000000000..6f880c7baa --- /dev/null +++ b/src/ol/geom/flat/intersectsextent.js @@ -0,0 +1,154 @@ +goog.provide('ol.geom.flat.intersectsextent'); + +goog.require('goog.asserts'); +goog.require('ol.extent'); +goog.require('ol.geom.flat.contains'); +goog.require('ol.geom.flat.segments'); + + +/** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {ol.Extent} extent Extent. + * @return {boolean} True if the geometry and the extent intersect. + */ +ol.geom.flat.intersectsextent.lineString = + function(flatCoordinates, offset, end, stride, extent) { + var coordinatesExtent = ol.extent.extendFlatCoordinates( + ol.extent.createEmpty(), flatCoordinates, offset, end, stride); + if (!ol.extent.intersects(extent, coordinatesExtent)) { + return false; + } + if (ol.extent.containsExtent(extent, coordinatesExtent)) { + return true; + } + if (coordinatesExtent[0] >= extent[0] && + coordinatesExtent[2] <= extent[2]) { + return true; + } + if (coordinatesExtent[1] >= extent[1] && + coordinatesExtent[3] <= extent[3]) { + return true; + } + return ol.geom.flat.segments.forEach(flatCoordinates, offset, end, stride, + /** + * @param {ol.Coordinate} point1 Start point. + * @param {ol.Coordinate} point2 End point. + * @return {boolean} `true` if the segment and the extent intersect, + * `false` otherwise. + */ + function(point1, point2) { + return ol.extent.segmentIntersects(extent, point1, point2); + }); +}; + + +/** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.} ends Ends. + * @param {number} stride Stride. + * @param {ol.Extent} extent Extent. + * @return {boolean} True if the geometry and the extent intersect. + */ +ol.geom.flat.intersectsextent.lineStrings = + function(flatCoordinates, offset, ends, stride, extent) { + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + if (ol.geom.flat.intersectsextent.lineString( + flatCoordinates, offset, ends[i], stride, extent)) { + return true; + } + offset = ends[i]; + } + return false; +}; + + +/** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {ol.Extent} extent Extent. + * @return {boolean} True if the geometry and the extent intersect. + */ +ol.geom.flat.intersectsextent.linearRing = + function(flatCoordinates, offset, end, stride, extent) { + if (ol.geom.flat.intersectsextent.lineString( + flatCoordinates, offset, end, stride, extent)) { + return true; + } + if (ol.geom.flat.contains.linearRingContainsXY( + flatCoordinates, offset, end, stride, extent[0], extent[1])) { + return true; + } + if (ol.geom.flat.contains.linearRingContainsXY( + flatCoordinates, offset, end, stride, extent[0], extent[3])) { + return true; + } + if (ol.geom.flat.contains.linearRingContainsXY( + flatCoordinates, offset, end, stride, extent[2], extent[1])) { + return true; + } + if (ol.geom.flat.contains.linearRingContainsXY( + flatCoordinates, offset, end, stride, extent[2], extent[3])) { + return true; + } + return false; +}; + + +/** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.} ends Ends. + * @param {number} stride Stride. + * @param {ol.Extent} extent Extent. + * @return {boolean} True if the geometry and the extent intersect. + */ +ol.geom.flat.intersectsextent.linearRings = + function(flatCoordinates, offset, ends, stride, extent) { + goog.asserts.assert(ends.length > 0); + if (!ol.geom.flat.intersectsextent.linearRing( + flatCoordinates, offset, ends[0], stride, extent)) { + return false; + } + if (ends.length === 1) { + return true; + } + var i, ii; + for (i = 1, ii = ends.length; i < ii; ++i) { + if (ol.geom.flat.contains.linearRingContainsExtent( + flatCoordinates, ends[i - 1], ends[i], stride, extent)) { + return false; + } + } + return true; +}; + + +/** + * @param {Array.} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.>} endss Endss. + * @param {number} stride Stride. + * @param {ol.Extent} extent Extent. + * @return {boolean} True if the geometry and the extent intersect. + */ +ol.geom.flat.intersectsextent.linearRingss = + function(flatCoordinates, offset, endss, stride, extent) { + goog.asserts.assert(endss.length > 0); + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i]; + if (ol.geom.flat.intersectsextent.linearRings( + flatCoordinates, offset, ends, stride, extent)) { + return true; + } + offset = ends[ends.length - 1]; + } + return false; +}; diff --git a/test/spec/ol/geom/flat/intersectsextentflatgeom.test.js b/test/spec/ol/geom/flat/intersectsextentflatgeom.test.js new file mode 100644 index 0000000000..e929efc08e --- /dev/null +++ b/test/spec/ol/geom/flat/intersectsextentflatgeom.test.js @@ -0,0 +1,92 @@ +goog.provide('ol.test.geom.flat.intersectsextent'); + +describe('ol.geom.flat.intersectsextent', function() { + + describe('ol.geom.flat.intersectsextent.lineString', function() { + var flatCoordinates; + beforeEach(function() { + flatCoordinates = [0, 0, 1, 1, 2, 2]; + }); + describe('linestring envelope does not intersect the extent', function() { + it('returns false', function() { + var extent = [3, 3, 4, 4]; + var r = ol.geom.flat.intersectsextent.lineString( + flatCoordinates, 0, flatCoordinates.length, 2, extent); + expect(r).to.be(false); + }); + }); + describe('linestring envelope within the extent', function() { + it('returns true', function() { + var extent = [-1, -1, 3, 3]; + var r = ol.geom.flat.intersectsextent.lineString( + flatCoordinates, 0, flatCoordinates.length, 2, extent); + expect(r).to.be(true); + }); + }); + describe('linestring envelope bisected by an edge of the extent', + function() { + it('returns true', function() { + var extent = [-0.1, 0.1, 2.1, 0.1]; + var r = ol.geom.flat.intersectsextent.lineString( + flatCoordinates, 0, flatCoordinates.length, 2, extent); + expect(r).to.be(true); + }); + }); + describe('a segment intersects the extent', function() { + it('returns true', function() { + var extent = [-0.5, -0.5, 0.5, 0.5]; + var r = ol.geom.flat.intersectsextent.lineString( + flatCoordinates, 0, flatCoordinates.length, 2, extent); + expect(r).to.be(true); + }); + }); + describe('no segments intersect the extent', function() { + it('returns false', function() { + var extent = [0.5, 1.5, 1, 1.75]; + var r = ol.geom.flat.intersectsextent.lineString( + flatCoordinates, 0, flatCoordinates.length, 2, extent); + expect(r).to.be(false); + }); + it('returns false', function() { + var extent = [1, 0.25, 1.5, 0.5]; + var r = ol.geom.flat.intersectsextent.lineString( + flatCoordinates, 0, flatCoordinates.length, 2, extent); + expect(r).to.be(false); + }); + }); + }); + + describe('ol.geom.flat.intersectsextent.linearRing', function() { + var flatCoordinates; + beforeEach(function() { + flatCoordinates = [0, 0, 1, 1, 2, 0, 1, -1, 0, 0]; + }); + describe('boundary intersects the extent', function() { + it('returns true', function() { + var extent = [1.5, 0.0, 2.5, 1.0]; + var r = ol.geom.flat.intersectsextent.linearRing( + flatCoordinates, 0, flatCoordinates.length, 2, extent); + expect(r).to.be(true); + }); + }); + describe('boundary does not intersect the extent and ring does not ' + + 'contain a corner of the extent', function() { + it('returns false', function() { + var extent = [2.0, 0.5, 3, 1.5]; + var r = ol.geom.flat.intersectsextent.linearRing( + flatCoordinates, 0, flatCoordinates.length, 2, extent); + expect(r).to.be(false); + }); + }); + describe('ring contains the extent', function() { + it('returns true', function() { + var extent = [0.75, -0.25, 1.25, 0.25]; + var r = ol.geom.flat.intersectsextent.linearRing( + flatCoordinates, 0, flatCoordinates.length, 2, extent); + expect(r).to.be(true); + }); + }); + }); +}); + +goog.require('ol.geom.flat.intersectsextent'); From a740f32e4d0c0c1c072631863a67f66c6a79251e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 17 Sep 2014 07:56:29 +0200 Subject: [PATCH 06/16] Add ol.geom.Geometry#intersectsExtent (abstract method) --- src/ol/geom/geometry.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ol/geom/geometry.js b/src/ol/geom/geometry.js index fd78792fcd..90d04778d2 100644 --- a/src/ol/geom/geometry.js +++ b/src/ol/geom/geometry.js @@ -184,6 +184,16 @@ ol.geom.Geometry.prototype.getType = goog.abstractMethod; ol.geom.Geometry.prototype.applyTransform = goog.abstractMethod; +/** + * Test if the geometry and the passed extent intersect. + * @param {ol.Extent} extent Extent. + * @return {boolean} `true` if the geometry and the extent intersect. + * @function + * @api + */ +ol.geom.Geometry.prototype.intersectsExtent = goog.abstractMethod; + + /** * Transform each coordinate of the geometry from one coordinate reference * system to another. The geometry is modified in place. From 612cf7056058f0f2ac6c1049dfb8164fb9d13343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 17 Sep 2014 07:57:19 +0200 Subject: [PATCH 07/16] Add ol.geom.Point#intersectsExtent --- src/ol/geom/point.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ol/geom/point.js b/src/ol/geom/point.js index d030d7a8aa..07ba09ace6 100644 --- a/src/ol/geom/point.js +++ b/src/ol/geom/point.js @@ -93,6 +93,16 @@ ol.geom.Point.prototype.getType = function() { }; +/** + * @inheritDoc + * @api + */ +ol.geom.Point.prototype.intersectsExtent = function(extent) { + return ol.extent.containsXY(extent, + this.flatCoordinates[0], this.flatCoordinates[1]); +}; + + /** * @param {ol.Coordinate} coordinates Coordinates. * @param {ol.geom.GeometryLayout=} opt_layout Layout. From 67f98145bb7fb766b57557f46367ca89a6bca612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 17 Sep 2014 07:57:47 +0200 Subject: [PATCH 08/16] Add ol.geom.MultiPoint#intersectsExtent --- src/ol/geom/multipoint.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/ol/geom/multipoint.js b/src/ol/geom/multipoint.js index b1b414ffbd..f19dfd6dfd 100644 --- a/src/ol/geom/multipoint.js +++ b/src/ol/geom/multipoint.js @@ -142,6 +142,25 @@ ol.geom.MultiPoint.prototype.getType = function() { }; +/** + * @inheritDoc + * @api + */ +ol.geom.MultiPoint.prototype.intersectsExtent = function(extent) { + var flatCoordinates = this.flatCoordinates; + var stride = this.stride; + var i, ii, x, y; + for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) { + x = flatCoordinates[i]; + y = flatCoordinates[i + 1]; + if (ol.extent.containsXY(extent, x, y)) { + return true; + } + } + return false; +}; + + /** * @param {Array.} coordinates Coordinates. * @param {ol.geom.GeometryLayout=} opt_layout Layout. From 8bdeca9f0a325507e32714806d3fd1ae4bf86510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 8 Jul 2014 16:25:07 +0200 Subject: [PATCH 09/16] Add ol.geom.Polygon#intersectsExtent --- src/ol/geom/polygon.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ol/geom/polygon.js b/src/ol/geom/polygon.js index 7e91e80ba5..52d55ff1fd 100644 --- a/src/ol/geom/polygon.js +++ b/src/ol/geom/polygon.js @@ -14,6 +14,7 @@ goog.require('ol.geom.flat.contains'); goog.require('ol.geom.flat.deflate'); goog.require('ol.geom.flat.inflate'); goog.require('ol.geom.flat.interiorpoint'); +goog.require('ol.geom.flat.intersectsextent'); goog.require('ol.geom.flat.orient'); goog.require('ol.geom.flat.simplify'); @@ -296,6 +297,16 @@ ol.geom.Polygon.prototype.getType = function() { }; +/** + * @inheritDoc + * @api + */ +ol.geom.Polygon.prototype.intersectsExtent = function(extent) { + return ol.geom.flat.intersectsextent.linearRings( + this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, extent); +}; + + /** * @param {Array.>} coordinates Coordinates. * @param {ol.geom.GeometryLayout=} opt_layout Layout. From d1f6f132d64a22922c89b341021a8c456a5af600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 8 Jul 2014 16:25:29 +0200 Subject: [PATCH 10/16] Add ol.geom.MultiPolygon#intersectsExtent --- src/ol/geom/multipolygon.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ol/geom/multipolygon.js b/src/ol/geom/multipolygon.js index e546877f65..b881e2eb12 100644 --- a/src/ol/geom/multipolygon.js +++ b/src/ol/geom/multipolygon.js @@ -14,6 +14,7 @@ goog.require('ol.geom.flat.contains'); goog.require('ol.geom.flat.deflate'); goog.require('ol.geom.flat.inflate'); goog.require('ol.geom.flat.interiorpoint'); +goog.require('ol.geom.flat.intersectsextent'); goog.require('ol.geom.flat.orient'); goog.require('ol.geom.flat.simplify'); @@ -315,6 +316,16 @@ ol.geom.MultiPolygon.prototype.getType = function() { }; +/** + * @inheritDoc + * @api + */ +ol.geom.MultiPolygon.prototype.intersectsExtent = function(extent) { + return ol.geom.flat.intersectsextent.linearRingss( + this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, extent); +}; + + /** * @param {Array.>>} coordinates Coordinates. * @param {ol.geom.GeometryLayout=} opt_layout Layout. From 8d7cf7960a34443f3aa809bb49cb55581b65b724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 11 Sep 2014 23:12:18 +0200 Subject: [PATCH 11/16] Add ol.geom.LineString#intersectsExtent --- src/ol/geom/linestring.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index a7027e4b72..7e2bcf74c5 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -9,6 +9,7 @@ goog.require('ol.geom.flat.closest'); goog.require('ol.geom.flat.deflate'); goog.require('ol.geom.flat.inflate'); goog.require('ol.geom.flat.interpolate'); +goog.require('ol.geom.flat.intersectsextent'); goog.require('ol.geom.flat.length'); goog.require('ol.geom.flat.simplify'); @@ -190,6 +191,17 @@ ol.geom.LineString.prototype.getType = function() { }; +/** + * @inheritDoc + * @api + */ +ol.geom.LineString.prototype.intersectsExtent = function(extent) { + return ol.geom.flat.intersectsextent.lineString( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + extent); +}; + + /** * @param {Array.} coordinates Coordinates. * @param {ol.geom.GeometryLayout=} opt_layout Layout. From 92c2aca4809af22f132205c62021a911b3599e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 11 Sep 2014 23:12:29 +0200 Subject: [PATCH 12/16] Add ol.geom.MultiLineString#intersectsExtent --- src/ol/geom/multilinestring.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ol/geom/multilinestring.js b/src/ol/geom/multilinestring.js index a2b2e3dc3c..728168b198 100644 --- a/src/ol/geom/multilinestring.js +++ b/src/ol/geom/multilinestring.js @@ -10,6 +10,7 @@ goog.require('ol.geom.flat.closest'); goog.require('ol.geom.flat.deflate'); goog.require('ol.geom.flat.inflate'); goog.require('ol.geom.flat.interpolate'); +goog.require('ol.geom.flat.intersectsextent'); goog.require('ol.geom.flat.simplify'); @@ -244,6 +245,16 @@ ol.geom.MultiLineString.prototype.getType = function() { }; +/** + * @inheritDoc + * @api + */ +ol.geom.MultiLineString.prototype.intersectsExtent = function(extent) { + return ol.geom.flat.intersectsextent.lineStrings( + this.flatCoordinates, 0, this.ends_, this.stride, extent); +}; + + /** * @param {Array.>} coordinates Coordinates. * @param {ol.geom.GeometryLayout=} opt_layout Layout. From 88911200c23a29787bf74e98ea581bb2d45522b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 17 Sep 2014 07:58:12 +0200 Subject: [PATCH 13/16] Add ol.geom.GeometryCollection#intersectsExtent --- src/ol/geom/geometrycollection.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/ol/geom/geometrycollection.js b/src/ol/geom/geometrycollection.js index ace6adfdf8..e6130bd673 100644 --- a/src/ol/geom/geometrycollection.js +++ b/src/ol/geom/geometrycollection.js @@ -218,6 +218,22 @@ ol.geom.GeometryCollection.prototype.getType = function() { }; +/** + * @inheritDoc + * @api + */ +ol.geom.GeometryCollection.prototype.intersectsExtent = function(extent) { + var geometries = this.geometries_; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + if (geometries[i].intersectsExtent(extent)) { + return true; + } + } + return false; +}; + + /** * @return {boolean} Is empty. */ From 227f3bae18bdd0b382009e0de3dc9303a9f64474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 17 Sep 2014 09:13:12 +0200 Subject: [PATCH 14/16] Add ol.source.Vector#forEachFeatureIntersectingExtent --- src/ol/source/vectorsource.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index e82ade9da9..7720f746c0 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -247,6 +247,39 @@ ol.source.Vector.prototype.forEachFeatureInExtentAtResolution = }; +/** + * This function executes the `callback` function once for each feature + * intersecting the `extent` until `callback` returns a truthy value. When + * `callback` returns a truthy value the function immediately returns that + * value. Otherwise the function returns `undefined`. + * @param {ol.Extent} extent Extent. + * @param {function(this: T, ol.Feature): S} f Callback. + * @param {T=} opt_this The object to use as `this` in `f`. + * @return {S|undefined} + * @template T,S + * @api + */ +ol.source.Vector.prototype.forEachFeatureIntersectingExtent = + function(extent, f, opt_this) { + return this.forEachFeatureInExtent(extent, + /** + * @param {ol.Feature} feature Feature. + * @return {S|undefined} + * @template S + */ + function(feature) { + var geometry = feature.getGeometry(); + goog.asserts.assert(goog.isDefAndNotNull(geometry)); + if (geometry.intersectsExtent(extent)) { + var result = f.call(opt_this, feature); + if (result) { + return result; + } + } + }); +}; + + /** * @return {Array.} Features. * @api stable From 79445e993b0013c58d3a3292c44213fc67ba5567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 8 Jul 2014 16:26:06 +0200 Subject: [PATCH 15/16] Add a box feature selection example --- examples/box-selection.html | 59 +++++++++++++++++++++++++++ examples/box-selection.js | 81 +++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 examples/box-selection.html create mode 100644 examples/box-selection.js diff --git a/examples/box-selection.html b/examples/box-selection.html new file mode 100644 index 0000000000..78aa899294 --- /dev/null +++ b/examples/box-selection.html @@ -0,0 +1,59 @@ + + + + + + + + + + + Box selection example + + + + + +
+ +
+
+
+
+
+ +
+ +
+

Box selection example

+

Using a DragBox interaction to select features.

+
+

This example shows how to use a DragBox interaction to select features. Selected features are added + to the feature overlay of a select interaction (ol.interaction.Select) for highlighting.

+

Use SHIFT+drag to draw boxes.

+

See the box-selection.js source to see how this is done.

+
+
DragBox, feature, selection, box
+
+
+
+   +
+
+ +
+ +
+ + + + + + + diff --git a/examples/box-selection.js b/examples/box-selection.js new file mode 100644 index 0000000000..09db36e0b6 --- /dev/null +++ b/examples/box-selection.js @@ -0,0 +1,81 @@ +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.events.condition'); +goog.require('ol.interaction'); +goog.require('ol.interaction.DragBox'); +goog.require('ol.interaction.Select'); +goog.require('ol.layer.Tile'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.GeoJSON'); +goog.require('ol.source.OSM'); +goog.require('ol.style.Stroke'); +goog.require('ol.style.Style'); + + +var vectorSource = new ol.source.GeoJSON({ + projection: 'EPSG:3857', + url: 'data/geojson/countries.geojson' +}); + + +var map = new ol.Map({ + layers: [ + new ol.layer.Tile({ + source: new ol.source.OSM() + }), + new ol.layer.Vector({ + source: vectorSource + }) + ], + renderer: 'canvas', + target: 'map', + view: new ol.View({ + center: [0, 0], + zoom: 2 + }) +}); + +// a normal select interaction to handle click +var select = new ol.interaction.Select(); +map.addInteraction(select); + +var selectedFeatures = select.getFeatures(); + +// a DragBox interaction used to select features by drawing boxes +var dragBox = new ol.interaction.DragBox({ + condition: ol.events.condition.shiftKeyOnly, + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: [0, 0, 255, 1] + }) + }) +}); + +map.addInteraction(dragBox); + +var infoBox = document.getElementById('info'); + +dragBox.on('boxend', function(e) { + // features that intersect the box are added to the collection of + // selected features, and their names are displayed in the "info" + // div + var info = []; + var extent = dragBox.getGeometry().getExtent(); + vectorSource.forEachFeatureIntersectingExtent(extent, function(feature) { + selectedFeatures.push(feature); + info.push(feature.get('name')); + }); + if (info.length > 0) { + infoBox.innerHTML = info.join(', '); + } +}); + +// clear selection when drawing a new box and when clicking on the map +dragBox.on('boxstart', function(e) { + selectedFeatures.clear(); + infoBox.innerHTML = ' '; +}); +map.on('click', function() { + selectedFeatures.clear(); + infoBox.innerHTML = ' '; +}); From 72e173d9253aeffd1aa6e205bfca83efafa53bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 17 Sep 2014 11:57:59 +0200 Subject: [PATCH 16/16] Rename ol.extent.segmentIntersects to intersectsSegment --- src/ol/extent.js | 2 +- src/ol/geom/flat/intersectsextent.js | 2 +- test/spec/ol/extent.test.js | 44 ++++++++++++++-------------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/ol/extent.js b/src/ol/extent.js index 3e5c92e2e3..5209ac9313 100644 --- a/src/ol/extent.js +++ b/src/ol/extent.js @@ -774,7 +774,7 @@ ol.extent.scaleFromCenter = function(extent, value) { * @param {ol.Coordinate} end Segment end coordinate. * @return {boolean} The segment intersects the extent. */ -ol.extent.segmentIntersects = function(extent, start, end) { +ol.extent.intersectsSegment = function(extent, start, end) { var intersects = false; var startRel = ol.extent.coordinateRelationship(extent, start); var endRel = ol.extent.coordinateRelationship(extent, end); diff --git a/src/ol/geom/flat/intersectsextent.js b/src/ol/geom/flat/intersectsextent.js index 6f880c7baa..a86ca4f2ff 100644 --- a/src/ol/geom/flat/intersectsextent.js +++ b/src/ol/geom/flat/intersectsextent.js @@ -40,7 +40,7 @@ ol.geom.flat.intersectsextent.lineString = * `false` otherwise. */ function(point1, point2) { - return ol.extent.segmentIntersects(extent, point1, point2); + return ol.extent.intersectsSegment(extent, point1, point2); }); }; diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index 8cde8720e5..ee5c384326 100644 --- a/test/spec/ol/extent.test.js +++ b/test/spec/ol/extent.test.js @@ -349,7 +349,7 @@ describe('ol.extent', function() { }); }); - describe('segmentIntersects()', function() { + describe('intersectsSegment()', function() { var extent = [-180, -90, 180, 90]; var north = [0, 100]; @@ -368,107 +368,107 @@ describe('ol.extent', function() { var inside = [10, 10]; it('returns true if contained', function() { - var intersects = ol.extent.segmentIntersects(extent, center, inside); + var intersects = ol.extent.intersectsSegment(extent, center, inside); expect(intersects).to.be(true); }); it('returns true if crosses top', function() { - var intersects = ol.extent.segmentIntersects(extent, center, north); + var intersects = ol.extent.intersectsSegment(extent, center, north); expect(intersects).to.be(true); }); it('returns true if crosses right', function() { - var intersects = ol.extent.segmentIntersects(extent, center, east); + var intersects = ol.extent.intersectsSegment(extent, center, east); expect(intersects).to.be(true); }); it('returns true if crosses bottom', function() { - var intersects = ol.extent.segmentIntersects(extent, center, south); + var intersects = ol.extent.intersectsSegment(extent, center, south); expect(intersects).to.be(true); }); it('returns true if crosses left', function() { - var intersects = ol.extent.segmentIntersects(extent, center, west); + var intersects = ol.extent.intersectsSegment(extent, center, west); expect(intersects).to.be(true); }); it('returns false if above', function() { - var intersects = ol.extent.segmentIntersects(extent, northwest, north); + var intersects = ol.extent.intersectsSegment(extent, northwest, north); expect(intersects).to.be(false); }); it('returns false if right', function() { - var intersects = ol.extent.segmentIntersects(extent, northeast, east); + var intersects = ol.extent.intersectsSegment(extent, northeast, east); expect(intersects).to.be(false); }); it('returns false if below', function() { - var intersects = ol.extent.segmentIntersects(extent, south, southwest); + var intersects = ol.extent.intersectsSegment(extent, south, southwest); expect(intersects).to.be(false); }); it('returns false if left', function() { - var intersects = ol.extent.segmentIntersects(extent, west, southwest); + var intersects = ol.extent.intersectsSegment(extent, west, southwest); expect(intersects).to.be(false); }); it('returns true if crosses top to bottom', function() { - var intersects = ol.extent.segmentIntersects(extent, north, south); + var intersects = ol.extent.intersectsSegment(extent, north, south); expect(intersects).to.be(true); }); it('returns true if crosses bottom to top', function() { - var intersects = ol.extent.segmentIntersects(extent, south, north); + var intersects = ol.extent.intersectsSegment(extent, south, north); expect(intersects).to.be(true); }); it('returns true if crosses left to right', function() { - var intersects = ol.extent.segmentIntersects(extent, west, east); + var intersects = ol.extent.intersectsSegment(extent, west, east); expect(intersects).to.be(true); }); it('returns true if crosses right to left', function() { - var intersects = ol.extent.segmentIntersects(extent, east, west); + var intersects = ol.extent.intersectsSegment(extent, east, west); expect(intersects).to.be(true); }); it('returns true if crosses northwest to east', function() { - var intersects = ol.extent.segmentIntersects(extent, northwest, east); + var intersects = ol.extent.intersectsSegment(extent, northwest, east); expect(intersects).to.be(true); }); it('returns true if crosses south to west', function() { - var intersects = ol.extent.segmentIntersects(extent, south, west); + var intersects = ol.extent.intersectsSegment(extent, south, west); expect(intersects).to.be(true); }); it('returns true if touches top', function() { - var intersects = ol.extent.segmentIntersects(extent, northwest, top); + var intersects = ol.extent.intersectsSegment(extent, northwest, top); expect(intersects).to.be(true); }); it('returns true if touches right', function() { - var intersects = ol.extent.segmentIntersects(extent, southeast, right); + var intersects = ol.extent.intersectsSegment(extent, southeast, right); expect(intersects).to.be(true); }); it('returns true if touches bottom', function() { - var intersects = ol.extent.segmentIntersects(extent, bottom, south); + var intersects = ol.extent.intersectsSegment(extent, bottom, south); expect(intersects).to.be(true); }); it('returns true if touches left', function() { - var intersects = ol.extent.segmentIntersects(extent, left, west); + var intersects = ol.extent.intersectsSegment(extent, left, west); expect(intersects).to.be(true); }); it('works for zero length inside', function() { - var intersects = ol.extent.segmentIntersects(extent, center, center); + var intersects = ol.extent.intersectsSegment(extent, center, center); expect(intersects).to.be(true); }); it('works for zero length outside', function() { - var intersects = ol.extent.segmentIntersects(extent, north, north); + var intersects = ol.extent.intersectsSegment(extent, north, north); expect(intersects).to.be(false); });