From b52d2836414d6f3b823b789c2cc996fa76f9325f Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 2 Mar 2013 18:39:24 +0100 Subject: [PATCH] Allow geometries to use a shared vertex array The ol.geom.SharedVertices structure represents a flattened array of vertex coordinates. This is intended to support optimal WebGL rendering. --- src/ol/geom/abstractcollection.js | 79 ++++++++ src/ol/geom/base.js | 14 ++ src/ol/geom/geometry.js | 43 ++-- src/ol/geom/geometrycollection.js | 60 ++---- src/ol/geom/linearring.js | 15 +- src/ol/geom/linestring.js | 102 +++++++--- src/ol/geom/multilinestring.js | 52 +++-- src/ol/geom/multipoint.js | 57 ++++-- src/ol/geom/multipolygon.js | 52 +++-- src/ol/geom/point.js | 64 +++++- src/ol/geom/polygon.js | 47 +++-- src/ol/geom/sharedvertices.js | 195 +++++++++++++++++++ src/ol/renderer/canvas/canvasrenderer.js | 33 ++-- test/spec/ol/geom/geometrycollection.test.js | 79 ++++++++ test/spec/ol/geom/linearring.test.js | 22 +-- test/spec/ol/geom/linestring.test.js | 61 ++++-- test/spec/ol/geom/multilinestring.test.js | 13 ++ test/spec/ol/geom/multipoint.test.js | 9 + test/spec/ol/geom/multipolygon.test.js | 14 ++ test/spec/ol/geom/point.test.js | 68 +++++-- test/spec/ol/geom/polygon.test.js | 21 +- test/spec/ol/geom/sharedvertices.test.js | 195 +++++++++++++++++++ test/spec/ol/parser/geojson.test.js | 8 +- 23 files changed, 1086 insertions(+), 217 deletions(-) create mode 100644 src/ol/geom/abstractcollection.js create mode 100644 src/ol/geom/base.js create mode 100644 src/ol/geom/sharedvertices.js create mode 100644 test/spec/ol/geom/geometrycollection.test.js create mode 100644 test/spec/ol/geom/sharedvertices.test.js diff --git a/src/ol/geom/abstractcollection.js b/src/ol/geom/abstractcollection.js new file mode 100644 index 0000000000..b5ce4ee588 --- /dev/null +++ b/src/ol/geom/abstractcollection.js @@ -0,0 +1,79 @@ +goog.provide('ol.geom.AbstractCollection'); + +goog.require('ol.Extent'); +goog.require('ol.geom.Geometry'); + + + +/** + * A collection of geometries. This constructor is not to be used directly. + * + * @constructor + * @extends {ol.geom.Geometry} + */ +ol.geom.AbstractCollection = function() { + goog.base(this); + + /** + * @type {number} + */ + this.dimension; + + /** + * @type {Array.} + */ + this.components = null; + + /** + * @type {ol.Extent} + * @protected + */ + this.bounds = null; + +}; +goog.inherits(ol.geom.AbstractCollection, ol.geom.Geometry); + + +/** + * @inheritDoc + */ +ol.geom.AbstractCollection.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; +}; + + +/** + * @inheritDoc + */ +ol.geom.AbstractCollection.prototype.getCoordinates = function() { + var count = this.components.length; + var coordinates = new Array(count); + for (var i = 0; i < count; ++i) { + coordinates[i] = this.components[i].getCoordinates(); + } + return coordinates; +}; + + +/** + * @inheritDoc + */ +ol.geom.AbstractCollection.prototype.getType = goog.abstractMethod; diff --git a/src/ol/geom/base.js b/src/ol/geom/base.js new file mode 100644 index 0000000000..31ebb15f95 --- /dev/null +++ b/src/ol/geom/base.js @@ -0,0 +1,14 @@ +goog.provide('ol.geom.Vertex'); +goog.provide('ol.geom.VertexArray'); + + +/** + * @typedef {Array.} + */ +ol.geom.Vertex; + + +/** + * @typedef {Array.} + */ +ol.geom.VertexArray; diff --git a/src/ol/geom/geometry.js b/src/ol/geom/geometry.js index b2ab428bf5..4e61d692f6 100644 --- a/src/ol/geom/geometry.js +++ b/src/ol/geom/geometry.js @@ -1,15 +1,23 @@ -goog.require('ol.Extent'); -goog.provide('ol.geom.Coordinate'); -goog.provide('ol.geom.CoordinateArray'); goog.provide('ol.geom.Geometry'); goog.provide('ol.geom.GeometryType'); +goog.require('ol.Extent'); +goog.require('ol.geom.SharedVertices'); + /** * @constructor */ -ol.geom.Geometry = function() {}; +ol.geom.Geometry = function() { + + /** + * @type {ol.geom.SharedVertices} + * @protected + */ + this.vertices = null; + +}; /** @@ -26,6 +34,21 @@ ol.geom.Geometry.prototype.dimension; ol.geom.Geometry.prototype.getBounds = goog.abstractMethod; +/** + * @return {Array} The GeoJSON style coordinates array for the geometry. + */ +ol.geom.Geometry.prototype.getCoordinates = goog.abstractMethod; + + +/** + * Get the shared vertices for this geometry. + * @return {ol.geom.SharedVertices} The shared vertices. + */ +ol.geom.Geometry.prototype.getSharedVertices = function() { + return this.vertices; +}; + + /** * Get the geometry type. * @return {ol.geom.GeometryType} The geometry type. @@ -33,18 +56,6 @@ ol.geom.Geometry.prototype.getBounds = goog.abstractMethod; ol.geom.Geometry.prototype.getType = goog.abstractMethod; -/** - * @typedef {Array.} - */ -ol.geom.Coordinate; - - -/** - * @typedef {Array.} - */ -ol.geom.CoordinateArray; - - /** * @enum {string} */ diff --git a/src/ol/geom/geometrycollection.js b/src/ol/geom/geometrycollection.js index 277eafa899..8585d95394 100644 --- a/src/ol/geom/geometrycollection.js +++ b/src/ol/geom/geometrycollection.js @@ -1,65 +1,43 @@ goog.provide('ol.geom.GeometryCollection'); -goog.require('ol.Extent'); +goog.require('ol.geom.AbstractCollection'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.GeometryType'); /** - * A collection of geometries. This constructor should not called. Instead - * create one of the fixed type collections. + * A mixed collection of geometries. Used one of the fixed type multi-part + * constructors for collections of the same type. + * * @constructor - * @extends {ol.geom.Geometry} + * @extends {ol.geom.AbstractCollection} + * @param {Array.} geometries Array of geometries. */ -ol.geom.GeometryCollection = function() { - +ol.geom.GeometryCollection = function(geometries) { goog.base(this); /** * @type {Array.} */ - this.components = null; + this.components = geometries; + + var dimension = 0; + for (var i = 0, ii = geometries.length; i < ii; ++i) { + if (goog.isDef(dimension)) { + dimension = geometries[i].dimension; + } else { + goog.asserts.assert(dimension == geometries[i].dimension); + } + } /** * @type {number} */ - this.dimension; - - /** - * @type {ol.Extent} - * @protected - */ - this.bounds = null; + this.dimension = dimension; }; -goog.inherits(ol.geom.GeometryCollection, ol.geom.Geometry); - - -/** - * @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; -}; +goog.inherits(ol.geom.GeometryCollection, ol.geom.AbstractCollection); /** diff --git a/src/ol/geom/linearring.js b/src/ol/geom/linearring.js index 92d181e6e0..0d2180be87 100644 --- a/src/ol/geom/linearring.js +++ b/src/ol/geom/linearring.js @@ -1,20 +1,21 @@ goog.provide('ol.geom.LinearRing'); -goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); +goog.require('ol.geom.SharedVertices'); +goog.require('ol.geom.VertexArray'); /** * @constructor * @extends {ol.geom.LineString} - * @param {ol.geom.CoordinateArray} coordinates Coordinates array (e.g. - * [[x0, y0], [x1, y1], [x0, y0]]). + * @param {ol.geom.VertexArray} coordinates Vertex array (e.g. + * [[x0, y0], [x1, y1]]). + * @param {ol.geom.SharedVertices=} opt_shared Shared vertices. */ -ol.geom.LinearRing = function(coordinates) { - - goog.base(this, coordinates); +ol.geom.LinearRing = function(coordinates, opt_shared) { + goog.base(this, coordinates, opt_shared); /** * We're intentionally not enforcing that rings be closed right now. This @@ -30,5 +31,5 @@ goog.inherits(ol.geom.LinearRing, ol.geom.LineString); * @inheritDoc */ ol.geom.LinearRing.prototype.getType = function() { - return ol.geom.GeometryType.GEOMETRYCOLLECTION; + return ol.geom.GeometryType.LINEARRING; }; diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index 6d8be32f5c..561c973a33 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -2,44 +2,47 @@ goog.provide('ol.geom.LineString'); goog.require('goog.asserts'); goog.require('ol.Extent'); -goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.SharedVertices'); +goog.require('ol.geom.VertexArray'); /** * @constructor * @extends {ol.geom.Geometry} - * @param {ol.geom.CoordinateArray} coordinates Coordinates array (e.g. + * @param {ol.geom.VertexArray} coordinates Vertex array (e.g. * [[x0, y0], [x1, y1]]). + * @param {ol.geom.SharedVertices=} opt_shared Shared vertices. */ -ol.geom.LineString = function(coordinates) { - +ol.geom.LineString = function(coordinates, opt_shared) { goog.base(this); + goog.asserts.assert(goog.isArray(coordinates[0])); - // assume the same dimension for all coordinates - var dimension = coordinates[0].length, - count = coordinates.length, - length = count * dimension; + var vertices = opt_shared, + dimension; + + if (!goog.isDef(vertices)) { + dimension = coordinates[0].length; + vertices = new ol.geom.SharedVertices({dimension: dimension}); + } /** - * @type {Array} + * @type {ol.geom.SharedVertices} */ - this.coordinates = new Array(length); - var i, offset, j; - for (i = 0; i < count; ++i) { - goog.asserts.assert(coordinates[i].length === dimension); - offset = i * dimension; - for (j = 0; j < dimension; ++j) { - this.coordinates[offset + j] = coordinates[i][j]; - } - } + this.vertices = vertices; + + /** + * @type {string} + * @private + */ + this.sharedId_ = vertices.add(coordinates); /** * @type {number} */ - this.dimension = dimension; + this.dimension = vertices.getDimension(); goog.asserts.assert(this.dimension >= 2); /** @@ -52,6 +55,45 @@ ol.geom.LineString = function(coordinates) { goog.inherits(ol.geom.LineString, ol.geom.Geometry); +/** + * Get a vertex coordinate value for the given dimension. + * @param {number} index Vertex index. + * @param {number} dim Coordinate dimension. + * @return {number} The vertex coordinate value. + */ +ol.geom.LineString.prototype.get = function(index, dim) { + return this.vertices.get(this.sharedId_, index, dim); +}; + + +/** + * @inheritDoc + * @return {ol.geom.VertexArray} Coordinates array. + */ +ol.geom.LineString.prototype.getCoordinates = function() { + var count = this.getCount(); + var coordinates = new Array(count); + var vertex; + for (var i = 0; i < count; ++i) { + vertex = new Array(this.dimension); + for (var j = 0; j < this.dimension; ++j) { + vertex[j] = this.get(i, j); + } + coordinates[i] = vertex; + } + return coordinates; +}; + + +/** + * Get the count of vertices in this linestring. + * @return {number} The vertex count. + */ +ol.geom.LineString.prototype.getCount = function() { + return this.vertices.getCount(this.sharedId_); +}; + + /** * @inheritDoc */ @@ -61,14 +103,15 @@ ol.geom.LineString.prototype.getBounds = function() { minY = minX = Number.POSITIVE_INFINITY, maxX, maxY = maxX = Number.NEGATIVE_INFINITY, - coordinates = this.coordinates, - len = coordinates.length, - dim = this.dimension, + vertices = this.vertices, + id = this.sharedId_, + count = vertices.getCount(id), + dimension = this.dimension, x, y, i; - for (i = 0; i < len; i += dim) { - x = coordinates[i]; - y = coordinates[i + 1]; + for (i = 0; i < count; ++i) { + x = vertices.get(id, i, 0); + y = vertices.get(id, i, 1); minX = Math.min(minX, x); minY = Math.min(minY, y); maxX = Math.max(maxX, x); @@ -86,3 +129,12 @@ ol.geom.LineString.prototype.getBounds = function() { ol.geom.LineString.prototype.getType = function() { return ol.geom.GeometryType.LINESTRING; }; + + +/** + * Get the identifier used to mark this line in the shared vertices structure. + * @return {string} The identifier. + */ +ol.geom.LineString.prototype.getSharedId = function() { + return this.sharedId_; +}; diff --git a/src/ol/geom/multilinestring.js b/src/ol/geom/multilinestring.js index c9def94fbe..71f99afbe1 100644 --- a/src/ol/geom/multilinestring.js +++ b/src/ol/geom/multilinestring.js @@ -1,45 +1,50 @@ goog.provide('ol.geom.MultiLineString'); goog.require('goog.asserts'); -goog.require('ol.geom.CoordinateArray'); -goog.require('ol.geom.GeometryCollection'); +goog.require('ol.geom.AbstractCollection'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); +goog.require('ol.geom.SharedVertices'); +goog.require('ol.geom.VertexArray'); /** * @constructor - * @extends {ol.geom.GeometryCollection} - * @param {Array.} coordinates Coordinates array. + * @extends {ol.geom.AbstractCollection} + * @param {Array.} coordinates Coordinates array. + * @param {ol.geom.SharedVertices=} opt_shared Shared vertices. */ -ol.geom.MultiLineString = function(coordinates) { +ol.geom.MultiLineString = function(coordinates, opt_shared) { goog.base(this); + goog.asserts.assert(goog.isArray(coordinates[0][0])); - var numParts = coordinates.length, + var vertices = opt_shared, dimension; + if (!goog.isDef(vertices)) { + // try to get dimension from first vertex in first line + dimension = coordinates[0][0].length; + vertices = new ol.geom.SharedVertices({dimension: dimension}); + } + + var numParts = coordinates.length; + /** * @type {Array.} */ this.components = new Array(numParts); 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; - } else { - goog.asserts.assert(this.components[i].dimension === dimension); - } + this.components[i] = new ol.geom.LineString(coordinates[i], vertices); } /** * @type {number} */ - this.dimension = dimension; - goog.asserts.assert(this.dimension >= 2); + this.dimension = vertices.getDimension(); }; -goog.inherits(ol.geom.MultiLineString, ol.geom.GeometryCollection); +goog.inherits(ol.geom.MultiLineString, ol.geom.AbstractCollection); /** @@ -48,3 +53,20 @@ goog.inherits(ol.geom.MultiLineString, ol.geom.GeometryCollection); ol.geom.MultiLineString.prototype.getType = function() { return ol.geom.GeometryType.MULTILINESTRING; }; + + +/** + * Create a multi-linestring geometry from an array of linestring geometries. + * + * @param {Array.} geometries Array of geometries. + * @param {ol.geom.SharedVertices=} opt_shared Shared vertices. + * @return {ol.geom.MultiLineString} A new geometry. + */ +ol.geom.MultiLineString.fromParts = function(geometries, opt_shared) { + var count = geometries.length; + var coordinates = new Array(count); + for (var i = 0; i < count; ++i) { + coordinates[i] = geometries[i].getCoordinates(); + } + return new ol.geom.MultiLineString(coordinates, opt_shared); +}; diff --git a/src/ol/geom/multipoint.js b/src/ol/geom/multipoint.js index e4d632e26d..43c475bfeb 100644 --- a/src/ol/geom/multipoint.js +++ b/src/ol/geom/multipoint.js @@ -1,45 +1,55 @@ goog.provide('ol.geom.MultiPoint'); goog.require('goog.asserts'); -goog.require('ol.geom.CoordinateArray'); -goog.require('ol.geom.GeometryCollection'); +goog.require('ol.geom.AbstractCollection'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.Point'); +goog.require('ol.geom.SharedVertices'); +goog.require('ol.geom.VertexArray'); /** * @constructor - * @extends {ol.geom.GeometryCollection} - * @param {ol.geom.CoordinateArray} coordinates Coordinates array. + * @extends {ol.geom.AbstractCollection} + * @param {ol.geom.VertexArray} coordinates Coordinates array. + * @param {ol.geom.SharedVertices=} opt_shared Shared vertices. */ -ol.geom.MultiPoint = function(coordinates) { +ol.geom.MultiPoint = function(coordinates, opt_shared) { goog.base(this); + goog.asserts.assert(goog.isArray(coordinates[0])); - var numParts = coordinates.length, + var vertices = opt_shared, dimension; + if (!goog.isDef(vertices)) { + // try to get dimension from first vertex + dimension = coordinates[0].length; + vertices = new ol.geom.SharedVertices({dimension: dimension}); + } + + /** + * @type {ol.geom.SharedVertices} + */ + this.vertices = vertices; + + var numParts = coordinates.length; + /** * @type {Array.} */ this.components = new Array(numParts); for (var i = 0; i < numParts; ++i) { - this.components[i] = new ol.geom.Point(coordinates[i]); - if (!goog.isDef(dimension)) { - dimension = this.components[i].dimension; - } else { - goog.asserts.assert(this.components[i].dimension === dimension); - } + this.components[i] = new ol.geom.Point(coordinates[i], vertices); } /** * @type {number} */ - this.dimension = dimension; - goog.asserts.assert(this.dimension >= 2); + this.dimension = vertices.getDimension(); }; -goog.inherits(ol.geom.MultiPoint, ol.geom.GeometryCollection); +goog.inherits(ol.geom.MultiPoint, ol.geom.AbstractCollection); /** @@ -48,3 +58,20 @@ goog.inherits(ol.geom.MultiPoint, ol.geom.GeometryCollection); ol.geom.MultiPoint.prototype.getType = function() { return ol.geom.GeometryType.MULTIPOINT; }; + + +/** + * Create a multi-point geometry from an array of point geometries. + * + * @param {Array.} geometries Array of geometries. + * @param {ol.geom.SharedVertices=} opt_shared Shared vertices. + * @return {ol.geom.MultiPoint} A new geometry. + */ +ol.geom.MultiPoint.fromParts = function(geometries, opt_shared) { + var count = geometries.length; + var coordinates = new Array(count); + for (var i = 0; i < count; ++i) { + coordinates[i] = geometries[i].getCoordinates(); + } + return new ol.geom.MultiPoint(coordinates, opt_shared); +}; diff --git a/src/ol/geom/multipolygon.js b/src/ol/geom/multipolygon.js index dcc0ec89c3..b3c87c6175 100644 --- a/src/ol/geom/multipolygon.js +++ b/src/ol/geom/multipolygon.js @@ -1,46 +1,51 @@ goog.provide('ol.geom.MultiPolygon'); goog.require('goog.asserts'); -goog.require('ol.geom.CoordinateArray'); -goog.require('ol.geom.GeometryCollection'); +goog.require('ol.geom.AbstractCollection'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.Polygon'); +goog.require('ol.geom.SharedVertices'); +goog.require('ol.geom.VertexArray'); /** * @constructor - * @extends {ol.geom.GeometryCollection} - * @param {Array.>} coordinates Coordinates + * @extends {ol.geom.AbstractCollection} + * @param {Array.>} coordinates Coordinates * array. + * @param {ol.geom.SharedVertices=} opt_shared Shared vertices. */ -ol.geom.MultiPolygon = function(coordinates) { +ol.geom.MultiPolygon = function(coordinates, opt_shared) { goog.base(this); + goog.asserts.assert(goog.isArray(coordinates[0][0][0])); - var numParts = coordinates.length, + var vertices = opt_shared, dimension; + if (!goog.isDef(vertices)) { + // try to get dimension from first vertex in first ring of the first poly + dimension = coordinates[0][0][0].length; + vertices = new ol.geom.SharedVertices({dimension: dimension}); + } + + var numParts = coordinates.length; + /** * @type {Array.} */ this.components = new Array(numParts); 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; - } else { - goog.asserts.assert(this.components[i].dimension === dimension); - } + this.components[i] = new ol.geom.Polygon(coordinates[i], vertices); } /** * @type {number} */ - this.dimension = dimension; - goog.asserts.assert(this.dimension >= 2); + this.dimension = vertices.getDimension(); }; -goog.inherits(ol.geom.MultiPolygon, ol.geom.GeometryCollection); +goog.inherits(ol.geom.MultiPolygon, ol.geom.AbstractCollection); /** @@ -49,3 +54,20 @@ goog.inherits(ol.geom.MultiPolygon, ol.geom.GeometryCollection); ol.geom.MultiPolygon.prototype.getType = function() { return ol.geom.GeometryType.MULTIPOLYGON; }; + + +/** + * Create a multi-polygon geometry from an array of polygon geometries. + * + * @param {Array.} geometries Array of geometries. + * @param {ol.geom.SharedVertices=} opt_shared Shared vertices. + * @return {ol.geom.MultiPolygon} A new geometry. + */ +ol.geom.MultiPolygon.fromParts = function(geometries, opt_shared) { + var count = geometries.length; + var coordinates = new Array(count); + for (var i = 0; i < count; ++i) { + coordinates[i] = geometries[i].getCoordinates(); + } + return new ol.geom.MultiPolygon(coordinates, opt_shared); +}; diff --git a/src/ol/geom/point.js b/src/ol/geom/point.js index 59790ce6ec..621a6e4f13 100644 --- a/src/ol/geom/point.js +++ b/src/ol/geom/point.js @@ -2,30 +2,45 @@ goog.provide('ol.geom.Point'); goog.require('goog.asserts'); goog.require('ol.Extent'); -goog.require('ol.geom.Coordinate'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.SharedVertices'); +goog.require('ol.geom.Vertex'); /** * @constructor * @extends {ol.geom.Geometry} - * @param {ol.geom.Coordinate} coordinates Coordinates array (e.g. [x, y]). + * @param {ol.geom.Vertex} coordinates Coordinates array (e.g. [x, y]). + * @param {ol.geom.SharedVertices=} opt_shared Shared vertices. */ -ol.geom.Point = function(coordinates) { - +ol.geom.Point = function(coordinates, opt_shared) { goog.base(this); + var vertices = opt_shared, + dimension; + + if (!goog.isDef(vertices)) { + dimension = coordinates.length; + vertices = new ol.geom.SharedVertices({dimension: dimension}); + } + /** - * @type {Array} + * @type {ol.geom.SharedVertices} */ - this.coordinates = coordinates; + this.vertices = vertices; + + /** + * @type {string} + * @private + */ + this.sharedId_ = vertices.add([coordinates]); /** * @type {number} */ - this.dimension = coordinates.length; + this.dimension = vertices.getDimension(); goog.asserts.assert(this.dimension >= 2); /** @@ -38,22 +53,53 @@ ol.geom.Point = function(coordinates) { goog.inherits(ol.geom.Point, ol.geom.Geometry); +/** + * @param {number} dim Coordinate dimension. + * @return {number} The coordinate value. + */ +ol.geom.Point.prototype.get = function(dim) { + return this.vertices.get(this.sharedId_, 0, dim); +}; + + /** * @inheritDoc */ ol.geom.Point.prototype.getBounds = function() { if (goog.isNull(this.bounds_)) { - var x = this.coordinates[0], - y = this.coordinates[1]; + var x = this.get(0), + y = this.get(1); this.bounds_ = new ol.Extent(x, y, x, y); } return this.bounds_; }; +/** + * @inheritDoc + * @return {ol.geom.Vertex} Coordinates array. + */ +ol.geom.Point.prototype.getCoordinates = function() { + var coordinates = new Array(this.dimension); + for (var i = 0; i < this.dimension; ++i) { + coordinates[i] = this.get(i); + } + return coordinates; +}; + + /** * @inheritDoc */ ol.geom.Point.prototype.getType = function() { return ol.geom.GeometryType.POINT; }; + + +/** + * Get the identifier used to mark this point in the shared vertices structure. + * @return {string} The identifier. + */ +ol.geom.Point.prototype.getSharedId = function() { + return this.sharedId_; +}; diff --git a/src/ol/geom/polygon.js b/src/ol/geom/polygon.js index c3c3e88a76..c38b4aa57a 100644 --- a/src/ol/geom/polygon.js +++ b/src/ol/geom/polygon.js @@ -2,43 +2,53 @@ goog.provide('ol.geom.Polygon'); goog.require('goog.asserts'); goog.require('ol.Extent'); -goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LinearRing'); +goog.require('ol.geom.SharedVertices'); +goog.require('ol.geom.VertexArray'); /** * @constructor * @extends {ol.geom.Geometry} - * @param {Array.} coordinates Array of rings. First + * @param {Array.} coordinates Array of rings. First * is outer, any remaining are inner. + * @param {ol.geom.SharedVertices=} opt_shared Shared vertices. */ -ol.geom.Polygon = function(coordinates) { - +ol.geom.Polygon = function(coordinates, opt_shared) { goog.base(this); + goog.asserts.assert(goog.isArray(coordinates[0][0])); - var numRings = coordinates.length, + var vertices = opt_shared, dimension; + if (!goog.isDef(vertices)) { + // try to get dimension from first vertex in first ring + dimension = coordinates[0][0].length; + vertices = new ol.geom.SharedVertices({dimension: dimension}); + } + + /** + * @type {ol.geom.SharedVertices} + */ + this.vertices = vertices; + + var numRings = coordinates.length; + /** * @type {Array.} */ this.rings = new Array(numRings); for (var i = 0; i < numRings; ++i) { - this.rings[i] = new ol.geom.LinearRing(coordinates[i]); - if (!goog.isDef(dimension)) { - dimension = this.rings[i].dimension; - } else { - goog.asserts.assert(this.rings[i].dimension === dimension); - } + this.rings[i] = new ol.geom.LinearRing(coordinates[i], vertices); } /** * @type {number} */ - this.dimension = dimension; + this.dimension = vertices.getDimension(); goog.asserts.assert(this.dimension >= 2); /** @@ -59,6 +69,19 @@ ol.geom.Polygon.prototype.getBounds = function() { }; +/** + * @return {Array.} Coordinates array. + */ +ol.geom.Polygon.prototype.getCoordinates = function() { + var count = this.rings.length; + var coordinates = new Array(count); + for (var i = 0; i < count; ++i) { + coordinates[i] = this.rings[i].getCoordinates(); + } + return coordinates; +}; + + /** * @inheritDoc */ diff --git a/src/ol/geom/sharedvertices.js b/src/ol/geom/sharedvertices.js new file mode 100644 index 0000000000..39ff922027 --- /dev/null +++ b/src/ol/geom/sharedvertices.js @@ -0,0 +1,195 @@ +goog.provide('ol.geom.SharedVertices'); + +goog.require('goog.asserts'); +goog.require('ol.geom.Vertex'); +goog.require('ol.geom.VertexArray'); + + +/** + * @typedef {{dimension: (number), + * offset: (ol.geom.Vertex|undefined)}} + */ +ol.geom.SharedVerticesOptions; + + + +/** + * Provides methods for dealing with shared, flattened arrays of vertices. + * + * @constructor + * @param {ol.geom.SharedVerticesOptions=} opt_options Shared vertices options. + */ +ol.geom.SharedVertices = function(opt_options) { + var options = goog.isDef(opt_options) ? opt_options : {}; + + /** + * @type {number} + * @private + */ + this.counter_ = 0; + + /** + * @type {Array.} + */ + this.coordinates = []; + + /** + * Number of dimensions per vertex. Default is 2. + * @type {number} + * @private + */ + this.dimension_ = options.dimension || 2; + + /** + * Vertex offset. + * @type {Array.} + * @private + */ + this.offset_ = options.offset || null; + goog.asserts.assert(goog.isNull(this.offset_) || + this.offset_.length === this.dimension_); + + /** + * @type {Object} + * @private + */ + this.lookup_ = {}; + + /** + * @type {Array.} + * @private + */ + this.ids_ = []; + +}; + + +/** + * Adds a vertex array to the shared coordinate array. + * @param {ol.geom.VertexArray} vertices Array of vertices. + * @return {string} Index used to reference the added vertex array. + */ +ol.geom.SharedVertices.prototype.add = function(vertices) { + var start = this.coordinates.length; + var offset = this.offset_; + var dimension = this.dimension_; + var count = vertices.length; + var vertex, index; + for (var i = 0; i < count; ++i) { + vertex = vertices[i]; + goog.asserts.assert(vertex.length == dimension); + if (!offset) { + Array.prototype.push.apply(this.coordinates, vertex); + } else { + index = start + (i * dimension); + for (var j = 0; j < dimension; ++j) { + this.coordinates[index + j] = vertex[j] - offset[j]; + } + } + } + var id = this.getId_(); + var idIndex = this.ids_.push(id) - 1; + this.lookup_[id] = { + idIndex: idIndex, + start: start, + count: count + }; + return id; +}; + + +/** + * @param {string} id The vertex array identifier (returned by add). + * @param {number} index The vertex index. + * @param {number} dim The coordinate dimension. + * @return {number} The coordinate value. + */ +ol.geom.SharedVertices.prototype.get = function(id, index, dim) { + goog.asserts.assert(dim <= this.dimension_); + goog.asserts.assert(this.lookup_.hasOwnProperty(id)); + goog.asserts.assert(index < this.lookup_[id].count); + var start = this.lookup_[id].start; + var value = this.coordinates[start + (index * this.dimension_) + dim]; + if (this.offset_) { + value += this.offset_[dim]; + } + return value; +}; + + +/** + * @param {string} id The vertex array identifier (returned by add). + * @return {number} The number of vertices in the referenced array. + */ +ol.geom.SharedVertices.prototype.getCount = function(id) { + goog.asserts.assert(this.lookup_.hasOwnProperty(id)); + return this.lookup_[id].count; +}; + + +/** + * Gets an identifier that is unique for this instance. + * @return {string} Identifier. + * @private + */ +ol.geom.SharedVertices.prototype.getId_ = function() { + return String(++this.counter_); +}; + + +/** + * @return {number} The dimension of each vertex in the array. + */ +ol.geom.SharedVertices.prototype.getDimension = function() { + return this.dimension_; +}; + + +/** + * @return {Array.} The offset array for vertex coordinates (or null). + */ +ol.geom.SharedVertices.prototype.getOffset = function() { + return this.offset_; +}; + + +/** + * @param {string} id The vertex array identifier (returned by add). + * @return {number} The start index in the shared vertices array. + */ +ol.geom.SharedVertices.prototype.getStart = function(id) { + goog.asserts.assert(this.lookup_.hasOwnProperty(id)); + return this.lookup_[id].start; +}; + + +/** + * @param {number} id The vertex array identifier (returned by add). + * @return {ol.geom.VertexArray} The removed vertex array. + */ +ol.geom.SharedVertices.prototype.remove = function(id) { + goog.asserts.assert(this.lookup_.hasOwnProperty(id)); + var info = this.lookup_[id]; + var dimension = this.dimension_; + var length = info.count * dimension; + var removed = this.coordinates.splice(info.start, length); + var offset = this.offset_; + var array = new Array(info.count); + var vertex; + for (var i = 0; i < info.count; ++i) { + vertex = new Array(dimension); + for (var j = 0; j < dimension; ++j) { + vertex[j] = removed[(i * dimension) + j] + (offset ? offset[j] : 0); + } + array[i] = vertex; + } + delete this.lookup_[id]; + this.ids_.splice(info.idIndex, 1); + var afterInfo; + for (var k = info.idIndex, kk = this.ids_.length; k < kk; ++k) { + afterInfo = this.lookup_[this.ids_[k]]; + afterInfo.idIndex -= 1; + afterInfo.start -= length; + } + return array; +}; diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index 7f3d17fd1d..4f0d0b7fb4 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -8,6 +8,9 @@ goog.require('ol.Pixel'); goog.require('ol.canvas'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); goog.require('ol.style.LineLiteral'); goog.require('ol.style.PointLiteral'); goog.require('ol.style.PolygonLiteral'); @@ -108,20 +111,18 @@ ol.renderer.canvas.Renderer.prototype.renderLineStringFeatures_ = function(features, symbolizer) { var context = this.context_, - i, ii, line, coords, dim, j, jj, x, y; + i, ii, line, dim, j, jj, x, y; context.globalAlpha = symbolizer.opacity; context.strokeStyle = symbolizer.strokeStyle; context.lineWidth = symbolizer.strokeWidth * this.inverseScale_; context.beginPath(); - for (i = 0, ii = features.length; i < ii; ++i) { - line = features[i].getGeometry(); + line = /** @type {ol.geom.LineString} */ features[i].getGeometry(); dim = line.dimension; - coords = line.coordinates; - for (j = 0, jj = coords.length; j < jj; j += dim) { - x = coords[j]; - y = coords[j + 1]; + for (j = 0, jj = line.getCount(); j < jj; ++j) { + x = line.get(j, 0); + y = line.get(j, 1); if (j === 0) { context.moveTo(x, y); } else { @@ -143,7 +144,7 @@ ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = function(features, symbolizer) { var context = this.context_, - canvas, i, ii, coords, vec; + canvas, i, ii, point, vec; if (symbolizer instanceof ol.style.ShapeLiteral) { canvas = ol.renderer.canvas.Renderer.renderShape(symbolizer); @@ -156,9 +157,9 @@ ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = context.setTransform(1, 0, 0, 1, -mid, -mid); context.globalAlpha = 1; for (i = 0, ii = features.length; i < ii; ++i) { - coords = features[i].getGeometry().coordinates; + point = /** @type {ol.geom.Point} */ features[i].getGeometry(); vec = goog.vec.Mat4.multVec3( - this.transform_, [coords[0], coords[1], 0], []); + this.transform_, [point.get(0), point.get(1), 0], []); context.drawImage(canvas, vec[0], vec[1]); } context.restore(); @@ -176,7 +177,7 @@ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = var context = this.context_, strokeStyle = symbolizer.strokeStyle, fillStyle = symbolizer.fillStyle, - i, ii, poly, rings, numRings, coords, dim, j, jj, x, y; + i, ii, poly, rings, numRings, ring, dim, j, jj, x, y; context.globalAlpha = symbolizer.opacity; if (strokeStyle) { @@ -196,7 +197,7 @@ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = */ context.beginPath(); for (i = 0, ii = features.length; i < ii; ++i) { - poly = features[i].getGeometry(); + poly = /** @type {ol.geom.Polygon} */ features[i].getGeometry(); dim = poly.dimension; rings = poly.rings; numRings = rings.length; @@ -205,10 +206,10 @@ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = // TODO: use sketch canvas to render outer and punch holes for inner rings throw new Error('Rendering holes not implemented'); } else { - coords = rings[0].coordinates; - for (j = 0, jj = coords.length; j < jj; j += dim) { - x = coords[j]; - y = coords[j + 1]; + ring = rings[0]; + for (j = 0, jj = ring.getCount(); j < jj; ++j) { + x = ring.get(j, 0); + y = ring.get(j, 1); if (j === 0) { context.moveTo(x, y); } else { diff --git a/test/spec/ol/geom/geometrycollection.test.js b/test/spec/ol/geom/geometrycollection.test.js new file mode 100644 index 0000000000..69246431de --- /dev/null +++ b/test/spec/ol/geom/geometrycollection.test.js @@ -0,0 +1,79 @@ +goog.provide('ol.test.geom.GeometryCollection'); + +describe('ol.geom.GeometryCollection', function() { + + var outer = [[0, 0], [10, 0], [10, 10], [0, 10], [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).toBeA(ol.geom.GeometryCollection); + expect(multi).toBeA(ol.geom.Geometry); + }); + + }); + + describe('#components', function() { + + it('is 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.components.length).toBe(3); + expect(multi.components[0]).toBeA(ol.geom.Point); + expect(multi.components[1]).toBeA(ol.geom.LineString); + expect(multi.components[2]).toBeA(ol.geom.Polygon); + }); + + }); + + describe('#dimension', function() { + + it('can be 2', 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.dimension).toBe(2); + }); + + it('can be 3', function() { + var multi = new ol.geom.GeometryCollection([ + new ol.geom.Point([30, 40, 50]) + ]); + expect(multi.dimension).toBe(3); + }); + + }); + + describe('#getBounds()', 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 bounds = multi.getBounds(); + expect(bounds.minX).toBe(1); + expect(bounds.minY).toBe(2); + expect(bounds.maxX).toBe(30); + expect(bounds.maxY).toBe(40); + }); + + }); + +}); + +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'); diff --git a/test/spec/ol/geom/linearring.test.js b/test/spec/ol/geom/linearring.test.js index 117289b3fe..54b0ff0d37 100644 --- a/test/spec/ol/geom/linearring.test.js +++ b/test/spec/ol/geom/linearring.test.js @@ -17,20 +17,6 @@ describe('ol.geom.LinearRing', function() { }); - describe('#coordinates', function() { - - it('is an array', function() { - var ring = new ol.geom.LinearRing([[10, 20], [30, 40]]); - - expect(ring.coordinates.length).toBe(4); - expect(ring.coordinates[0]).toBe(10); - expect(ring.coordinates[1]).toBe(20); - expect(ring.coordinates[2]).toBe(30); - expect(ring.coordinates[3]).toBe(40); - }); - - }); - describe('#dimension', function() { it('can be 2', function() { @@ -45,6 +31,14 @@ describe('ol.geom.LinearRing', function() { }); + describe('#getCoordinates()', function() { + + it('is an array', function() { + var ring = new ol.geom.LinearRing([[10, 20], [30, 40]]); + expect(ring.getCoordinates()).toEqual([[10, 20], [30, 40]]); + }); + + }); }); diff --git a/test/spec/ol/geom/linestring.test.js b/test/spec/ol/geom/linestring.test.js index 654ffcf208..25fe5ca0d1 100644 --- a/test/spec/ol/geom/linestring.test.js +++ b/test/spec/ol/geom/linestring.test.js @@ -16,18 +16,12 @@ describe('ol.geom.LineString', function() { }).toThrow(); }); - }); - - describe('#coordinates', function() { - - it('is an array', function() { - var line = new ol.geom.LineString([[10, 20], [30, 40]]); - - expect(line.coordinates.length).toBe(4); - expect(line.coordinates[0]).toBe(10); - expect(line.coordinates[1]).toBe(20); - expect(line.coordinates[2]).toBe(30); - expect(line.coordinates[3]).toBe(40); + it('accepts shared vertices', function() { + var vertices = new ol.geom.SharedVertices(); + var l1 = new ol.geom.LineString([[10, 20], [30, 40]], vertices); + var l2 = new ol.geom.LineString([[50, 60], [70, 80]], vertices); + expect(l1.getCoordinates()).toEqual([[10, 20], [30, 40]]); + expect(l2.getCoordinates()).toEqual([[50, 60], [70, 80]]); }); }); @@ -59,8 +53,51 @@ describe('ol.geom.LineString', function() { }); + describe('#getCoordinates', function() { + + it('returns an array', function() { + var line = new ol.geom.LineString([[10, 20], [30, 40]]); + expect(line.getCoordinates()).toEqual([[10, 20], [30, 40]]); + }); + + }); + + describe('#getSharedId()', function() { + + it('returns identifiers', function() { + var vertices = new ol.geom.SharedVertices(); + + var l1 = new ol.geom.LineString([[10, 20], [30, 40]], vertices); + var l2 = new ol.geom.LineString( + [[50, 60], [70, 80], [90, 100]], vertices); + + var id1 = l1.getSharedId(); + var id2 = l2.getSharedId(); + + expect(vertices.coordinates).toEqual( + [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]); + + expect(vertices.getStart(id1)).toBe(0); + expect(vertices.getCount(id1)).toBe(2); + expect(vertices.get(id1, 0, 0)).toBe(10); + expect(vertices.get(id1, 0, 1)).toBe(20); + expect(vertices.get(id1, 1, 0)).toBe(30); + expect(vertices.get(id1, 1, 1)).toBe(40); + + expect(vertices.getStart(id2)).toBe(4); + expect(vertices.getCount(id2)).toBe(3); + expect(vertices.get(id2, 0, 0)).toBe(50); + expect(vertices.get(id2, 0, 1)).toBe(60); + expect(vertices.get(id2, 1, 0)).toBe(70); + expect(vertices.get(id2, 1, 1)).toBe(80); + expect(vertices.get(id2, 2, 0)).toBe(90); + expect(vertices.get(id2, 2, 1)).toBe(100); + }); + + }); }); goog.require('ol.geom.Geometry'); goog.require('ol.geom.LineString'); +goog.require('ol.geom.SharedVertices'); diff --git a/test/spec/ol/geom/multilinestring.test.js b/test/spec/ol/geom/multilinestring.test.js index 356a70fc92..8bca72fa5c 100644 --- a/test/spec/ol/geom/multilinestring.test.js +++ b/test/spec/ol/geom/multilinestring.test.js @@ -68,6 +68,19 @@ describe('ol.geom.MultiLineString', function() { }); + describe('#getCoordinates', function() { + + it('returns an array', function() { + var coordinates = [ + [[10, 20], [30, 40]], + [[20, 30], [40, 50]] + ]; + var multi = new ol.geom.MultiLineString(coordinates); + expect(multi.getCoordinates()).toEqual(coordinates); + }); + + }); + }); goog.require('ol.geom.Geometry'); diff --git a/test/spec/ol/geom/multipoint.test.js b/test/spec/ol/geom/multipoint.test.js index 0e1c641ff2..d373e6031d 100644 --- a/test/spec/ol/geom/multipoint.test.js +++ b/test/spec/ol/geom/multipoint.test.js @@ -58,6 +58,15 @@ describe('ol.geom.MultiPoint', function() { }); + describe('#getCoordinates', function() { + + it('returns an array', function() { + var multi = new ol.geom.MultiPoint([[10, 20], [30, 40]]); + expect(multi.getCoordinates()).toEqual([[10, 20], [30, 40]]); + }); + + }); + }); goog.require('ol.geom.Geometry'); diff --git a/test/spec/ol/geom/multipolygon.test.js b/test/spec/ol/geom/multipolygon.test.js index 0dd9dd9a80..cd2ad19793 100644 --- a/test/spec/ol/geom/multipolygon.test.js +++ b/test/spec/ol/geom/multipolygon.test.js @@ -71,6 +71,20 @@ describe('ol.geom.MultiPolygon', function() { }); + describe('#getCoordinates', function() { + + it('returns an array', function() { + var coordinates = [ + [outer1, inner1a, inner1b], + [outer2] + ]; + var multi = new ol.geom.MultiPolygon(coordinates); + expect(multi.getCoordinates()).toEqual(coordinates); + }); + + }); + + }); goog.require('ol.geom.Geometry'); diff --git a/test/spec/ol/geom/point.test.js b/test/spec/ol/geom/point.test.js index b4276cfa45..5f8784135a 100644 --- a/test/spec/ol/geom/point.test.js +++ b/test/spec/ol/geom/point.test.js @@ -10,6 +10,16 @@ describe('ol.geom.Point', function() { expect(point).toBeA(ol.geom.Geometry); }); + it('accepts shared vertices', function() { + var vertices = new ol.geom.SharedVertices(); + var p1 = new ol.geom.Point([10, 20], vertices); + var p2 = new ol.geom.Point([30, 40], vertices); + var p3 = new ol.geom.Point([50, 60], vertices); + expect(p1.getCoordinates()).toEqual([10, 20]); + expect(p2.getCoordinates()).toEqual([30, 40]); + expect(p3.getCoordinates()).toEqual([50, 60]); + }); + it('throws when given with insufficient dimensions', function() { expect(function() { var point = new ol.geom.Point([1]); @@ -18,19 +28,6 @@ describe('ol.geom.Point', function() { }); - describe('#coordinates', function() { - - it('is an array', function() { - var point = new ol.geom.Point([10, 20]); - - expect(point.coordinates.length).toBe(2); - expect(point.coordinates[0]).toBe(10); - expect(point.coordinates[1]).toBe(20); - - }); - - }); - describe('#dimension', function() { it('can be 2', function() { @@ -58,7 +55,52 @@ describe('ol.geom.Point', function() { }); + describe('#getCoordinates()', function() { + + it('returns an array', function() { + var point = new ol.geom.Point([10, 20]); + expect(point.getCoordinates()).toEqual([10, 20]); + }); + + }); + + + describe('#getSharedId()', function() { + + it('returns identifiers', function() { + var vertices = new ol.geom.SharedVertices(); + + var p1 = new ol.geom.Point([10, 20], vertices); + var p2 = new ol.geom.Point([30, 40], vertices); + var p3 = new ol.geom.Point([50, 60], vertices); + + var id1 = p1.getSharedId(); + var id2 = p2.getSharedId(); + var id3 = p3.getSharedId(); + + expect(vertices.coordinates).toEqual( + [10, 20, 30, 40, 50, 60]); + + expect(vertices.getStart(id1)).toBe(0); + expect(vertices.getCount(id1)).toBe(1); + expect(vertices.get(id1, 0, 0)).toBe(10); + expect(vertices.get(id1, 0, 1)).toBe(20); + + expect(vertices.getStart(id2)).toBe(2); + expect(vertices.getCount(id2)).toBe(1); + expect(vertices.get(id2, 0, 0)).toBe(30); + expect(vertices.get(id2, 0, 1)).toBe(40); + + expect(vertices.getStart(id3)).toBe(4); + expect(vertices.getCount(id3)).toBe(1); + expect(vertices.get(id3, 0, 0)).toBe(50); + expect(vertices.get(id3, 0, 1)).toBe(60); + }); + + }); + }); goog.require('ol.geom.Geometry'); goog.require('ol.geom.Point'); +goog.require('ol.geom.SharedVertices'); diff --git a/test/spec/ol/geom/polygon.test.js b/test/spec/ol/geom/polygon.test.js index 591b896643..199c0df9ec 100644 --- a/test/spec/ol/geom/polygon.test.js +++ b/test/spec/ol/geom/polygon.test.js @@ -1,4 +1,4 @@ -gooog.require('ol.test.geom.Polygon'); +goog.provide('ol.test.geom.Polygon'); describe('ol.geom.Polygon', function() { @@ -20,6 +20,16 @@ describe('ol.geom.Polygon', function() { }).toThrow(); }); + it('accepts shared vertices', function() { + var vertices = new ol.geom.SharedVertices(); + var p1 = new ol.geom.Polygon([outer], vertices); + var p2 = new ol.geom.Polygon([outer, inner1], vertices); + var p3 = new ol.geom.Polygon([outer, inner2], vertices); + expect(p1.getCoordinates()).toEqual([outer]); + expect(p2.getCoordinates()).toEqual([outer, inner1]); + expect(p3.getCoordinates()).toEqual([outer, inner2]); + }); + }); describe('#rings', function() { @@ -62,8 +72,17 @@ describe('ol.geom.Polygon', function() { }); + describe('#getCoordinates()', function() { + + it('returns an array', function() { + var poly = new ol.geom.Polygon([outer, inner1, inner2]); + expect(poly.getCoordinates()).toEqual([outer, inner1, inner2]); + }); + + }); }); goog.require('ol.geom.Geometry'); goog.require('ol.geom.Polygon'); +goog.require('ol.geom.SharedVertices'); diff --git a/test/spec/ol/geom/sharedvertices.test.js b/test/spec/ol/geom/sharedvertices.test.js new file mode 100644 index 0000000000..c007c39205 --- /dev/null +++ b/test/spec/ol/geom/sharedvertices.test.js @@ -0,0 +1,195 @@ +goog.provide('ol.test.geom.SharedVertices'); + +describe('ol.geom.SharedVertices', function() { + + describe('constructor', function() { + it('creates an instance', function() { + var vertices = new ol.geom.SharedVertices(); + expect(vertices).toBeA(ol.geom.SharedVertices); + }); + + it('accepts options', function() { + var vertices = new ol.geom.SharedVertices({ + dimension: 4, + offset: [1, 2, 3, 4] + }); + + expect(vertices.getDimension()).toBe(4); + expect(vertices.getOffset()).toEqual([1, 2, 3, 4]); + }); + }); + + describe('offset option', function() { + it('offsets the internally stored vertex coordinates', function() { + var vertices = new ol.geom.SharedVertices({offset: [3, -1]}); + vertices.add([[3, -1], [0, 0]]); + vertices.add([[10, 20]]); + expect(vertices.coordinates).toEqual([0, 0, -3, 1, 7, 21]); + }); + }); + + describe('#add()', function() { + it('adds vertex arrays to the shared coordinates', function() { + var vertices = new ol.geom.SharedVertices(); + expect(vertices.coordinates.length).toBe(0); + + vertices.add([[1, 2], [3, 4]]); + expect(vertices.coordinates).toEqual([1, 2, 3, 4]); + + vertices.add([[5, 6]]); + expect(vertices.coordinates).toEqual([1, 2, 3, 4, 5, 6]); + }); + + it('returns an identifier for coordinate access', function() { + var vertices = new ol.geom.SharedVertices(); + var id = vertices.add([[1, 2], [3, 4]]); + expect(typeof id).toBe('string'); + }); + }); + + describe('#get()', function() { + it('provides access to vertex coordinates', function() { + var vertices = new ol.geom.SharedVertices(); + var first = vertices.add([[1, 2], [3, 4]]); + var second = vertices.add([[5, 6]]); + + expect(vertices.get(first, 0, 0)).toBe(1); + expect(vertices.get(first, 0, 1)).toBe(2); + expect(vertices.get(first, 1, 0)).toBe(3); + expect(vertices.get(first, 1, 1)).toBe(4); + expect(vertices.get(second, 0, 0)).toBe(5); + expect(vertices.get(second, 0, 1)).toBe(6); + }); + + it('works for non-2d vertices', function() { + var vertices = new ol.geom.SharedVertices({dimension: 3}); + var id = vertices.add([[1, 2, 3], [4, 5, 6]]); + + expect(vertices.get(id, 0, 0)).toBe(1); + expect(vertices.get(id, 0, 1)).toBe(2); + expect(vertices.get(id, 0, 2)).toBe(3); + expect(vertices.get(id, 1, 0)).toBe(4); + expect(vertices.get(id, 1, 1)).toBe(5); + expect(vertices.get(id, 1, 2)).toBe(6); + }); + + it('works when an offset is provided', function() { + var vertices = new ol.geom.SharedVertices({offset: [3, 3]}); + var id = vertices.add([[1, 2], [3, 4], [5, 6]]); + + expect(vertices.get(id, 0, 0)).toBe(1); + expect(vertices.get(id, 0, 1)).toBe(2); + expect(vertices.get(id, 1, 0)).toBe(3); + expect(vertices.get(id, 1, 1)).toBe(4); + expect(vertices.get(id, 2, 0)).toBe(5); + expect(vertices.get(id, 2, 1)).toBe(6); + }); + + }); + + describe('#getCount()', function() { + it('returns the length of an identified vertex array', function() { + var vertices = new ol.geom.SharedVertices(); + var first = vertices.add([[2, 3], [3, 4], [4, 5]]); + var second = vertices.add([[5, 6], [6, 6]]); + + expect(vertices.getCount(first)).toBe(3); + expect(vertices.getCount(second)).toBe(2); + }); + }); + + describe('#getDimension()', function() { + it('returns 2 by default', function() { + var vertices = new ol.geom.SharedVertices(); + expect(vertices.getDimension()).toBe(2); + }); + + it('returns the dimension provided to the constructor', function() { + var vertices = new ol.geom.SharedVertices({dimension: 10}); + expect(vertices.getDimension()).toBe(10); + }); + }); + + describe('#getOffset()', function() { + it('returns null by default', function() { + var vertices = new ol.geom.SharedVertices(); + expect(vertices.getOffset()).toBeNull(); + }); + + it('returns the offset provided to the constructor', function() { + var vertices = new ol.geom.SharedVertices({offset: [1, 2]}); + expect(vertices.getOffset()).toEqual([1, 2]); + }); + }); + + describe('#getStart()', function() { + it('returns the start of the identified vertex array', function() { + var vertices = new ol.geom.SharedVertices(); + var first = vertices.add([[1, 2]]); + var second = vertices.add([[3, 4], [5, 6]]); + var third = vertices.add([[7, 8], [9, 10], [11, 12]]); + + expect(vertices.coordinates).toEqual( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + expect(vertices.getStart(first)).toBe(0); + expect(vertices.getStart(second)).toBe(2); + expect(vertices.getStart(third)).toBe(6); + }); + }); + + describe('#remove()', function() { + it('removes a vertex array', function() { + var vertices = new ol.geom.SharedVertices(); + var first = vertices.add([[1, 2], [3, 4]]); + var second = vertices.add([[5, 6]]); + var third = vertices.add([[7, 8], [9, 10], [11, 12]]); + + expect(vertices.remove(second)).toEqual([[5, 6]]); + expect(vertices.coordinates).toEqual([1, 2, 3, 4, 7, 8, 9, 10, 11, 12]); + + expect(vertices.remove(first)).toEqual([[1, 2], [3, 4]]); + expect(vertices.coordinates).toEqual([7, 8, 9, 10, 11, 12]); + + expect(vertices.remove(third)).toEqual([[7, 8], [9, 10], [11, 12]]); + expect(vertices.coordinates).toEqual([]); + }); + + it('adjusts returned vertices by offset', function() { + + var vertices = new ol.geom.SharedVertices({offset: [10, 20]}); + var first = vertices.add([[1, 2]]); + var second = vertices.add([[3, 4]]); + var third = vertices.add([[5, 6]]); + + expect(vertices.remove(second)).toEqual([[3, 4]]); + expect(vertices.coordinates).toEqual([-9, -18, -5, -14]); + + expect(vertices.remove(third)).toEqual([[5, 6]]); + expect(vertices.coordinates).toEqual([-9, -18]); + + expect(vertices.remove(first)).toEqual([[1, 2]]); + expect(vertices.coordinates).toEqual([]); + }); + + }); + + describe('#coordinates', function() { + it('is not reassigned', function() { + var vertices = new ol.geom.SharedVertices(); + var first = vertices.add([[1, 2], [3, 4]]); + var coordinates = vertices.coordinates; + + var second = vertices.add([[5, 6]]); + expect(vertices.coordinates).toBe(coordinates); + + vertices.remove(first); + expect(vertices.coordinates).toBe(coordinates); + + vertices.remove(second); + expect(vertices.coordinates).toBe(coordinates); + }); + }); + +}); + +goog.require('ol.geom.SharedVertices'); diff --git a/test/spec/ol/parser/geojson.test.js b/test/spec/ol/parser/geojson.test.js index dfbd3ae70d..624b95b07b 100644 --- a/test/spec/ol/parser/geojson.test.js +++ b/test/spec/ol/parser/geojson.test.js @@ -76,8 +76,7 @@ describe('ol.parser.geojson', function() { var obj = ol.parser.geojson.read(str); expect(obj).toBeA(ol.geom.Point); - expect(obj.coordinates[0]).toBe(10); - expect(obj.coordinates[1]).toBe(20); + expect(obj.getCoordinates()).toEqual([10, 20]); }); it('parses linestring', function() { @@ -88,10 +87,7 @@ describe('ol.parser.geojson', function() { var obj = ol.parser.geojson.read(str); expect(obj).toBeA(ol.geom.LineString); - expect(obj.coordinates[0]).toBe(10); - expect(obj.coordinates[1]).toBe(20); - expect(obj.coordinates[2]).toBe(30); - expect(obj.coordinates[3]).toBe(40); + expect(obj.getCoordinates()).toEqual([[10, 20], [30, 40]]); }); it('parses polygon', function() {