diff --git a/src/ol/geom/geometry.js b/src/ol/geom/geometry.js index 76ede57540..ca079dd89c 100644 --- a/src/ol/geom/geometry.js +++ b/src/ol/geom/geometry.js @@ -1,5 +1,3 @@ -// FIXME add GeometryCollection - goog.provide('ol.geom.Geometry'); goog.require('goog.asserts'); @@ -18,7 +16,8 @@ ol.geom.GeometryType = { POLYGON: 'Polygon', MULTI_POINT: 'MultiPoint', MULTI_LINE_STRING: 'MultiLineString', - MULTI_POLYGON: 'MultiPolygon' + MULTI_POLYGON: 'MultiPolygon', + GEOMETRY_COLLECTION: 'GeometryCollection' }; diff --git a/src/ol/geom/geometrycollection.exports b/src/ol/geom/geometrycollection.exports new file mode 100644 index 0000000000..103ce2c40f --- /dev/null +++ b/src/ol/geom/geometrycollection.exports @@ -0,0 +1,7 @@ +@exportSymbol ol.geom.GeometryCollection +@exportProperty ol.geom.GeometryCollection.prototype.clone +@exportProperty ol.geom.GeometryCollection.prototype.getExtent +@exportProperty ol.geom.GeometryCollection.prototype.getGeometries +@exportProperty ol.geom.GeometryCollection.prototype.getSimplifiedGeometry +@exportProperty ol.geom.GeometryCollection.prototype.getType +@exportProperty ol.geom.GeometryCollection.prototype.setGeometries diff --git a/src/ol/geom/geometrycollection.js b/src/ol/geom/geometrycollection.js new file mode 100644 index 0000000000..7765699b0c --- /dev/null +++ b/src/ol/geom/geometrycollection.js @@ -0,0 +1,204 @@ +goog.provide('ol.geom.GeometryCollection'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.object'); +goog.require('ol.extent'); +goog.require('ol.geom.Geometry'); + + + +/** + * @constructor + * @extends {ol.geom.Geometry} + * @param {Array.=} opt_geometries Geometries. + */ +ol.geom.GeometryCollection = function(opt_geometries) { + + goog.base(this); + + /** + * @private + * @type {Array.} + */ + this.geometries_ = goog.isDef(opt_geometries) ? opt_geometries : null; + +}; +goog.inherits(ol.geom.GeometryCollection, ol.geom.Geometry); + + +/** + * @param {Array.} geometries Geometries. + * @private + * @return {Array.} Cloned geometries. + */ +ol.geom.GeometryCollection.cloneGeometries_ = function(geometries) { + var clonedGeometries = []; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + clonedGeometries.push(geometries[i].clone()); + } + return clonedGeometries; +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.clone = function() { + var geometryCollection = new ol.geom.GeometryCollection(null); + geometryCollection.setGeometries(this.geometries_); + return geometryCollection; +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.closestPointXY = + function(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < + ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + var geometries = this.geometries_; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + minSquaredDistance = geometries[i].closestPointXY( + x, y, closestPoint, minSquaredDistance); + } + return minSquaredDistance; +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.containsXY = function(x, y) { + var geometries = this.geometries_; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + if (geometries[i].containsXY(x, y)) { + return true; + } + } + return false; +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.getExtent = function(opt_extent) { + if (this.extentRevision != this.revision) { + var extent = ol.extent.createOrUpdateEmpty(this.extent); + var geometries = this.geometries_; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + ol.extent.extend(extent, geometries[i].getExtent()); + } + this.extent = extent; + this.extentRevision = this.revision; + } + goog.asserts.assert(goog.isDef(this.extent)); + return ol.extent.returnOrUpdate(this.extent, opt_extent); +}; + + +/** + * @return {Array.} Geometries. + */ +ol.geom.GeometryCollection.prototype.getGeometries = function() { + return ol.geom.GeometryCollection.cloneGeometries_(this.geometries_); +}; + + +/** + * @return {Array.} Geometries. + */ +ol.geom.GeometryCollection.prototype.getGeometriesArray = function() { + return this.geometries_; +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.getSimplifiedGeometry = + function(squaredTolerance) { + if (this.simplifiedGeometryRevision != this.revision) { + goog.object.clear(this.simplifiedGeometryCache); + this.simplifiedGeometryMaxMinSquaredTolerance = 0; + this.simplifiedGeometryRevision = this.revision; + } + if (squaredTolerance < 0 || + (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 && + squaredTolerance < this.simplifiedGeometryMaxMinSquaredTolerance)) { + return this; + } + var key = squaredTolerance.toString(); + if (this.simplifiedGeometryCache.hasOwnProperty(key)) { + return this.simplifiedGeometryCache[key]; + } else { + var simplifiedGeometries = []; + var geometries = this.geometries_; + var simplified = false; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + var geometry = geometries[i]; + var simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance); + simplifiedGeometries.push(simplifiedGeometry); + if (simplifiedGeometry !== geometry) { + simplified = true; + } + } + if (simplified) { + var simplifiedGeometryCollection = new ol.geom.GeometryCollection(null); + simplifiedGeometryCollection.setGeometriesArray(simplifiedGeometries); + this.simplifiedGeometryCache[key] = simplifiedGeometryCollection; + return simplifiedGeometryCollection; + } else { + this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance; + return this; + } + } +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.getType = function() { + return ol.geom.GeometryType.GEOMETRY_COLLECTION; +}; + + +/** + * @param {Array.} geometries Geometries. + */ +ol.geom.GeometryCollection.prototype.setGeometries = function(geometries) { + this.setGeometriesArray( + ol.geom.GeometryCollection.cloneGeometries_(geometries)); +}; + + +/** + * @param {Array.} geometries Geometries. + */ +ol.geom.GeometryCollection.prototype.setGeometriesArray = function(geometries) { + this.geometries_ = geometries; + this.dispatchChangeEvent(); +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.transform = function(transformFn) { + var geometries = this.geometries_; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + geometries[i].transform(transformFn); + } + this.dispatchChangeEvent(); +}; diff --git a/test/spec/ol/geom/geometrycollection.test.js b/test/spec/ol/geom/geometrycollection.test.js new file mode 100644 index 0000000000..f409fbf7dd --- /dev/null +++ b/test/spec/ol/geom/geometrycollection.test.js @@ -0,0 +1,110 @@ +goog.provide('ol.test.geom.GeometryCollection'); + + +describe('ol.geom.GeometryCollection', function() { + + var outer = [[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]], + inner1 = [[1, 1], [2, 1], [2, 2], [1, 2], [1, 1]], + inner2 = [[8, 8], [9, 8], [9, 9], [8, 9], [8, 8]]; + + describe('constructor', function() { + + it('creates a geometry collection from an array of geometries', function() { + var point = new ol.geom.Point([10, 20]); + var line = new ol.geom.LineString([[10, 20], [30, 40]]); + var poly = new ol.geom.Polygon([outer, inner1, inner2]); + var multi = new ol.geom.GeometryCollection([point, line, poly]); + expect(multi).to.be.a(ol.geom.GeometryCollection); + expect(multi).to.be.a(ol.geom.Geometry); + }); + + }); + + describe('#getGeometries', function() { + + it('returns a collection of geometries', function() { + var point = new ol.geom.Point([10, 20]); + var line = new ol.geom.LineString([[10, 20], [30, 40]]); + var poly = new ol.geom.Polygon([outer, inner1, inner2]); + var multi = new ol.geom.GeometryCollection([point, line, poly]); + + var geometries = multi.getGeometries(); + expect(geometries).to.be.an(Array); + expect(geometries).to.have.length(3); + expect(geometries[0]).to.be.a(ol.geom.Point); + expect(geometries[1]).to.be.a(ol.geom.LineString); + expect(geometries[2]).to.be.a(ol.geom.Polygon); + }); + + }); + + describe('#clone()', function() { + + it('has a working clone method', function() { + var point = new ol.geom.Point([10, 20]); + var line = new ol.geom.LineString([[10, 20], [30, 40]]); + var poly = new ol.geom.Polygon([outer, inner1, inner2]); + var multi = new ol.geom.GeometryCollection([point, line, poly]); + var clone = multi.clone(); + expect(clone).to.not.be(multi); + var geometries = clone.getGeometries(); + expect(geometries[0].getCoordinates()).to.eql([10, 20]); + expect(geometries[1].getCoordinates()).to.eql([[10, 20], [30, 40]]); + expect(geometries[2].getCoordinates()).to.eql([outer, inner1, inner2]); + }); + + }); + + describe('#getExtent()', function() { + + it('returns the bounding extent', function() { + var point = new ol.geom.Point([10, 2]); + var line = new ol.geom.LineString([[1, 20], [30, 40]]); + var multi = new ol.geom.GeometryCollection([point, line]); + var extent = multi.getExtent(); + expect(extent[0]).to.be(1); + expect(extent[2]).to.be(30); + expect(extent[1]).to.be(2); + expect(extent[3]).to.be(40); + }); + + }); + + describe('#setGeometries', function() { + + var line, multi, point, poly; + beforeEach(function() { + point = new ol.geom.Point([10, 20]); + line = new ol.geom.LineString([[10, 20], [30, 40]]); + poly = new ol.geom.Polygon([outer, inner1, inner2]); + multi = new ol.geom.GeometryCollection([point, line, poly]); + }); + + it('fires a change event', function() { + var listener = sinon.spy(); + multi.on('change', listener); + point.setCoordinates([15, 25]); + expect(listener.calledOnce).to.be(false); + multi.setGeometries([point, line, poly]); + expect(listener.calledOnce).to.be(true); + }); + + it('updates the extent', function() { + expect(multi.getExtent()).to.eql([0, 0, 30, 40]); + line.setCoordinates([[10, 20], [300, 400]]); + expect(multi.getExtent()).to.eql([0, 0, 30, 40]); + multi.setGeometries([point, line, poly]); + expect(multi.getExtent()).to.eql([0, 0, 300, 400]); + }); + + }); + +}); + + +goog.require('ol.Collection'); +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryCollection'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon');