diff --git a/src/ol/geom/geometry.js b/src/ol/geom/geometry.js index 2ddbbe0987..238c9bca51 100644 --- a/src/ol/geom/geometry.js +++ b/src/ol/geom/geometry.js @@ -1,3 +1,4 @@ +goog.require('ol.Extent'); goog.provide('ol.geom.Coordinate'); goog.provide('ol.geom.CoordinateArray'); goog.provide('ol.geom.Geometry'); @@ -10,6 +11,20 @@ goog.provide('ol.geom.Geometry'); ol.geom.Geometry = function() {}; +/** + * The dimension of this geometry (2 or 3). + * @type {number} + */ +ol.geom.Geometry.prototype.dimension; + + +/** + * Get the rectangular 2D evelope for this geoemtry. + * @return {ol.Extent} The bounding rectangular envelope. + */ +ol.geom.Geometry.prototype.getBounds = goog.abstractMethod; + + /** * @typedef {Array.} */ diff --git a/src/ol/geom/geometrycollection.js b/src/ol/geom/geometrycollection.js new file mode 100644 index 0000000000..419d9239b5 --- /dev/null +++ b/src/ol/geom/geometrycollection.js @@ -0,0 +1,70 @@ +goog.provide('ol.geom.GeometryCollection'); + +goog.require('goog.asserts'); +goog.require('ol.Extent'); +goog.require('ol.geom.Geometry'); + + + +/** + * A mixed collection of geometries. This constructor is typically not called + * directly. Instead call @see ol.geom.GeometryCollection#fromGeometries. + * @constructor + * @implements {ol.geom.Geometry} + */ +ol.geom.GeometryCollection = function() { + + /** + * @type {Array.} + */ + this.components = null; + + /** + * @type {number} + */ + this.dimension; + + /** + * @type {ol.Extent} + * @protected + */ + this.bounds = null; + +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.getBounds = function() { + if (goog.isNull(this.bounds)) { + var minX, + minY = minX = Number.POSITIVE_INFINITY, + maxX, + maxY = maxX = Number.NEGATIVE_INFINITY, + components = this.components, + len = components.length, + bounds, i; + + for (i = 0; i < len; ++i) { + bounds = components[i].getBounds(); + minX = Math.min(bounds.minX, minX); + minY = Math.min(bounds.minY, minY); + maxX = Math.max(bounds.maxX, maxX); + maxY = Math.max(bounds.maxY, maxY); + } + this.bounds = new ol.Extent(minX, minY, maxX, maxY); + } + return this.bounds; +}; + + +/** + * @param {Array.} components Array of geometries. + * @return {ol.geom.GeometryCollection} A mixed geometry collection. + */ +ol.geom.GeometryCollection.fromGeometries = function(components) { + var collection = new ol.geom.GeometryCollection(); + collection.components = components; + return collection; +}; diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index a232449af6..a1a5f83f43 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -2,6 +2,7 @@ goog.provide('ol.geom.LineString'); goog.require('goog.asserts'); goog.require('goog.vec.Float64Array'); +goog.require('ol.Extent'); goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); @@ -35,4 +36,38 @@ ol.geom.LineString = function(coordinates) { this.dimension = dimension; goog.asserts.assert(this.dimension >= 2); + /** + * @type {ol.Extent} + * @private + */ + this.bounds_ = null; + +}; + + +/** + * @inheritDoc + */ +ol.geom.LineString.prototype.getBounds = function() { + if (goog.isNull(this.bounds_)) { + var minX, + minY = minX = Number.POSITIVE_INFINITY, + maxX, + maxY = maxX = Number.NEGATIVE_INFINITY, + coordinates = this.coordinates, + len = coordinates.length, + dim = this.dimension, + x, y, i; + + for (i = 0; i < len; i += dim) { + x = coordinates[i]; + y = coordinates[i + 1]; + minX = Math.min(minX, x); + minY = Math.min(minY, y); + maxX = Math.max(maxX, x); + maxY = Math.max(maxY, y); + } + this.bounds_ = new ol.Extent(minX, minY, maxX, maxY); + } + return this.bounds_; }; diff --git a/src/ol/geom/multilinestring.js b/src/ol/geom/multilinestring.js index f73f24d8e6..36567eb7eb 100644 --- a/src/ol/geom/multilinestring.js +++ b/src/ol/geom/multilinestring.js @@ -2,17 +2,18 @@ goog.provide('ol.geom.MultiLineString'); goog.require('goog.asserts'); goog.require('ol.geom.CoordinateArray'); -goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryCollection'); goog.require('ol.geom.LineString'); /** * @constructor - * @implements {ol.geom.Geometry} + * @extends {ol.geom.GeometryCollection} * @param {Array.} coordinates Coordinates array. */ ol.geom.MultiLineString = function(coordinates) { + goog.base(this); var numParts = coordinates.length, dimension; @@ -24,7 +25,7 @@ ol.geom.MultiLineString = function(coordinates) { for (var i = 0; i < numParts; ++i) { this.components[i] = new ol.geom.LineString(coordinates[i]); if (!goog.isDef(dimension)) { - dimension = this.components[i]; + dimension = this.components[i].dimension; } else { goog.asserts.assert(this.components[i].dimension === dimension); } @@ -37,3 +38,4 @@ ol.geom.MultiLineString = function(coordinates) { goog.asserts.assert(this.dimension >= 2); }; +goog.inherits(ol.geom.MultiLineString, ol.geom.GeometryCollection); diff --git a/src/ol/geom/multipoint.js b/src/ol/geom/multipoint.js index 8669a7c0cc..4fa8174e3b 100644 --- a/src/ol/geom/multipoint.js +++ b/src/ol/geom/multipoint.js @@ -2,17 +2,18 @@ goog.provide('ol.geom.MultiPoint'); goog.require('goog.asserts'); goog.require('ol.geom.CoordinateArray'); -goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryCollection'); goog.require('ol.geom.Point'); /** * @constructor - * @implements {ol.geom.Geometry} + * @extends {ol.geom.GeometryCollection} * @param {ol.geom.CoordinateArray} coordinates Coordinates array. */ ol.geom.MultiPoint = function(coordinates) { + goog.base(this); var numParts = coordinates.length, dimension; @@ -37,3 +38,4 @@ ol.geom.MultiPoint = function(coordinates) { goog.asserts.assert(this.dimension >= 2); }; +goog.inherits(ol.geom.MultiPoint, ol.geom.GeometryCollection); diff --git a/src/ol/geom/multipolygon.js b/src/ol/geom/multipolygon.js index 958ddf9d5d..8049493784 100644 --- a/src/ol/geom/multipolygon.js +++ b/src/ol/geom/multipolygon.js @@ -2,18 +2,19 @@ goog.provide('ol.geom.MultiPolygon'); goog.require('goog.asserts'); goog.require('ol.geom.CoordinateArray'); -goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryCollection'); goog.require('ol.geom.Polygon'); /** * @constructor - * @implements {ol.geom.Geometry} + * @extends {ol.geom.GeometryCollection} * @param {Array.>} coordinates Coordinates * array. */ ol.geom.MultiPolygon = function(coordinates) { + goog.base(this); var numParts = coordinates.length, dimension; @@ -25,7 +26,7 @@ ol.geom.MultiPolygon = function(coordinates) { for (var i = 0; i < numParts; ++i) { this.components[i] = new ol.geom.Polygon(coordinates[i]); if (!goog.isDef(dimension)) { - dimension = this.components[i]; + dimension = this.components[i].dimension; } else { goog.asserts.assert(this.components[i].dimension === dimension); } @@ -38,3 +39,4 @@ ol.geom.MultiPolygon = function(coordinates) { goog.asserts.assert(this.dimension >= 2); }; +goog.inherits(ol.geom.MultiPolygon, ol.geom.GeometryCollection); diff --git a/src/ol/geom/point.js b/src/ol/geom/point.js index 931bd72bcf..03f644106d 100644 --- a/src/ol/geom/point.js +++ b/src/ol/geom/point.js @@ -2,6 +2,7 @@ goog.provide('ol.geom.Point'); goog.require('goog.asserts'); goog.require('goog.vec.Float64Array'); +goog.require('ol.Extent'); goog.require('ol.geom.Coordinate'); goog.require('ol.geom.Geometry'); @@ -25,4 +26,23 @@ ol.geom.Point = function(coordinates) { this.dimension = coordinates.length; goog.asserts.assert(this.dimension >= 2); + /** + * @type {ol.Extent} + * @private + */ + this.bounds_ = null; + +}; + + +/** + * @inheritDoc + */ +ol.geom.Point.prototype.getBounds = function() { + if (goog.isNull(this.bounds_)) { + var x = this.coordinates[0], + y = this.coordinates[1]; + this.bounds_ = new ol.Extent(x, y, x, y); + } + return this.bounds_; }; diff --git a/src/ol/geom/polygon.js b/src/ol/geom/polygon.js index ab52e13876..90a9b07c88 100644 --- a/src/ol/geom/polygon.js +++ b/src/ol/geom/polygon.js @@ -2,6 +2,7 @@ goog.provide('ol.geom.Polygon'); goog.require('goog.asserts'); goog.require('goog.vec.Float64Array'); +goog.require('ol.Extent'); goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.LinearRing'); @@ -38,4 +39,18 @@ ol.geom.Polygon = function(coordinates) { this.dimension = dimension; goog.asserts.assert(this.dimension >= 2); + /** + * @type {ol.Extent} + * @private + */ + this.bounds_ = null; + +}; + + +/** + * @inheritDoc + */ +ol.geom.Polygon.prototype.getBounds = function() { + return this.rings[0].getBounds(); }; diff --git a/test/ol.html b/test/ol.html index bdc279a547..fd2d2cb985 100644 --- a/test/ol.html +++ b/test/ol.html @@ -81,6 +81,9 @@ + + + diff --git a/test/spec/ol/geom/linearring.test.js b/test/spec/ol/geom/linearring.test.js index adace4fd66..ed67faa20d 100644 --- a/test/spec/ol/geom/linearring.test.js +++ b/test/spec/ol/geom/linearring.test.js @@ -15,7 +15,7 @@ describe('ol.geom.LinearRing', function() { }); - describe('coordinates', function() { + describe('#coordinates', function() { it('is a Float64Array', function() { var ring = new ol.geom.LinearRing([[10, 20], [30, 40]]); @@ -30,7 +30,7 @@ describe('ol.geom.LinearRing', function() { }); - describe('dimension', function() { + describe('#dimension', function() { it('can be 2', function() { var ring = new ol.geom.LinearRing([[10, 20], [30, 40]]); diff --git a/test/spec/ol/geom/linestring.test.js b/test/spec/ol/geom/linestring.test.js index bdf9e527ed..99abe77b32 100644 --- a/test/spec/ol/geom/linestring.test.js +++ b/test/spec/ol/geom/linestring.test.js @@ -15,7 +15,7 @@ describe('ol.geom.LineString', function() { }); - describe('coordinates', function() { + describe('#coordinates', function() { it('is a Float64Array', function() { var line = new ol.geom.LineString([[10, 20], [30, 40]]); @@ -30,7 +30,7 @@ describe('ol.geom.LineString', function() { }); - describe('dimension', function() { + describe('#dimension', function() { it('can be 2', function() { var line = new ol.geom.LineString([[10, 20], [30, 40]]); @@ -44,6 +44,19 @@ describe('ol.geom.LineString', function() { }); + describe('#getBounds()', function() { + + it('returns the bounding extent', function() { + var line = new ol.geom.LineString([[10, 20], [20, 30], [30, 40]]); + var bounds = line.getBounds(); + expect(bounds.minX).toBe(10); + expect(bounds.minY).toBe(20); + expect(bounds.maxX).toBe(30); + expect(bounds.maxY).toBe(40); + }); + + }); + }); diff --git a/test/spec/ol/geom/multilinestring.test.js b/test/spec/ol/geom/multilinestring.test.js new file mode 100644 index 0000000000..d5935f5f4d --- /dev/null +++ b/test/spec/ol/geom/multilinestring.test.js @@ -0,0 +1,69 @@ +describe('ol.geom.MultiLineString', function() { + + describe('constructor', function() { + + it('creates a multi-linestring from an array', function() { + var multi = new ol.geom.MultiLineString([ + [[10, 20], [30, 40]], + [[20, 30], [40, 50]]]); + expect(multi).toBeA(ol.geom.MultiLineString); + }); + + it('throws when given with insufficient dimensions', function() { + expect(function() { + var multi = new ol.geom.MultiPoint([1]); + }).toThrow(); + }); + + }); + + describe('#components', function() { + + it('is an array of linestrings', function() { + var multi = new ol.geom.MultiLineString([ + [[10, 20], [30, 40]], + [[20, 30], [40, 50]]]); + + expect(multi.components.length).toBe(2); + expect(multi.components[0]).toBeA(ol.geom.LineString); + expect(multi.components[1]).toBeA(ol.geom.LineString); + + }); + + }); + + describe('#dimension', function() { + + it('can be 2', function() { + var multi = new ol.geom.MultiLineString([ + [[10, 20], [30, 40]], + [[20, 30], [40, 50]]]); + expect(multi.dimension).toBe(2); + }); + + it('can be 3', function() { + var multi = new ol.geom.MultiLineString([ + [[10, 20, 30], [30, 40, 50]], + [[20, 30, 40], [40, 50, 60]]]); + expect(multi.dimension).toBe(3); + }); + + }); + + describe('#getBounds()', function() { + + it('returns the bounding extent', function() { + var multi = new ol.geom.MultiLineString([ + [[10, 20], [30, 40]], + [[20, 30], [40, 50]]]); + var bounds = multi.getBounds(); + expect(bounds.minX).toBe(10); + expect(bounds.minY).toBe(20); + expect(bounds.maxX).toBe(40); + expect(bounds.maxY).toBe(50); + }); + + }); + +}); + diff --git a/test/spec/ol/geom/multipoint.test.js b/test/spec/ol/geom/multipoint.test.js new file mode 100644 index 0000000000..03368c359a --- /dev/null +++ b/test/spec/ol/geom/multipoint.test.js @@ -0,0 +1,59 @@ +describe('ol.geom.MultiPoint', function() { + + describe('constructor', function() { + + it('creates a multi-point from an array', function() { + var multi = new ol.geom.MultiPoint([[10, 20], [30, 40]]); + expect(multi).toBeA(ol.geom.MultiPoint); + }); + + it('throws when given with insufficient dimensions', function() { + expect(function() { + var multi = new ol.geom.MultiPoint([1]); + }).toThrow(); + }); + + }); + + describe('#components', function() { + + it('is an array of points', function() { + var multi = new ol.geom.MultiPoint([[10, 20], [30, 40]]); + + expect(multi.components.length).toBe(2); + expect(multi.components[0]).toBeA(ol.geom.Point); + expect(multi.components[1]).toBeA(ol.geom.Point); + + }); + + }); + + describe('#dimension', function() { + + it('can be 2', function() { + var multi = new ol.geom.MultiPoint([[10, 20], [30, 40]]); + expect(multi.dimension).toBe(2); + }); + + it('can be 3', function() { + var multi = new ol.geom.MultiPoint([[10, 20, 30], [30, 40, 50]]); + expect(multi.dimension).toBe(3); + }); + + }); + + describe('#getBounds()', function() { + + it('returns the bounding extent', function() { + var multi = new ol.geom.MultiPoint([[10, 20], [30, 40]]); + var bounds = multi.getBounds(); + expect(bounds.minX).toBe(10); + expect(bounds.minY).toBe(20); + expect(bounds.maxX).toBe(30); + expect(bounds.maxY).toBe(40); + }); + + }); + +}); + diff --git a/test/spec/ol/geom/multipolygon.test.js b/test/spec/ol/geom/multipolygon.test.js new file mode 100644 index 0000000000..b9b57c3e09 --- /dev/null +++ b/test/spec/ol/geom/multipolygon.test.js @@ -0,0 +1,72 @@ +describe('ol.geom.MultiPolygon', function() { + + var outer1 = [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]], + inner1a = [[1, 1], [2, 1], [2, 2], [1, 2], [1, 1]], + inner1b = [[8, 8], [9, 8], [9, 9], [8, 9], [8, 8]], + outer2 = [[10, 10], [20, 0], [20, 50], [10, 50], [10, 10]]; + + describe('constructor', function() { + + it('creates a multi-linestring from an array', function() { + var multi = new ol.geom.MultiPolygon([ + [outer1, inner1a, inner1b], + [outer2]]); + expect(multi).toBeA(ol.geom.MultiPolygon); + }); + + it('throws when given with insufficient dimensions', function() { + expect(function() { + var multi = new ol.geom.MultiPolygon([1]); + }).toThrow(); + }); + + }); + + describe('#components', function() { + + it('is an array of polygons', function() { + var multi = new ol.geom.MultiPolygon([ + [outer1, inner1a, inner1b], + [outer2]]); + + expect(multi.components.length).toBe(2); + expect(multi.components[0]).toBeA(ol.geom.Polygon); + expect(multi.components[1]).toBeA(ol.geom.Polygon); + + }); + + }); + + describe('#dimension', function() { + + it('can be 2', function() { + var multi = new ol.geom.MultiPolygon([ + [outer1, inner1a, inner1b], + [outer2]]); + expect(multi.dimension).toBe(2); + }); + + it('can be 3', function() { + var multi = new ol.geom.MultiPolygon([[[[10, 20, 30], [40, 50, 60]]]]); + expect(multi.dimension).toBe(3); + }); + + }); + + describe('#getBounds()', function() { + + it('returns the bounding extent', function() { + var multi = new ol.geom.MultiPolygon([ + [outer1, inner1a, inner1b], + [outer2]]); + var bounds = multi.getBounds(); + expect(bounds.minX).toBe(0); + expect(bounds.minY).toBe(0); + expect(bounds.maxX).toBe(20); + expect(bounds.maxY).toBe(50); + }); + + }); + +}); + diff --git a/test/spec/ol/geom/point.test.js b/test/spec/ol/geom/point.test.js index a9ce41733e..9ec8c67879 100644 --- a/test/spec/ol/geom/point.test.js +++ b/test/spec/ol/geom/point.test.js @@ -15,7 +15,7 @@ describe('ol.geom.Point', function() { }); - describe('coordinates', function() { + describe('#coordinates', function() { it('is a Float64Array', function() { var point = new ol.geom.Point([10, 20]); @@ -29,7 +29,7 @@ describe('ol.geom.Point', function() { }); - describe('dimension', function() { + describe('#dimension', function() { it('can be 2', function() { var point = new ol.geom.Point([10, 20]); @@ -43,6 +43,18 @@ describe('ol.geom.Point', function() { }); + describe('#getBounds()', function() { + + it('returns the bounding extent', function() { + var point = new ol.geom.Point([10, 20]); + var bounds = point.getBounds(); + expect(bounds.minX).toBe(10); + expect(bounds.minY).toBe(20); + expect(bounds.maxX).toBe(10); + expect(bounds.maxY).toBe(20); + }); + + }); }); diff --git a/test/spec/ol/geom/polygon.test.js b/test/spec/ol/geom/polygon.test.js index 44b797a993..db500d1096 100644 --- a/test/spec/ol/geom/polygon.test.js +++ b/test/spec/ol/geom/polygon.test.js @@ -19,7 +19,7 @@ describe('ol.geom.Polygon', function() { }); - describe('rings', function() { + describe('#rings', function() { it('is an array of LinearRing', function() { var poly = new ol.geom.Polygon([outer, inner1, inner2]); @@ -32,7 +32,7 @@ describe('ol.geom.Polygon', function() { }); - describe('dimension', function() { + describe('#dimension', function() { it('can be 2', function() { var poly = new ol.geom.Polygon([outer, inner1, inner2]); @@ -46,6 +46,19 @@ describe('ol.geom.Polygon', function() { }); + describe('#getBounds()', function() { + + it('returns the bounding extent', function() { + var poly = new ol.geom.Polygon([outer, inner1, inner2]); + var bounds = poly.getBounds(); + expect(bounds.minX).toBe(0); + expect(bounds.minY).toBe(0); + expect(bounds.maxX).toBe(10); + expect(bounds.maxY).toBe(10); + }); + + }); + });