From 3c305db4a44bca70d19ed465fc2895353a79278c Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 4 Jan 2013 18:47:33 +0100 Subject: [PATCH 001/180] Canvas tile renderer - first cut Untested and no styling --- src/ol/renderer/canvas/canvastilerenderer.js | 174 +++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 src/ol/renderer/canvas/canvastilerenderer.js diff --git a/src/ol/renderer/canvas/canvastilerenderer.js b/src/ol/renderer/canvas/canvastilerenderer.js new file mode 100644 index 0000000000..7055d122be --- /dev/null +++ b/src/ol/renderer/canvas/canvastilerenderer.js @@ -0,0 +1,174 @@ +goog.provide('ol.renderer.tile.Canvas'); + +goog.require('goog.style'); +goog.require('ol.Coordinate'); +goog.require('ol.Pixel'); +goog.require('ol.Projection'); + + + +ol.renderer.tile.Canvas = function(layerRenderer) { + + this.layerRenderer_ = layerRenderer; + + /** + * @type {ol.Projection} + * @private + */ + this.projection_; + + /** + * @type {number} + * @private + */ + this.resolution_; + + /** + * @type {ol.Coordinate} + * @private + */ + this.origin_ = new ol.Coordinate(0, 0); + + /** + * @type {ol.Pixel} + * @private + */ + this.originPixel_; + + /** + * @type {Object|ol.Size} + * @private + */ + this.size_; + + /** + * @type {CanvasContext} + * @private + */ + this.context_; + + /** + * @type {CanvasImageData} + * @private + */ + this.pixelData_; + +}; + +/** + * @param size {Object|ol.Size} + */ +ol.renderer.tile.Canvas.prototype.setSize = function(size) { + if (!(size instanceof ol.Size)) { + size = new ol.Size(size.width, size.height); + } + this.size_ = size; + if (!this.context_) { + this.context_ = document.createElement('canvas').getContext('2d'); + this.pixelData_ = this.context_.createImageData(1,1).data; + this.pixelData[3] = 1; // opacity + } + goog.style.setSize(this.canvas_, size); +}; + +/** + * @param size {Object|ol.Coordinate} + */ +ol.renderer.tile.Canvas.prototype.setOrigin = function(origin) { + if (!(origin instanceof ol.Coordinate)) { + origin = new ol.Coordinate(origin.x, origin.y); + } + this.origin_ = origin; + this.originPixel_ = this.layerRenderer_.getPixelFromCoordinate(origin); +}; + +/** + * @param resolution {number} + */ +ol.renderer.tile.Canvas.prototype.setResolution = function(resolution) { + this.resolution_ = resolution; +}; + +/* + * @param geoJson {Object|Array} GeoJSON or an array of GeoJSON features + * @param projection {ol.Projection=} + */ +ol.renderer.tile.Canvas.prototype.render = function(geoJson, projection) { + var map = this.map; + this.projection_ = map.getProjection(); + this.resolution_ = map.getResolution(); + + var features; + if (goog.isArray(geoJson)) { + features = geoJson; + } else if (geoJson.type === 'FeatureCollection') { + features = geoJson.features; + } else if (geoJson.type === 'Feature') { + features = [geoJson]; + } + + if (features) { + //FIXME Support geometry level projections + projection = projection || new ol.Projection('EPSG:4326'); + if (!(projection instanceof ol.Projection)) { + projection = new ol.Projection(projection); + } + for (var i=0, ii=features.length; i Date: Sun, 6 Jan 2013 10:07:29 -0700 Subject: [PATCH 002/180] Satisfying gjslint --- src/ol/renderer/canvas/canvastilerenderer.js | 80 ++++++++++++++------ 1 file changed, 55 insertions(+), 25 deletions(-) diff --git a/src/ol/renderer/canvas/canvastilerenderer.js b/src/ol/renderer/canvas/canvastilerenderer.js index 7055d122be..f6dd4dbaaf 100644 --- a/src/ol/renderer/canvas/canvastilerenderer.js +++ b/src/ol/renderer/canvas/canvastilerenderer.js @@ -7,8 +7,16 @@ goog.require('ol.Projection'); +/** + * @constructor + * @param {ol.renderer.Layer} layerRenderer Layer renderer. + */ ol.renderer.tile.Canvas = function(layerRenderer) { - + + /** + * @type {ol.renderer.Layer} + * @private + */ this.layerRenderer_ = layerRenderer; /** @@ -16,7 +24,7 @@ ol.renderer.tile.Canvas = function(layerRenderer) { * @private */ this.projection_; - + /** * @type {number} * @private @@ -28,7 +36,7 @@ ol.renderer.tile.Canvas = function(layerRenderer) { * @private */ this.origin_ = new ol.Coordinate(0, 0); - + /** * @type {ol.Pixel} * @private @@ -40,13 +48,13 @@ ol.renderer.tile.Canvas = function(layerRenderer) { * @private */ this.size_; - + /** * @type {CanvasContext} * @private */ this.context_; - + /** * @type {CanvasImageData} * @private @@ -55,8 +63,9 @@ ol.renderer.tile.Canvas = function(layerRenderer) { }; + /** - * @param size {Object|ol.Size} + * @param {Object|ol.Size} size Tile size. */ ol.renderer.tile.Canvas.prototype.setSize = function(size) { if (!(size instanceof ol.Size)) { @@ -65,14 +74,15 @@ ol.renderer.tile.Canvas.prototype.setSize = function(size) { this.size_ = size; if (!this.context_) { this.context_ = document.createElement('canvas').getContext('2d'); - this.pixelData_ = this.context_.createImageData(1,1).data; + this.pixelData_ = this.context_.createImageData(1, 1).data; this.pixelData[3] = 1; // opacity } goog.style.setSize(this.canvas_, size); }; + /** - * @param size {Object|ol.Coordinate} + * @param {Object|ol.Coordinate} origin Tile origin. */ ol.renderer.tile.Canvas.prototype.setOrigin = function(origin) { if (!(origin instanceof ol.Coordinate)) { @@ -82,22 +92,24 @@ ol.renderer.tile.Canvas.prototype.setOrigin = function(origin) { this.originPixel_ = this.layerRenderer_.getPixelFromCoordinate(origin); }; + /** - * @param resolution {number} + * @param {number} resolution Tile resolution. */ ol.renderer.tile.Canvas.prototype.setResolution = function(resolution) { this.resolution_ = resolution; }; -/* - * @param geoJson {Object|Array} GeoJSON or an array of GeoJSON features - * @param projection {ol.Projection=} + +/** + * @param {Object|Array} geoJson GeoJSON or an array of GeoJSON features. + * @param {ol.Projection=} opt_projection Projection. */ -ol.renderer.tile.Canvas.prototype.render = function(geoJson, projection) { +ol.renderer.tile.Canvas.prototype.render = function(geoJson, opt_projection) { var map = this.map; this.projection_ = map.getProjection(); this.resolution_ = map.getResolution(); - + var features; if (goog.isArray(geoJson)) { features = geoJson; @@ -106,49 +118,67 @@ ol.renderer.tile.Canvas.prototype.render = function(geoJson, projection) { } else if (geoJson.type === 'Feature') { features = [geoJson]; } - + if (features) { //FIXME Support geometry level projections - projection = projection || new ol.Projection('EPSG:4326'); - if (!(projection instanceof ol.Projection)) { - projection = new ol.Projection(projection); + // @type {ol.Projection} + var projection; + if (goog.isDef(opt_projection)) { + projection = opt_projection; + } else { + projection = new ol.Projection('EPSG:4326'); } - for (var i=0, ii=features.length; i Date: Sun, 6 Jan 2013 10:59:21 -0700 Subject: [PATCH 003/180] Satisfying compiler --- src/ol/renderer/canvas/canvastilerenderer.js | 54 ++++++++++++++------ 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/src/ol/renderer/canvas/canvastilerenderer.js b/src/ol/renderer/canvas/canvastilerenderer.js index f6dd4dbaaf..f5244bd5d9 100644 --- a/src/ol/renderer/canvas/canvastilerenderer.js +++ b/src/ol/renderer/canvas/canvastilerenderer.js @@ -6,6 +6,20 @@ goog.require('ol.Pixel'); goog.require('ol.Projection'); +/** + * TODO(tschaub): get rid of this + * @typedef {{coordinates: (Array)}} + */ +ol.GeoJSONGeometry; + + +/** + * TODO(tschaub): get rid of this + * @typedef {{geometry: (ol.GeoJSONGeometry)}} + */ +ol.GeoJSONFeature; + + /** * @constructor @@ -50,13 +64,19 @@ ol.renderer.tile.Canvas = function(layerRenderer) { this.size_; /** - * @type {CanvasContext} + * @type {Element} + * @private + */ + this.canvas_; + + /** + * @type {CanvasRenderingContext2D} * @private */ this.context_; /** - * @type {CanvasImageData} + * @type {ImageData} * @private */ this.pixelData_; @@ -73,9 +93,10 @@ ol.renderer.tile.Canvas.prototype.setSize = function(size) { } this.size_ = size; if (!this.context_) { - this.context_ = document.createElement('canvas').getContext('2d'); + this.canvas_ = goog.dom.createElement(goog.dom.TagName.CANVAS); + this.context_ = this.canvas_.getContext('2d'); this.pixelData_ = this.context_.createImageData(1, 1).data; - this.pixelData[3] = 1; // opacity + this.pixelData_[3] = 1; // opacity } goog.style.setSize(this.canvas_, size); }; @@ -89,7 +110,8 @@ ol.renderer.tile.Canvas.prototype.setOrigin = function(origin) { origin = new ol.Coordinate(origin.x, origin.y); } this.origin_ = origin; - this.originPixel_ = this.layerRenderer_.getPixelFromCoordinate(origin); + var mapRenderer = this.layerRenderer_.getMapRenderer(); + this.originPixel_ = mapRenderer.getPixelFromCoordinate(origin); }; @@ -106,9 +128,10 @@ ol.renderer.tile.Canvas.prototype.setResolution = function(resolution) { * @param {ol.Projection=} opt_projection Projection. */ ol.renderer.tile.Canvas.prototype.render = function(geoJson, opt_projection) { - var map = this.map; - this.projection_ = map.getProjection(); - this.resolution_ = map.getResolution(); + var map = this.layerRenderer_.getMap(); + // TODO(tschaub): make it so this isn't required everywhere + this.projection_ = /** @type {ol.Projection} */ map.getProjection(); + this.resolution_ = /** @type {number} */ map.getResolution(); var features; if (goog.isArray(geoJson)) { @@ -121,12 +144,12 @@ ol.renderer.tile.Canvas.prototype.render = function(geoJson, opt_projection) { if (features) { //FIXME Support geometry level projections - // @type {ol.Projection} + /** @type {ol.Projection} */ var projection; if (goog.isDef(opt_projection)) { projection = opt_projection; } else { - projection = new ol.Projection('EPSG:4326'); + projection = ol.Projection.getFromCode('EPSG:4326'); } for (var i = 0, ii = features.length; i < ii; ++i) { this.renderGeometry(features[i].geometry, projection); @@ -141,7 +164,7 @@ ol.renderer.tile.Canvas.prototype.render = function(geoJson, opt_projection) { */ ol.renderer.tile.Canvas.prototype.renderGeometry = function(geometry, projection) { - var i, coordinates, hole = false; + var i, ii, coordinates, hole = false; if (geometry.type === 'Point') { this.renderPosList([geometry.coordinates], projection, false, false, hole); @@ -172,17 +195,18 @@ ol.renderer.tile.Canvas.prototype.renderGeometry = * @param {boolean} fill Fill. * @param {boolean} hole Treat as hole. */ -ol.renderer.tileCanvas.prototype.renderPosList = +ol.renderer.tile.Canvas.prototype.renderPosList = function(posList, projection, connect, fill, hole) { - var position, coordinate, pixel, localX, localY; + var i, ii, position, coordinate, pixel, localX, localY; if (connect === true) { this.context_.beginPath(); } for (i = 0, ii = posList.length; i < ii; ++i) { position = posList[i]; coordinate = ol.Projection.transform(new ol.Coordinate( - position[0], position[1]), projection, this.projection); - pixel = this.layerRenderer_.getPixelFromCoordinate(coordinate); + position[0], position[1]), projection, this.projection_); + var mapRenderer = this.layerRenderer_.getMapRenderer(); + pixel = mapRenderer.getPixelFromCoordinate(coordinate); localX = pixel.x - this.originPixel_.x; localY = pixel.y - this.originPixel_.y + this.size_.height; if (connect === false && fill === false) { From 997c7145ab165bb90ec7924e6f28184345524ba3 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 7 Jan 2013 01:34:33 -0500 Subject: [PATCH 004/180] Bare bones geom package --- src/ol/geom/geometry.js | 7 +++++++ src/ol/geom/linestring.js | 19 +++++++++++++++++++ src/ol/geom/multilinestring.js | 19 +++++++++++++++++++ src/ol/geom/multipoint.js | 19 +++++++++++++++++++ src/ol/geom/multipolygon.js | 19 +++++++++++++++++++ src/ol/geom/point.js | 19 +++++++++++++++++++ src/ol/geom/polygon.js | 19 +++++++++++++++++++ 7 files changed, 121 insertions(+) create mode 100644 src/ol/geom/geometry.js create mode 100644 src/ol/geom/linestring.js create mode 100644 src/ol/geom/multilinestring.js create mode 100644 src/ol/geom/multipoint.js create mode 100644 src/ol/geom/multipolygon.js create mode 100644 src/ol/geom/point.js create mode 100644 src/ol/geom/polygon.js diff --git a/src/ol/geom/geometry.js b/src/ol/geom/geometry.js new file mode 100644 index 0000000000..57f8ec0545 --- /dev/null +++ b/src/ol/geom/geometry.js @@ -0,0 +1,7 @@ +goog.provide('ol.geom.Geometry'); + + +/** + * @interface + */ +ol.geom.Geometry = function() {}; diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js new file mode 100644 index 0000000000..241aff7dfd --- /dev/null +++ b/src/ol/geom/linestring.js @@ -0,0 +1,19 @@ +goog.provide('ol.geom.LineString'); + +goog.require('ol.geom.Geometry'); + + + +/** + * @constructor + * @implements {ol.geom.Geometry} + * @param {Array} coordinates Coordinates array. + */ +ol.geom.LineString = function(coordinates) { + + /** + * @type {Array} + */ + this.coordinates = coordinates; + +}; diff --git a/src/ol/geom/multilinestring.js b/src/ol/geom/multilinestring.js new file mode 100644 index 0000000000..0665f9d630 --- /dev/null +++ b/src/ol/geom/multilinestring.js @@ -0,0 +1,19 @@ +goog.provide('ol.geom.MultiLineString'); + +goog.require('ol.geom.Geometry'); + + + +/** + * @constructor + * @implements {ol.geom.Geometry} + * @param {Array} coordinates Coordinates array. + */ +ol.geom.MultiLineString = function(coordinates) { + + /** + * @type {Array} + */ + this.coordinates = coordinates; + +}; diff --git a/src/ol/geom/multipoint.js b/src/ol/geom/multipoint.js new file mode 100644 index 0000000000..586730f6b6 --- /dev/null +++ b/src/ol/geom/multipoint.js @@ -0,0 +1,19 @@ +goog.provide('ol.geom.MultiPoint'); + +goog.require('ol.geom.Geometry'); + + + +/** + * @constructor + * @implements {ol.geom.Geometry} + * @param {Array} coordinates Coordinates array. + */ +ol.geom.MultiPoint = function(coordinates) { + + /** + * @type {Array} + */ + this.coordinates = coordinates; + +}; diff --git a/src/ol/geom/multipolygon.js b/src/ol/geom/multipolygon.js new file mode 100644 index 0000000000..4f22f0d003 --- /dev/null +++ b/src/ol/geom/multipolygon.js @@ -0,0 +1,19 @@ +goog.provide('ol.geom.MultiPolygon'); + +goog.require('ol.geom.Geometry'); + + + +/** + * @constructor + * @implements {ol.geom.Geometry} + * @param {Array} coordinates Coordinates array. + */ +ol.geom.MultiPolygon = function(coordinates) { + + /** + * @type {Array} + */ + this.coordinates = coordinates; + +}; diff --git a/src/ol/geom/point.js b/src/ol/geom/point.js new file mode 100644 index 0000000000..2f4e2b6d64 --- /dev/null +++ b/src/ol/geom/point.js @@ -0,0 +1,19 @@ +goog.provide('ol.geom.Point'); + +goog.require('ol.geom.Geometry'); + + + +/** + * @constructor + * @implements {ol.geom.Geometry} + * @param {Array} coordinates Coordinates array. + */ +ol.geom.Point = function(coordinates) { + + /** + * @type {Array} + */ + this.coordinates = coordinates; + +}; diff --git a/src/ol/geom/polygon.js b/src/ol/geom/polygon.js new file mode 100644 index 0000000000..bd0f698c7d --- /dev/null +++ b/src/ol/geom/polygon.js @@ -0,0 +1,19 @@ +goog.provide('ol.geom.Polygon'); + +goog.require('ol.geom.Geometry'); + + + +/** + * @constructor + * @implements {ol.geom.Geometry} + * @param {Array} coordinates Coordinates array. + */ +ol.geom.Polygon = function(coordinates) { + + /** + * @type {Array} + */ + this.coordinates = coordinates; + +}; From 7cca691b9f4cfb4910c96f664316629da9b2f625 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 7 Jan 2013 01:41:02 -0500 Subject: [PATCH 005/180] Start of style package These literals will be used internally only - created when evaluating non-literal symbolizers (with properties that are expressions). --- src/ol/style/fill.js | 27 +++++++++++++++++++++++ src/ol/style/shape.js | 45 ++++++++++++++++++++++++++++++++++++++ src/ol/style/stroke.js | 32 +++++++++++++++++++++++++++ src/ol/style/symbolizer.js | 8 +++++++ 4 files changed, 112 insertions(+) create mode 100644 src/ol/style/fill.js create mode 100644 src/ol/style/shape.js create mode 100644 src/ol/style/stroke.js create mode 100644 src/ol/style/symbolizer.js diff --git a/src/ol/style/fill.js b/src/ol/style/fill.js new file mode 100644 index 0000000000..a583b8beff --- /dev/null +++ b/src/ol/style/fill.js @@ -0,0 +1,27 @@ +goog.provide('ol.style.LiteralFill'); + +goog.require('ol.style.LiteralSymbolizer'); + + +/** + * @typedef {{color: (string), + * opacity: (number)}} + */ +ol.style.LiteralFillConfig; + + + +/** + * @constructor + * @implements {ol.style.LiteralSymbolizer} + * @param {ol.style.LiteralFillConfig} config Symbolizer properties. + */ +ol.style.LiteralFill = function(config) { + + /** @type {string} */ + this.color = config.color; + + /** @type {number} */ + this.opacity = config.opacity; + +}; diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js new file mode 100644 index 0000000000..09ce5a4a8c --- /dev/null +++ b/src/ol/style/shape.js @@ -0,0 +1,45 @@ +goog.provide('ol.style.LiteralShape'); + +goog.require('ol.style.LiteralFill'); +goog.require('ol.style.LiteralStroke'); +goog.require('ol.style.LiteralSymbolizer'); + + +/** + * @enum {string} + */ +ol.style.ShapeType = { + CIRCLE: 'circle' +}; + + +/** + * @typedef {{type: (ol.style.ShapeType), + * size: (number), + * fill: (ol.style.LiteralFill), + * stroke: (ol.style.LiteralStroke)}} + */ +ol.style.LiteralShapeConfig; + + + +/** + * @constructor + * @implements {ol.style.LiteralSymbolizer} + * @param {ol.style.LiteralShapeConfig} config Symbolizer properties. + */ +ol.style.LiteralShape = function(config) { + + /** @type {string} */ + this.type = config.type; + + /** @type {number} */ + this.size = config.size; + + /** @type {ol.style.LiteralFill} */ + this.fill = config.fill; + + /** @type {ol.style.LiteralStroke} */ + this.stroke = config.stroke; + +}; diff --git a/src/ol/style/stroke.js b/src/ol/style/stroke.js new file mode 100644 index 0000000000..c8ac1a76a2 --- /dev/null +++ b/src/ol/style/stroke.js @@ -0,0 +1,32 @@ +goog.provide('ol.style.LiteralStroke'); + +goog.require('ol.style.LiteralSymbolizer'); + + +/** + * @typedef {{width: (number), + * color: (string), + * opacity: (number)}} + */ +ol.style.LiteralStrokeConfig; + + + +/** + * @constructor + * @implements {ol.style.LiteralSymbolizer} + * @param {ol.style.LiteralStrokeConfig} config Symbolizer properties. + */ +ol.style.LiteralStroke = function(config) { + + /** @type {string} */ + this.color = config.color; + + /** @type {number} */ + this.opacity = config.opacity; + + /** @type {number} */ + this.width = config.width; + +}; + diff --git a/src/ol/style/symbolizer.js b/src/ol/style/symbolizer.js new file mode 100644 index 0000000000..1959afa171 --- /dev/null +++ b/src/ol/style/symbolizer.js @@ -0,0 +1,8 @@ +goog.provide('ol.style.LiteralSymbolizer'); + + + +/** + * @interface + */ +ol.style.LiteralSymbolizer = function() {}; From 7971ec1b3b5112d4b8ed1f9f7d82cb9cb228f0b3 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 7 Jan 2013 01:42:31 -0500 Subject: [PATCH 006/180] Render geometries to a canvas --- src/ol/renderer/canvas/canvasrenderer.js | 170 +++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 src/ol/renderer/canvas/canvasrenderer.js diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js new file mode 100644 index 0000000000..bae416f345 --- /dev/null +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -0,0 +1,170 @@ +goog.provide('ol.renderer.canvas.CanvasRenderer'); + +goog.require('goog.asserts'); +goog.require('ol.Coordinate'); +goog.require('ol.Pixel'); +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.Point'); +goog.require('ol.renderer.Layer'); +goog.require('ol.style.LiteralFill'); +goog.require('ol.style.LiteralShape'); +goog.require('ol.style.LiteralStroke'); +goog.require('ol.style.LiteralSymbolizer'); + + + +/** + * @constructor + */ +ol.renderer.canvas.CanvasRenderer = function() { + + /** + * @type {CanvasRenderingContext2D} + * @private + */ + this.context_ = null; + + /** + * @type {ol.renderer.Layer} + * @private + */ + this.layerRenderer_ = null; + + /** + * @type {ol.Pixel} + * @private + */ + this.offset_ = null; + +}; + + +/** + * Confirm that we're ready to render. + * @private + */ +ol.renderer.canvas.CanvasRenderer.prototype.assertReady_ = function() { + goog.asserts.assert(!goog.isNull(this.context_), + 'Call setTarget before rendering'); + goog.asserts.assert(!goog.isNull(this.layerRenderer_), + 'Call setLayerRenderer before rendering'); + goog.asserts.assert(!goog.isNull(this.offset_), + 'Call setOffset before rendering'); +}; + + +/** + * @param {ol.renderer.Layer} layerRenderer Layer renderer. + */ +ol.renderer.canvas.CanvasRenderer.prototype.setLayerRenderer = + function(layerRenderer) { + this.layerRenderer_ = layerRenderer; +}; + + +/** + * @param {ol.Pixel} offset Pixel offset of top left corner of canvas. + */ +ol.renderer.canvas.CanvasRenderer.prototype.setOffset = function(offset) { + this.offset_ = offset; +}; + + +/** + * @param {Element} canvas Target canvas element. + */ +ol.renderer.canvas.CanvasRenderer.prototype.setTarget = function(canvas) { + this.context_ = canvas.getContext('2d'); +}; + + +/** + * Render a geometry. + * @param {ol.geom.Geometry} geometry The geometry to render. + * @param {Array.} symbolizers Symbolizers to render with. + * @private + */ +ol.renderer.canvas.CanvasRenderer.prototype.renderGeometry_ = + function(geometry, symbolizers) { + this.assertReady_(); + if (geometry instanceof ol.geom.Point) { + this.renderPoint_(geometry, symbolizers); + } else { + // TODO: render other geometry types + goog.asserts.assert(false, 'Not yet implemented'); + } +}; + + +/** + * Render a point geometry. + * @param {ol.geom.Point} point Point geometry. + * @param {Array.} symbolizers Symbolizers. + * @private + */ +ol.renderer.canvas.CanvasRenderer.prototype.renderPoint_ = + function(point, symbolizers) { + for (var i = 0, ii = symbolizers.length; i < ii; ++i) { + var symbolizer = symbolizers[i]; + if (symbolizer instanceof ol.style.LiteralShape) { + if (symbolizer.type === ol.style.ShapeType.CIRCLE) { + this.renderPointCircle_(point, symbolizer); + } else { + this.renderPointShape_(point, symbolizer); + } + } + } +}; + + +/** + * Render a point as a circle. + * @param {ol.geom.Point} point Point geometry. + * @param {ol.style.LiteralShape} symbolizer Shape symbolizer. + * @private + */ +ol.renderer.canvas.CanvasRenderer.prototype.renderPointCircle_ = + function(point, symbolizer) { + var coords = point.coordinates, + coordinate = new ol.Coordinate(coords[0], coords[1]), + mapRenderer = this.layerRenderer_.getMapRenderer(), + pixel = mapRenderer.getPixelFromCoordinate(coordinate), + px = pixel.x + this.offset_.x, + py = pixel.y + this.offset_.y, + twoPi = Math.PI * 2, + radius = symbolizer.size / 2, + /** @type {ol.style.LiteralFill} */ + fill = symbolizer.fill, + /** @type {ol.style.LiteralStroke} */ + stroke = symbolizer.stroke, + context = this.context_; + + if (!goog.isNull(fill)) { + context.globalAlpha = fill.opacity; + context.fillStyle = fill.color; + context.beginPath(); + context.arc(px, py, radius, 0, twoPi, true); + context.fill(); + } + if (!goog.isNull(stroke)) { + context.globalAlpha = stroke.opacity; + context.strokeStyle = stroke.color; + context.lineWidth = stroke.width; + context.beginPath(); + context.arc(px, py, radius, 0, twoPi, true); + context.stroke(); + } +}; + + +/** + * Render a point as a shape (other than circle). + * @param {ol.geom.Point} point Point geometry. + * @param {ol.style.LiteralShape} symbolizer Shape symbolizer. + * @private + */ +ol.renderer.canvas.CanvasRenderer.prototype.renderPointShape_ = + function(point, symbolizer) { + // TODO: other shape types + goog.asserts.assert(false, 'Not implemented'); +}; From 3ac437393ae2a20342525e5a70e82637ebcbd1b6 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 18 Jan 2013 15:54:48 -0700 Subject: [PATCH 007/180] To be reworked as ol.renderer.canvas.VectorLayer --- src/ol/renderer/canvas/canvastilerenderer.js | 228 ------------------- 1 file changed, 228 deletions(-) delete mode 100644 src/ol/renderer/canvas/canvastilerenderer.js diff --git a/src/ol/renderer/canvas/canvastilerenderer.js b/src/ol/renderer/canvas/canvastilerenderer.js deleted file mode 100644 index f5244bd5d9..0000000000 --- a/src/ol/renderer/canvas/canvastilerenderer.js +++ /dev/null @@ -1,228 +0,0 @@ -goog.provide('ol.renderer.tile.Canvas'); - -goog.require('goog.style'); -goog.require('ol.Coordinate'); -goog.require('ol.Pixel'); -goog.require('ol.Projection'); - - -/** - * TODO(tschaub): get rid of this - * @typedef {{coordinates: (Array)}} - */ -ol.GeoJSONGeometry; - - -/** - * TODO(tschaub): get rid of this - * @typedef {{geometry: (ol.GeoJSONGeometry)}} - */ -ol.GeoJSONFeature; - - - -/** - * @constructor - * @param {ol.renderer.Layer} layerRenderer Layer renderer. - */ -ol.renderer.tile.Canvas = function(layerRenderer) { - - /** - * @type {ol.renderer.Layer} - * @private - */ - this.layerRenderer_ = layerRenderer; - - /** - * @type {ol.Projection} - * @private - */ - this.projection_; - - /** - * @type {number} - * @private - */ - this.resolution_; - - /** - * @type {ol.Coordinate} - * @private - */ - this.origin_ = new ol.Coordinate(0, 0); - - /** - * @type {ol.Pixel} - * @private - */ - this.originPixel_; - - /** - * @type {Object|ol.Size} - * @private - */ - this.size_; - - /** - * @type {Element} - * @private - */ - this.canvas_; - - /** - * @type {CanvasRenderingContext2D} - * @private - */ - this.context_; - - /** - * @type {ImageData} - * @private - */ - this.pixelData_; - -}; - - -/** - * @param {Object|ol.Size} size Tile size. - */ -ol.renderer.tile.Canvas.prototype.setSize = function(size) { - if (!(size instanceof ol.Size)) { - size = new ol.Size(size.width, size.height); - } - this.size_ = size; - if (!this.context_) { - this.canvas_ = goog.dom.createElement(goog.dom.TagName.CANVAS); - this.context_ = this.canvas_.getContext('2d'); - this.pixelData_ = this.context_.createImageData(1, 1).data; - this.pixelData_[3] = 1; // opacity - } - goog.style.setSize(this.canvas_, size); -}; - - -/** - * @param {Object|ol.Coordinate} origin Tile origin. - */ -ol.renderer.tile.Canvas.prototype.setOrigin = function(origin) { - if (!(origin instanceof ol.Coordinate)) { - origin = new ol.Coordinate(origin.x, origin.y); - } - this.origin_ = origin; - var mapRenderer = this.layerRenderer_.getMapRenderer(); - this.originPixel_ = mapRenderer.getPixelFromCoordinate(origin); -}; - - -/** - * @param {number} resolution Tile resolution. - */ -ol.renderer.tile.Canvas.prototype.setResolution = function(resolution) { - this.resolution_ = resolution; -}; - - -/** - * @param {Object|Array} geoJson GeoJSON or an array of GeoJSON features. - * @param {ol.Projection=} opt_projection Projection. - */ -ol.renderer.tile.Canvas.prototype.render = function(geoJson, opt_projection) { - var map = this.layerRenderer_.getMap(); - // TODO(tschaub): make it so this isn't required everywhere - this.projection_ = /** @type {ol.Projection} */ map.getProjection(); - this.resolution_ = /** @type {number} */ map.getResolution(); - - var features; - if (goog.isArray(geoJson)) { - features = geoJson; - } else if (geoJson.type === 'FeatureCollection') { - features = geoJson.features; - } else if (geoJson.type === 'Feature') { - features = [geoJson]; - } - - if (features) { - //FIXME Support geometry level projections - /** @type {ol.Projection} */ - var projection; - if (goog.isDef(opt_projection)) { - projection = opt_projection; - } else { - projection = ol.Projection.getFromCode('EPSG:4326'); - } - for (var i = 0, ii = features.length; i < ii; ++i) { - this.renderGeometry(features[i].geometry, projection); - } - } -}; - - -/** - * @param {Object} geometry GeoJSON geometry. - * @param {ol.Projection} projection Projection. - */ -ol.renderer.tile.Canvas.prototype.renderGeometry = - function(geometry, projection) { - var i, ii, coordinates, hole = false; - if (geometry.type === 'Point') { - this.renderPosList([geometry.coordinates], - projection, false, false, hole); - } else if (geometry.type === 'MultiPoint') { - this.renderPosList(geometry.coordinates, - projection, false, false, hole); - } else if (geometry.type === 'LineString') { - this.renderPosList(geometry.coordinates, projection, true, false, hole); - } else if (geometry.type === 'MultiLineString') { - coordinates = geometry.coordinates; - for (i = 0, ii = coordinates.length; i < ii; ++i) { - this.renderPosList(coordinates[i], projection, true, false, hole); - } - } else if (geometry.type === 'Polygon') { - coordinates = geometry.coordinates; - for (i = 0, ii = coordinates.length; i < ii; ++i) { - this.renderPosList(coordinates[i], projection, true, true, hole); - hole = true; - } - } -}; - - -/** - * @param {Array} posList Coordinates. - * @param {ol.Projection} projection Projection. - * @param {boolean} connect Connect endpoints. - * @param {boolean} fill Fill. - * @param {boolean} hole Treat as hole. - */ -ol.renderer.tile.Canvas.prototype.renderPosList = - function(posList, projection, connect, fill, hole) { - var i, ii, position, coordinate, pixel, localX, localY; - if (connect === true) { - this.context_.beginPath(); - } - for (i = 0, ii = posList.length; i < ii; ++i) { - position = posList[i]; - coordinate = ol.Projection.transform(new ol.Coordinate( - position[0], position[1]), projection, this.projection_); - var mapRenderer = this.layerRenderer_.getMapRenderer(); - pixel = mapRenderer.getPixelFromCoordinate(coordinate); - localX = pixel.x - this.originPixel_.x; - localY = pixel.y - this.originPixel_.y + this.size_.height; - if (connect === false && fill === false) { - //FIXME Draw more than just a black pixel for points - this.context_.putImageData(this.pixelData_, localX, localY); - } else { - this.context_[i === 0 ? 'moveTo' : 'pathTo'](localX, localY); - } - } - if (connect === true) { - this.context_.closePath(); - this.context_.stroke(); - } - if (fill === true) { - //FIXME deal with holes - this.context_.fill(); - } - -}; From 278d32061fd7395ac5a7a69328c52b9cfae4dafc Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 18 Jan 2013 15:55:24 -0700 Subject: [PATCH 008/180] To be reworked for rendering like geoms with same symbolizer --- src/ol/renderer/canvas/canvasrenderer.js | 109 ----------------------- 1 file changed, 109 deletions(-) diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index b6e041c17d..da8c2d0cff 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -32,12 +32,6 @@ ol.renderer.canvas.CanvasRenderer = function() { */ this.context_ = null; - /** - * @type {ol.renderer.Layer} - * @private - */ - this.layerRenderer_ = null; - /** * @type {ol.Pixel} * @private @@ -54,22 +48,11 @@ ol.renderer.canvas.CanvasRenderer = function() { ol.renderer.canvas.CanvasRenderer.prototype.assertReady_ = function() { goog.asserts.assert(!goog.isNull(this.context_), 'Call setTarget before rendering'); - goog.asserts.assert(!goog.isNull(this.layerRenderer_), - 'Call setLayerRenderer before rendering'); goog.asserts.assert(!goog.isNull(this.offset_), 'Call setOffset before rendering'); }; -/** - * @param {ol.renderer.Layer} layerRenderer Layer renderer. - */ -ol.renderer.canvas.CanvasRenderer.prototype.setLayerRenderer = - function(layerRenderer) { - this.layerRenderer_ = layerRenderer; -}; - - /** * @param {ol.Pixel} offset Pixel offset of top left corner of canvas. */ @@ -85,95 +68,3 @@ ol.renderer.canvas.CanvasRenderer.prototype.setTarget = function(canvas) { this.context_ = canvas.getContext('2d'); }; - -/** - * Render a geometry. - * @param {ol.geom.Geometry} geometry The geometry to render. - * @param {Array.} symbolizers Symbolizers to render - * with. - * @private - */ -ol.renderer.canvas.CanvasRenderer.prototype.renderGeometry_ = - function(geometry, symbolizers) { - this.assertReady_(); - if (geometry instanceof ol.geom.Point) { - this.renderPoint_(geometry, symbolizers); - } else { - // TODO: render other geometry types - goog.asserts.assert(false, 'Not yet implemented'); - } -}; - - -/** - * Render a point geometry. - * @param {ol.geom.Point} point Point geometry. - * @param {Array.} symbolizers Symbolizers. - * @private - */ -ol.renderer.canvas.CanvasRenderer.prototype.renderPoint_ = - function(point, symbolizers) { - for (var i = 0, ii = symbolizers.length; i < ii; ++i) { - var symbolizer = symbolizers[i]; - if (symbolizer instanceof ol.style.LiteralShape) { - if (symbolizer.type === ol.style.ShapeType.CIRCLE) { - this.renderPointCircle_(point, symbolizer); - } else { - this.renderPointShape_(point, symbolizer); - } - } - } -}; - - -/** - * Render a point as a circle. - * @param {ol.geom.Point} point Point geometry. - * @param {ol.style.LiteralShape} symbolizer Shape symbolizer. - * @private - */ -ol.renderer.canvas.CanvasRenderer.prototype.renderPointCircle_ = - function(point, symbolizer) { - var coords = point.coordinates, - coordinate = new ol.Coordinate(coords[0], coords[1]), - mapRenderer = this.layerRenderer_.getMapRenderer(), - pixel = mapRenderer.getPixelFromCoordinate(coordinate), - px = pixel.x + this.offset_.x, - py = pixel.y + this.offset_.y, - twoPi = Math.PI * 2, - radius = symbolizer.size / 2, - /** @type {ol.style.LiteralFill} */ - fill = symbolizer.fill, - /** @type {ol.style.LiteralStroke} */ - stroke = symbolizer.stroke, - context = this.context_; - - if (!goog.isNull(fill)) { - context.globalAlpha = fill.opacity; - context.fillStyle = fill.color; - context.beginPath(); - context.arc(px, py, radius, 0, twoPi, true); - context.fill(); - } - if (!goog.isNull(stroke)) { - context.globalAlpha = stroke.opacity; - context.strokeStyle = stroke.color; - context.lineWidth = stroke.width; - context.beginPath(); - context.arc(px, py, radius, 0, twoPi, true); - context.stroke(); - } -}; - - -/** - * Render a point as a shape (other than circle). - * @param {ol.geom.Point} point Point geometry. - * @param {ol.style.LiteralShape} symbolizer Shape symbolizer. - * @private - */ -ol.renderer.canvas.CanvasRenderer.prototype.renderPointShape_ = - function(point, symbolizer) { - // TODO: other shape types - goog.asserts.assert(false, 'Not implemented'); -}; From fd0a5f3622ddb07768cc8722894c119fc0f115f6 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 18 Jan 2013 15:57:48 -0700 Subject: [PATCH 009/180] Point, linestring, and linearring coordinates as Float32Array --- src/ol/geom/geometry.js | 15 ++++++++ src/ol/geom/linearring.js | 27 +++++++++++++++ src/ol/geom/linestring.js | 25 ++++++++++++-- src/ol/geom/multilinestring.js | 26 ++++++++++++-- src/ol/geom/multipoint.js | 26 ++++++++++++-- src/ol/geom/multipolygon.js | 27 +++++++++++++-- src/ol/geom/point.js | 15 ++++++-- src/ol/geom/polygon.js | 28 +++++++++++++-- test/ol.html | 4 +++ test/spec/ol/geom/linearring.test.js | 49 ++++++++++++++++++++++++++ test/spec/ol/geom/linestring.test.js | 49 ++++++++++++++++++++++++++ test/spec/ol/geom/point.test.js | 48 ++++++++++++++++++++++++++ test/spec/ol/geom/polygon.test.js | 51 ++++++++++++++++++++++++++++ 13 files changed, 372 insertions(+), 18 deletions(-) create mode 100644 src/ol/geom/linearring.js create mode 100644 test/spec/ol/geom/linearring.test.js create mode 100644 test/spec/ol/geom/linestring.test.js create mode 100644 test/spec/ol/geom/point.test.js create mode 100644 test/spec/ol/geom/polygon.test.js diff --git a/src/ol/geom/geometry.js b/src/ol/geom/geometry.js index 57f8ec0545..2ddbbe0987 100644 --- a/src/ol/geom/geometry.js +++ b/src/ol/geom/geometry.js @@ -1,7 +1,22 @@ +goog.provide('ol.geom.Coordinate'); +goog.provide('ol.geom.CoordinateArray'); goog.provide('ol.geom.Geometry'); + /** * @interface */ ol.geom.Geometry = function() {}; + + +/** + * @typedef {Array.} + */ +ol.geom.Coordinate; + + +/** + * @typedef {Array.} + */ +ol.geom.CoordinateArray; diff --git a/src/ol/geom/linearring.js b/src/ol/geom/linearring.js new file mode 100644 index 0000000000..0af7ef18dc --- /dev/null +++ b/src/ol/geom/linearring.js @@ -0,0 +1,27 @@ +goog.provide('ol.geom.LinearRing'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Float32Array'); +goog.require('ol.geom.CoordinateArray'); +goog.require('ol.geom.LineString'); + + + +/** + * @constructor + * @extends {ol.geom.LineString} + * @param {ol.geom.CoordinateArray} coordinates Coordinates array (e.g. + * [[x0, y0], [x1, y1], [x0, y0]]). + */ +ol.geom.LinearRing = function(coordinates) { + + goog.base(this, coordinates); + + /** + * We're intentionally not enforcing that rings be closed right now. This + * will allow proper rendering of data from tiled vector sources that leave + * open rings. + */ + +}; +goog.inherits(ol.geom.LinearRing, ol.geom.LineString); diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index 241aff7dfd..d820674615 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -1,5 +1,8 @@ goog.provide('ol.geom.LineString'); +goog.require('goog.asserts'); +goog.require('goog.vec.Float32Array'); +goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); @@ -7,13 +10,29 @@ goog.require('ol.geom.Geometry'); /** * @constructor * @implements {ol.geom.Geometry} - * @param {Array} coordinates Coordinates array. + * @param {ol.geom.CoordinateArray} coordinates Coordinates array (e.g. + * [[x0, y0], [x1, y1]]). */ ol.geom.LineString = function(coordinates) { + // assume the same dimension for all coordinates + var dimension = coordinates[0].length, + count = coordinates.length, + length = count * dimension; + /** - * @type {Array} + * @type {Float32Array} */ - this.coordinates = coordinates; + this.coordinates = new Float32Array(length); + for (var i = 0; i < count; ++i) { + goog.asserts.assert(coordinates[i].length === dimension); + this.coordinates.set(coordinates[i], i * dimension); + } + + /** + * @type {number} + */ + this.dimension = dimension; + goog.asserts.assert(this.dimension >= 2); }; diff --git a/src/ol/geom/multilinestring.js b/src/ol/geom/multilinestring.js index 0665f9d630..f73f24d8e6 100644 --- a/src/ol/geom/multilinestring.js +++ b/src/ol/geom/multilinestring.js @@ -1,19 +1,39 @@ goog.provide('ol.geom.MultiLineString'); +goog.require('goog.asserts'); +goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); +goog.require('ol.geom.LineString'); /** * @constructor * @implements {ol.geom.Geometry} - * @param {Array} coordinates Coordinates array. + * @param {Array.} coordinates Coordinates array. */ ol.geom.MultiLineString = function(coordinates) { + var numParts = coordinates.length, + dimension; + /** - * @type {Array} + * @type {Array.} */ - this.coordinates = coordinates; + 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]; + } else { + goog.asserts.assert(this.components[i].dimension === dimension); + } + } + + /** + * @type {number} + */ + this.dimension = dimension; + goog.asserts.assert(this.dimension >= 2); }; diff --git a/src/ol/geom/multipoint.js b/src/ol/geom/multipoint.js index 586730f6b6..8669a7c0cc 100644 --- a/src/ol/geom/multipoint.js +++ b/src/ol/geom/multipoint.js @@ -1,19 +1,39 @@ goog.provide('ol.geom.MultiPoint'); +goog.require('goog.asserts'); +goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); +goog.require('ol.geom.Point'); /** * @constructor * @implements {ol.geom.Geometry} - * @param {Array} coordinates Coordinates array. + * @param {ol.geom.CoordinateArray} coordinates Coordinates array. */ ol.geom.MultiPoint = function(coordinates) { + var numParts = coordinates.length, + dimension; + /** - * @type {Array} + * @type {Array.} */ - this.coordinates = coordinates; + 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); + } + } + + /** + * @type {number} + */ + this.dimension = dimension; + goog.asserts.assert(this.dimension >= 2); }; diff --git a/src/ol/geom/multipolygon.js b/src/ol/geom/multipolygon.js index 4f22f0d003..958ddf9d5d 100644 --- a/src/ol/geom/multipolygon.js +++ b/src/ol/geom/multipolygon.js @@ -1,19 +1,40 @@ goog.provide('ol.geom.MultiPolygon'); +goog.require('goog.asserts'); +goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); +goog.require('ol.geom.Polygon'); /** * @constructor * @implements {ol.geom.Geometry} - * @param {Array} coordinates Coordinates array. + * @param {Array.>} coordinates Coordinates + * array. */ ol.geom.MultiPolygon = function(coordinates) { + var numParts = coordinates.length, + dimension; + /** - * @type {Array} + * @type {Array.} */ - this.coordinates = coordinates; + 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]; + } else { + goog.asserts.assert(this.components[i].dimension === dimension); + } + } + + /** + * @type {number} + */ + this.dimension = dimension; + goog.asserts.assert(this.dimension >= 2); }; diff --git a/src/ol/geom/point.js b/src/ol/geom/point.js index 2f4e2b6d64..db4bda24fb 100644 --- a/src/ol/geom/point.js +++ b/src/ol/geom/point.js @@ -1,5 +1,8 @@ goog.provide('ol.geom.Point'); +goog.require('goog.asserts'); +goog.require('goog.vec.Float32Array'); +goog.require('ol.geom.Coordinate'); goog.require('ol.geom.Geometry'); @@ -7,13 +10,19 @@ goog.require('ol.geom.Geometry'); /** * @constructor * @implements {ol.geom.Geometry} - * @param {Array} coordinates Coordinates array. + * @param {ol.geom.Coordinate} coordinates Coordinates array (e.g. [x, y]). */ ol.geom.Point = function(coordinates) { /** - * @type {Array} + * @type {Float32Array} */ - this.coordinates = coordinates; + this.coordinates = new Float32Array(coordinates); + + /** + * @type {number} + */ + this.dimension = coordinates.length; + goog.asserts.assert(this.dimension >= 2); }; diff --git a/src/ol/geom/polygon.js b/src/ol/geom/polygon.js index bd0f698c7d..489cd9255e 100644 --- a/src/ol/geom/polygon.js +++ b/src/ol/geom/polygon.js @@ -1,19 +1,41 @@ goog.provide('ol.geom.Polygon'); +goog.require('goog.asserts'); +goog.require('goog.vec.Float32Array'); +goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); +goog.require('ol.geom.LinearRing'); /** * @constructor * @implements {ol.geom.Geometry} - * @param {Array} coordinates Coordinates array. + * @param {Array.} coordinates Array of rings. First + * is outer, any remaining are inner. */ ol.geom.Polygon = function(coordinates) { + var numRings = coordinates.length, + dimension; + /** - * @type {Array} + * @type {Array.} */ - this.coordinates = coordinates; + 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); + } + } + + /** + * @type {number} + */ + this.dimension = dimension; + goog.asserts.assert(this.dimension >= 2); }; diff --git a/test/ol.html b/test/ol.html index b0e05e2f6d..c71623217a 100644 --- a/test/ol.html +++ b/test/ol.html @@ -80,6 +80,10 @@ + + + + diff --git a/test/spec/ol/geom/linearring.test.js b/test/spec/ol/geom/linearring.test.js new file mode 100644 index 0000000000..c32d43651b --- /dev/null +++ b/test/spec/ol/geom/linearring.test.js @@ -0,0 +1,49 @@ +describe('ol.geom.LinearRing', function() { + + describe('constructor', function() { + + it('creates a ring from an array', function() { + var ring = new ol.geom.LinearRing([[10, 20], [30, 40]]); + expect(ring).toBeA(ol.geom.LinearRing); + }); + + it('throws when given mismatched dimension', function() { + expect(function() { + var ring = new ol.geom.LinearRing([[10, 20], [30, 40, 50]]); + }).toThrow(); + }); + + }); + + describe('coordinates', function() { + + it('is a Float32Array', function() { + var ring = new ol.geom.LinearRing([[10, 20], [30, 40]]); + expect(ring.coordinates).toBeA(Float32Array); + + 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() { + var ring = new ol.geom.LinearRing([[10, 20], [30, 40]]); + expect(ring.dimension).toBe(2); + }); + + it('can be 3', function() { + var ring = new ol.geom.LinearRing([[10, 20, 30], [40, 50, 60]]); + expect(ring.dimension).toBe(3); + }); + + }); + + +}); + diff --git a/test/spec/ol/geom/linestring.test.js b/test/spec/ol/geom/linestring.test.js new file mode 100644 index 0000000000..582c6fdf2a --- /dev/null +++ b/test/spec/ol/geom/linestring.test.js @@ -0,0 +1,49 @@ +describe('ol.geom.LineString', function() { + + describe('constructor', function() { + + it('creates a linestring from an array', function() { + var line = new ol.geom.LineString([[10, 20], [30, 40]]); + expect(line).toBeA(ol.geom.LineString); + }); + + it('throws when given mismatched dimension', function() { + expect(function() { + var line = new ol.geom.LineString([[10, 20], [30, 40, 50]]); + }).toThrow(); + }); + + }); + + describe('coordinates', function() { + + it('is a Float32Array', function() { + var line = new ol.geom.LineString([[10, 20], [30, 40]]); + expect(line.coordinates).toBeA(Float32Array); + + 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); + }); + + }); + + describe('dimension', function() { + + it('can be 2', function() { + var line = new ol.geom.LineString([[10, 20], [30, 40]]); + expect(line.dimension).toBe(2); + }); + + it('can be 3', function() { + var line = new ol.geom.LineString([[10, 20, 30], [40, 50, 60]]); + expect(line.dimension).toBe(3); + }); + + }); + + +}); + diff --git a/test/spec/ol/geom/point.test.js b/test/spec/ol/geom/point.test.js new file mode 100644 index 0000000000..237b07394b --- /dev/null +++ b/test/spec/ol/geom/point.test.js @@ -0,0 +1,48 @@ +describe('ol.geom.Point', function() { + + describe('constructor', function() { + + it('creates a point from an array', function() { + var point = new ol.geom.Point([10, 20]); + expect(point).toBeA(ol.geom.Point); + }); + + it('throws when given with insufficient dimensions', function() { + expect(function() { + var point = new ol.geom.Point([1]); + }).toThrow(); + }); + + }); + + describe('coordinates', function() { + + it('is a Float32Array', function() { + var point = new ol.geom.Point([10, 20]); + expect(point.coordinates).toBeA(Float32Array); + + 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() { + var point = new ol.geom.Point([10, 20]); + expect(point.dimension).toBe(2); + }); + + it('can be 3', function() { + var point = new ol.geom.Point([10, 20, 30]); + expect(point.dimension).toBe(3); + }); + + }); + + +}); + diff --git a/test/spec/ol/geom/polygon.test.js b/test/spec/ol/geom/polygon.test.js new file mode 100644 index 0000000000..44b797a993 --- /dev/null +++ b/test/spec/ol/geom/polygon.test.js @@ -0,0 +1,51 @@ +describe('ol.geom.Polygon', 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 polygon from an array', function() { + var poly = new ol.geom.Polygon([outer, inner1, inner2]); + expect(poly).toBeA(ol.geom.Polygon); + }); + + it('throws when given mismatched dimension', function() { + expect(function() { + var poly = new ol.geom.Polygon([[[10, 20], [30, 40, 50]]]); + }).toThrow(); + }); + + }); + + describe('rings', function() { + + it('is an array of LinearRing', function() { + var poly = new ol.geom.Polygon([outer, inner1, inner2]); + + expect(poly.rings.length).toBe(3); + expect(poly.rings[0]).toBeA(ol.geom.LinearRing); + expect(poly.rings[1]).toBeA(ol.geom.LinearRing); + expect(poly.rings[2]).toBeA(ol.geom.LinearRing); + }); + + }); + + describe('dimension', function() { + + it('can be 2', function() { + var poly = new ol.geom.Polygon([outer, inner1, inner2]); + expect(poly.dimension).toBe(2); + }); + + it('can be 3', function() { + var poly = new ol.geom.Polygon([[[10, 20, 30], [40, 50, 60]]]); + expect(poly.dimension).toBe(3); + }); + + }); + + +}); + From b92fdaf9111f151abd55c756b861a7c6780f0a01 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 18 Jan 2013 17:20:10 -0700 Subject: [PATCH 010/180] Going with point, line, and polygon symbolizers Separate fill and stroke symbolizers make a nicer API, but less efficient rendering --- src/ol/style/fill.js | 27 --------------------------- src/ol/style/line.js | 31 +++++++++++++++++++++++++++++++ src/ol/style/point.js | 11 +++++++++++ src/ol/style/polygon.js | 35 +++++++++++++++++++++++++++++++++++ src/ol/style/shape.js | 27 +++++++++++++++++---------- src/ol/style/stroke.js | 32 -------------------------------- 6 files changed, 94 insertions(+), 69 deletions(-) delete mode 100644 src/ol/style/fill.js create mode 100644 src/ol/style/line.js create mode 100644 src/ol/style/point.js create mode 100644 src/ol/style/polygon.js delete mode 100644 src/ol/style/stroke.js diff --git a/src/ol/style/fill.js b/src/ol/style/fill.js deleted file mode 100644 index a583b8beff..0000000000 --- a/src/ol/style/fill.js +++ /dev/null @@ -1,27 +0,0 @@ -goog.provide('ol.style.LiteralFill'); - -goog.require('ol.style.LiteralSymbolizer'); - - -/** - * @typedef {{color: (string), - * opacity: (number)}} - */ -ol.style.LiteralFillConfig; - - - -/** - * @constructor - * @implements {ol.style.LiteralSymbolizer} - * @param {ol.style.LiteralFillConfig} config Symbolizer properties. - */ -ol.style.LiteralFill = function(config) { - - /** @type {string} */ - this.color = config.color; - - /** @type {number} */ - this.opacity = config.opacity; - -}; diff --git a/src/ol/style/line.js b/src/ol/style/line.js new file mode 100644 index 0000000000..e3bc7352c7 --- /dev/null +++ b/src/ol/style/line.js @@ -0,0 +1,31 @@ +goog.provide('ol.style.LiteralLine'); + +goog.require('ol.style.LiteralSymbolizer'); + + +/** + * @typedef {{strokeStyle: (string), + * strokeWidth: (number), + * opacity: (number)}} + */ +ol.style.LiteralLineConfig; + + + +/** + * @constructor + * @implements {ol.style.LiteralSymbolizer} + * @param {ol.style.LiteralLineConfig} config Symbolizer properties. + */ +ol.style.LiteralLine = function(config) { + + /** @type {string} */ + this.strokeStyle = config.strokeStyle; + + /** @type {number} */ + this.strokeWidth = config.strokeWidth; + + /** @type {number} */ + this.opacity = config.opacity; + +}; diff --git a/src/ol/style/point.js b/src/ol/style/point.js new file mode 100644 index 0000000000..d15cf89383 --- /dev/null +++ b/src/ol/style/point.js @@ -0,0 +1,11 @@ +goog.provide('ol.style.LiteralPoint'); + +goog.require('ol.style.LiteralSymbolizer'); + + + +/** + * @interface + * @implements {ol.style.LiteralSymbolizer} + */ +ol.style.LiteralPoint = function() {}; diff --git a/src/ol/style/polygon.js b/src/ol/style/polygon.js new file mode 100644 index 0000000000..160cdfa310 --- /dev/null +++ b/src/ol/style/polygon.js @@ -0,0 +1,35 @@ +goog.provide('ol.style.LiteralPolygon'); + +goog.require('ol.style.LiteralSymbolizer'); + + +/** + * @typedef {{fillStyle: (string), + * strokeStyle: (string), + * strokeWidth: (number), + * opacity: (number)}} + */ +ol.style.LiteralPolygonConfig; + + + +/** + * @constructor + * @implements {ol.style.LiteralSymbolizer} + * @param {ol.style.LiteralPolygonConfig} config Symbolizer properties. + */ +ol.style.LiteralPolygon = function(config) { + + /** @type {string} */ + this.fillStyle = config.fillStyle; + + /** @type {string} */ + this.strokeStyle = config.strokeStyle; + + /** @type {number} */ + this.strokeWidth = config.strokeWidth; + + /** @type {number} */ + this.opacity = config.opacity; + +}; diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js index 09ce5a4a8c..a64e46b437 100644 --- a/src/ol/style/shape.js +++ b/src/ol/style/shape.js @@ -1,8 +1,7 @@ goog.provide('ol.style.LiteralShape'); +goog.provide('ol.style.ShapeType'); -goog.require('ol.style.LiteralFill'); -goog.require('ol.style.LiteralStroke'); -goog.require('ol.style.LiteralSymbolizer'); +goog.require('ol.style.LiteralPoint'); /** @@ -16,8 +15,10 @@ ol.style.ShapeType = { /** * @typedef {{type: (ol.style.ShapeType), * size: (number), - * fill: (ol.style.LiteralFill), - * stroke: (ol.style.LiteralStroke)}} + * fillStyle: (string), + * strokeStyle: (string), + * strokeWidth: (number), + * opacity: (number)}} */ ol.style.LiteralShapeConfig; @@ -25,7 +26,7 @@ ol.style.LiteralShapeConfig; /** * @constructor - * @implements {ol.style.LiteralSymbolizer} + * @implements {ol.style.LiteralPoint} * @param {ol.style.LiteralShapeConfig} config Symbolizer properties. */ ol.style.LiteralShape = function(config) { @@ -36,10 +37,16 @@ ol.style.LiteralShape = function(config) { /** @type {number} */ this.size = config.size; - /** @type {ol.style.LiteralFill} */ - this.fill = config.fill; + /** @type {string} */ + this.fillStyle = config.fillStyle; - /** @type {ol.style.LiteralStroke} */ - this.stroke = config.stroke; + /** @type {string} */ + this.strokeStyle = config.strokeStyle; + + /** @type {number} */ + this.strokeWidth = config.strokeWidth; + + /** @type {number} */ + this.opacity = config.opacity; }; diff --git a/src/ol/style/stroke.js b/src/ol/style/stroke.js deleted file mode 100644 index c8ac1a76a2..0000000000 --- a/src/ol/style/stroke.js +++ /dev/null @@ -1,32 +0,0 @@ -goog.provide('ol.style.LiteralStroke'); - -goog.require('ol.style.LiteralSymbolizer'); - - -/** - * @typedef {{width: (number), - * color: (string), - * opacity: (number)}} - */ -ol.style.LiteralStrokeConfig; - - - -/** - * @constructor - * @implements {ol.style.LiteralSymbolizer} - * @param {ol.style.LiteralStrokeConfig} config Symbolizer properties. - */ -ol.style.LiteralStroke = function(config) { - - /** @type {string} */ - this.color = config.color; - - /** @type {number} */ - this.opacity = config.opacity; - - /** @type {number} */ - this.width = config.width; - -}; - From 140e9ba355ce34b1c0cbba25598cee50edf98c05 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 18 Jan 2013 17:22:07 -0700 Subject: [PATCH 011/180] General purpose canvas renderer Currently only renders points as circles. Eventually will have methods for other geometry types and other symbolizer types. Also provides static methods to render point symbolizers (returning a canvas that can be drawn on another). --- src/ol/renderer/canvas/canvasrenderer.js | 128 ++++++++++++++++++----- src/ol/style/point.js | 2 +- 2 files changed, 100 insertions(+), 30 deletions(-) diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index da8c2d0cff..a22dcfa427 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -1,17 +1,17 @@ goog.provide('ol.renderer.canvas'); -goog.provide('ol.renderer.canvas.CanvasRenderer'); +goog.provide('ol.renderer.canvas.Renderer'); goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); goog.require('ol.Coordinate'); goog.require('ol.Pixel'); goog.require('ol.canvas'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.Point'); goog.require('ol.renderer.Layer'); -goog.require('ol.style.LiteralFill'); +goog.require('ol.style.LiteralPoint'); goog.require('ol.style.LiteralShape'); -goog.require('ol.style.LiteralStroke'); -goog.require('ol.style.LiteralSymbolizer'); +goog.require('ol.style.ShapeType'); /** @@ -23,48 +23,118 @@ ol.renderer.canvas.isSupported = ol.canvas.isSupported; /** * @constructor + * @param {!HTMLCanvasElement} canvas Target canvas. + * @param {!goog.vec.Mat4.Number} transform Transform. + * @param {ol.Pixel=} opt_offset Pixel offset for top-left corner. This is + * provided as an optional argument as a convenience in cases where the + * transform applies to a separate canvas. */ -ol.renderer.canvas.CanvasRenderer = function() { +ol.renderer.canvas.Renderer = function(canvas, transform, opt_offset) { + + var context = /** @type {CanvasRenderingContext2D} */ + (canvas.getContext('2d')), + dx = goog.isDef(opt_offset) ? opt_offset.x : 0, + dy = goog.isDef(opt_offset) ? opt_offset.y : 0; + + context.setTransform( + goog.vec.Mat4.getElement(transform, 0, 0), + goog.vec.Mat4.getElement(transform, 1, 0), + goog.vec.Mat4.getElement(transform, 0, 1), + goog.vec.Mat4.getElement(transform, 1, 1), + goog.vec.Mat4.getElement(transform, 0, 3) + dx, + goog.vec.Mat4.getElement(transform, 1, 3) + dy); /** * @type {CanvasRenderingContext2D} * @private */ - this.context_ = null; - - /** - * @type {ol.Pixel} - * @private - */ - this.offset_ = null; + this.context_ = context; }; /** - * Confirm that we're ready to render. + * @param {Array.} points Point array. + * @param {ol.style.LiteralPoint} symbolizer Point symbolizer. + */ +ol.renderer.canvas.Renderer.prototype.renderPoints = + function(points, symbolizer) { + + var context = this.context_, + canvas, i, ii, coords; + + if (symbolizer instanceof ol.style.LiteralShape) { + canvas = ol.renderer.canvas.Renderer.renderShape(symbolizer); + } else { + throw new Error('Unsupported symbolizer: ' + symbolizer); + } + + var mid = canvas.width / 2; + context.save(); + context.translate(-mid, -mid); + context.globalAlpha = 1; + for (i = 0, ii = points.length; i < ii; ++i) { + coords = points[i].coordinates; + context.drawImage(canvas, coords[0], coords[1]); + } + context.restore(); + +}; + + +/** + * @param {ol.style.LiteralShape} circle Shape symbolizer. + * @return {!HTMLCanvasElement} Canvas element. * @private */ -ol.renderer.canvas.CanvasRenderer.prototype.assertReady_ = function() { - goog.asserts.assert(!goog.isNull(this.context_), - 'Call setTarget before rendering'); - goog.asserts.assert(!goog.isNull(this.offset_), - 'Call setOffset before rendering'); +ol.renderer.canvas.Renderer.renderCircle_ = function(circle) { + var size = circle.size + (2 * circle.strokeWidth) + 1, + mid = size / 2, + canvas = /** @type {HTMLCanvasElement} */ + (goog.dom.createElement(goog.dom.TagName.CANVAS)), + context = /** @type {CanvasRenderingContext2D} */ + (canvas.getContext('2d')), + fillStyle = circle.fillStyle, + strokeStyle = circle.strokeStyle, + twoPi = Math.PI * 2; + + canvas.height = size; + canvas.width = size; + + context.globalAlpha = circle.opacity; + + if (fillStyle) { + context.fillStyle = circle.fillStyle; + } + if (strokeStyle) { + context.lineWidth = circle.strokeWidth; + context.strokeStyle = circle.strokeStyle; + } + + context.beginPath(); + context.arc(mid, mid, circle.size / 2, 0, twoPi, true); + + if (fillStyle) { + context.fill(); + } + if (strokeStyle) { + context.stroke(); + } + return canvas; }; /** - * @param {ol.Pixel} offset Pixel offset of top left corner of canvas. + * @param {ol.style.LiteralShape} shape Shape symbolizer. + * @return {!HTMLCanvasElement} Canvas element. */ -ol.renderer.canvas.CanvasRenderer.prototype.setOffset = function(offset) { - this.offset_ = offset; -}; - - -/** - * @param {Element} canvas Target canvas element. - */ -ol.renderer.canvas.CanvasRenderer.prototype.setTarget = function(canvas) { - this.context_ = canvas.getContext('2d'); +ol.renderer.canvas.Renderer.renderShape = function(shape) { + var canvas; + if (shape.type === ol.style.ShapeType.CIRCLE) { + canvas = ol.renderer.canvas.Renderer.renderCircle_(shape); + } else { + throw new Error('Unsupported shape type: ' + shape); + } + return canvas; }; diff --git a/src/ol/style/point.js b/src/ol/style/point.js index d15cf89383..e937a188a5 100644 --- a/src/ol/style/point.js +++ b/src/ol/style/point.js @@ -6,6 +6,6 @@ goog.require('ol.style.LiteralSymbolizer'); /** * @interface - * @implements {ol.style.LiteralSymbolizer} + * @extends {ol.style.LiteralSymbolizer} */ ol.style.LiteralPoint = function() {}; From 0414e337b3bfc2b1f2d5e4c941550847644c3a31 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Fri, 18 Jan 2013 17:57:32 -0700 Subject: [PATCH 012/180] Render lines --- src/ol/renderer/canvas/canvasrenderer.js | 38 ++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index a22dcfa427..a2ae75f951 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -7,8 +7,9 @@ goog.require('ol.Coordinate'); goog.require('ol.Pixel'); goog.require('ol.canvas'); goog.require('ol.geom.Geometry'); +goog.require('ol.geom.LineString'); goog.require('ol.geom.Point'); -goog.require('ol.renderer.Layer'); +goog.require('ol.style.LiteralLine'); goog.require('ol.style.LiteralPoint'); goog.require('ol.style.LiteralShape'); goog.require('ol.style.ShapeType'); @@ -53,6 +54,40 @@ ol.renderer.canvas.Renderer = function(canvas, transform, opt_offset) { }; +/** + * @param {Array.} lines Line array. + * @param {ol.style.LiteralLine} symbolizer Line symbolizer. + */ +ol.renderer.canvas.Renderer.prototype.renderLineStrings = + function(lines, symbolizer) { + + var context = this.context_, + i, ii, line, coords, dim, j, jj, x, y; + + context.globalAlpha = symbolizer.opacity; + context.strokeStyle = symbolizer.strokeStyle; + context.lineWidth = symbolizer.strokeWidth; + context.beginPath(); + + for (i = 0, ii = lines.length; i < ii; ++i) { + line = lines[i]; + dim = line.dimension; + coords = line.coordinates; + for (j = 0, jj = coords.length; j < jj; j += dim) { + x = coords[j]; + y = coords[j + 1]; + if (j === 0) { + context.moveTo(x, y); + } else { + context.lineTo(x, y); + } + } + } + + context.stroke(); +}; + + /** * @param {Array.} points Point array. * @param {ol.style.LiteralPoint} symbolizer Point symbolizer. @@ -78,7 +113,6 @@ ol.renderer.canvas.Renderer.prototype.renderPoints = context.drawImage(canvas, coords[0], coords[1]); } context.restore(); - }; From 497eac82c28e685ac56a9820ca931b53dd284f88 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 19 Jan 2013 17:50:04 -0700 Subject: [PATCH 013/180] Polygon rendering (no holes yet) --- src/ol/renderer/canvas/canvasrenderer.js | 73 ++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index a2ae75f951..cf7660fd6b 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -9,8 +9,10 @@ goog.require('ol.canvas'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.LineString'); goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); goog.require('ol.style.LiteralLine'); goog.require('ol.style.LiteralPoint'); +goog.require('ol.style.LiteralPolygon'); goog.require('ol.style.LiteralShape'); goog.require('ol.style.ShapeType'); @@ -116,6 +118,77 @@ ol.renderer.canvas.Renderer.prototype.renderPoints = }; +/** + * @param {Array.} polygons Array of polygons. + * @param {ol.style.LiteralPolygon} symbolizer Polygon symbolizer. + */ +ol.renderer.canvas.Renderer.prototype.renderPolygons = + function(polygons, symbolizer) { + + var context = this.context_, + strokeStyle = symbolizer.strokeStyle, + fillStyle = symbolizer.fillStyle, + i, ii, poly, rings, numRings, coords, dim, j, jj, x, y; + + context.globalAlpha = symbolizer.opacity; + if (strokeStyle) { + context.strokeStyle = symbolizer.strokeStyle; + context.lineWidth = symbolizer.strokeWidth; + } + if (fillStyle) { + context.fillStyle = fillStyle; + } + + /** + * Four scenarios covered here: + * 1) stroke only, no holes - only need to have a single path + * 2) fill only, no holes - only need to have a single path + * 3) fill and stroke, no holes + * 4) holes - render polygon to sketch canvas first + */ + context.beginPath(); + for (i = 0, ii = polygons.length; i < ii; ++i) { + poly = polygons[i]; + dim = poly.dimension; + rings = poly.rings; + numRings = rings.length; + if (numRings > 1) { + // scenario 4 + // 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]; + if (j === 0) { + context.moveTo(x, y); + } else { + context.lineTo(x, y); + } + } + if (fillStyle && strokeStyle) { + // scenario 3 - fill and stroke each time + context.fill(); + context.stroke(); + if (i < ii - 1) { + context.beginPath(); + } + } + } + } + if (!(fillStyle && strokeStyle)) { + if (fillStyle) { + // scenario 2 - fill all at once + context.fill(); + } else { + // scenario 1 - stroke all at once + context.stroke(); + } + } +}; + + /** * @param {ol.style.LiteralShape} circle Shape symbolizer. * @return {!HTMLCanvasElement} Canvas element. From 4c2ad6061993fcfd78efd007b1544fef1968742f Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 20 Jan 2013 00:28:07 -0700 Subject: [PATCH 014/180] Add geojson module in io package --- base.json | 2 + externs/geojson.js | 64 +++++++------- src/ol/io/geojson.js | 145 ++++++++++++++++++++++++++++++++ test/ol.html | 1 + test/spec/ol/io/geojson.test.js | 65 ++++++++++++++ 5 files changed, 245 insertions(+), 32 deletions(-) create mode 100644 src/ol/io/geojson.js create mode 100644 test/spec/ol/io/geojson.test.js diff --git a/base.json b/base.json index e0f8102db4..7862da8233 100644 --- a/base.json +++ b/base.json @@ -39,7 +39,9 @@ "disambiguate-properties": true, "externs": [ + "//json.js", "externs/bingmaps.js", + "externs/geojson.js", "externs/proj4js.js", "externs/tilejson.js" ], diff --git a/externs/geojson.js b/externs/geojson.js index 687eb4cecb..6645f8a79e 100644 --- a/externs/geojson.js +++ b/externs/geojson.js @@ -6,17 +6,29 @@ */ - /** * @constructor */ -var GeoJSONCRS = function() {}; +var GeoJSONObject = function() {}; /** * @type {string} */ -GeoJSONCRS.prototype.type; +GeoJSONObject.prototype.type; + + +/** + * @type {!GeoJSONCRS|undefined} + */ +GeoJSONObject.prototype.crs; + + +/** + * @constructor + * @extends {GeoJSONObject} + */ +var GeoJSONCRS = function() {}; /** @@ -28,35 +40,38 @@ GeoJSONCRS.prototype.properties; /** * @constructor + * @extends {GeoJSONObject} */ var GeoJSONGeometry = function() {}; /** - * @type {string} - */ -GeoJSONGeometry.prototype.type; - - -/** - * @type {!Array.|!Array.>} + * @type {!Array.|!Array.>| + * !Array.>>} */ GeoJSONGeometry.prototype.coordinates; +/** + * @constructor + * @extends {GeoJSONObject} + */ +var GeoJSONGeometryCollection = function() {}; + + +/** + * @type {!Array.} + */ +GeoJSONGeometryCollection.prototype.geometries; + /** * @constructor + * @extends {GeoJSONObject} */ var GeoJSONFeature = function() {}; -/** - * @type {string} - */ -GeoJSONFeature.prototype.type; - - /** * @type {GeoJSONGeometry} */ @@ -72,16 +87,11 @@ GeoJSONFeature.prototype.properties; /** * @constructor + * @extends {GeoJSONObject} */ var GeoJSONFeatureCollection = function() {}; -/** - * @type {string} - */ -GeoJSONFeatureCollection.prototype.type; - - /** * @type {!Array.} */ @@ -94,13 +104,3 @@ GeoJSONFeatureCollection.prototype.features; GeoJSONFeatureCollection.prototype.bbox; -/** - * @type {!GeoJSONCRS|undefined} - */ -GeoJSONFeatureCollection.prototype.crs; - - -/** - * @type {!Object.} - */ -GeoJSONFeatureCollection.prototype.properties; diff --git a/src/ol/io/geojson.js b/src/ol/io/geojson.js new file mode 100644 index 0000000000..9587a7a5d0 --- /dev/null +++ b/src/ol/io/geojson.js @@ -0,0 +1,145 @@ +goog.provide('ol.io.geojson'); + +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); + + +/** + * Parse a GeoJSON string. + * @param {string} str GeoJSON string. + * @return {ol.geom.Geometry|Array.} Parsed geometry or array + * of geometries. + */ +ol.io.geojson.read = function(str) { + // TODO: add options and accept projection + var json = /** @type {GeoJSONObject} */ (JSON.parse(str)); + return ol.io.geojson.parse_(json); +}; + + +/** + * @param {GeoJSONObject} json GeoJSON object. + * @return {ol.geom.Geometry|Array.} Parsed geometry or array + * of geometries. + * @private + */ +ol.io.geojson.parse_ = function(json) { + var result; + switch (json.type) { + case 'GeometryCollection': + result = ol.io.geojson.parseGeometryCollection_( + /** @type {GeoJSONGeometryCollection} */ (json)); + break; + case 'Point': + result = ol.io.geojson.parsePoint_( + /** @type {GeoJSONGeometry} */ (json)); + break; + case 'LineString': + result = ol.io.geojson.parseLineString_( + /** @type {GeoJSONGeometry} */ (json)); + break; + case 'Polygon': + result = ol.io.geojson.parsePolygon_( + /** @type {GeoJSONGeometry} */ (json)); + break; + case 'MultiPoint': + result = ol.io.geojson.parseMultiPoint_( + /** @type {GeoJSONGeometry} */ (json)); + break; + case 'MultiLineString': + result = ol.io.geojson.parseMultiLineString_( + /** @type {GeoJSONGeometry} */ (json)); + break; + case 'MultiPolygon': + result = ol.io.geojson.parseMultiPolygon_( + /** @type {GeoJSONGeometry} */ (json)); + break; + default: + throw new Error('GeoJSON parsing not implemented for type: ' + json.type); + } + return result; +}; + + +/** + * @param {GeoJSONGeometryCollection} json GeoJSON geometry collection. + * @return {Array.} Parsed array of geometries. + * @private + */ +ol.io.geojson.parseGeometryCollection_ = function(json) { + var geometries = json.geometries, + len = geometries.length, + result = new Array(len), + i; + + for (i = 0; i < len; ++i) { + result[i] = ol.io.geojson.parse_( + /** @type {GeoJSONGeometry} */ (geometries[i])); + } + return result; +}; + + +/** + * @param {GeoJSONGeometry} json GeoJSON linestring. + * @return {ol.geom.LineString} Parsed linestring. + * @private + */ +ol.io.geojson.parseLineString_ = function(json) { + return new ol.geom.LineString(json.coordinates); +}; + + +/** + * @param {GeoJSONGeometry} json GeoJSON multi-linestring. + * @return {ol.geom.MultiLineString} Parsed multi-linestring. + * @private + */ +ol.io.geojson.parseMultiLineString_ = function(json) { + return new ol.geom.MultiLineString(json.coordinates); +}; + + +/** + * @param {GeoJSONGeometry} json GeoJSON multi-point. + * @return {ol.geom.MultiPoint} Parsed multi-point. + * @private + */ +ol.io.geojson.parseMultiPoint_ = function(json) { + return new ol.geom.MultiPoint(json.coordinates); +}; + + +/** + * @param {GeoJSONGeometry} json GeoJSON multi-polygon. + * @return {ol.geom.MultiPolygon} Parsed multi-polygon. + * @private + */ +ol.io.geojson.parseMultiPolygon_ = function(json) { + return new ol.geom.MultiPolygon(json.coordinates); +}; + + +/** + * @param {GeoJSONGeometry} json GeoJSON point. + * @return {ol.geom.Point} Parsed multi-point. + * @private + */ +ol.io.geojson.parsePoint_ = function(json) { + return new ol.geom.Point(json.coordinates); +}; + + +/** + * @param {GeoJSONGeometry} json GeoJSON polygon. + * @return {ol.geom.Polygon} Parsed polygon. + * @private + */ +ol.io.geojson.parsePolygon_ = function(json) { + return new ol.geom.Polygon(json.coordinates); +}; diff --git a/test/ol.html b/test/ol.html index c71623217a..bdc279a547 100644 --- a/test/ol.html +++ b/test/ol.html @@ -80,6 +80,7 @@ + diff --git a/test/spec/ol/io/geojson.test.js b/test/spec/ol/io/geojson.test.js new file mode 100644 index 0000000000..fc2ab65b4b --- /dev/null +++ b/test/spec/ol/io/geojson.test.js @@ -0,0 +1,65 @@ +describe('ol.io.geojson', function() { + + describe('read()', function() { + + it('parses point', function() { + var str = JSON.stringify({ + type: 'Point', + coordinates: [10, 20] + }); + + var obj = ol.io.geojson.read(str); + expect(obj).toBeA(ol.geom.Point); + expect(obj.coordinates[0]).toBe(10); + expect(obj.coordinates[1]).toBe(20); + }); + + it('parses linestring', function() { + var str = JSON.stringify({ + type: 'LineString', + coordinates: [[10, 20], [30, 40]] + }); + + var obj = ol.io.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); + }); + + it('parses polygon', 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]], + str = JSON.stringify({ + type: 'Polygon', + coordinates: [outer, inner1, inner2] + }); + + var obj = ol.io.geojson.read(str); + expect(obj).toBeA(ol.geom.Polygon); + expect(obj.rings.length).toBe(3); + expect(obj.rings[0]).toBeA(ol.geom.LinearRing); + expect(obj.rings[1]).toBeA(ol.geom.LinearRing); + expect(obj.rings[2]).toBeA(ol.geom.LinearRing); + }); + + it('parses geometry collection', function() { + var str = JSON.stringify({ + type: 'GeometryCollection', + geometries: [ + {type: 'Point', coordinates: [10, 20]}, + {type: 'LineString', coordinates: [[30, 40], [50, 60]]} + ] + }); + + var array = ol.io.geojson.read(str); + expect(array.length).toBe(2); + expect(array[0]).toBeA(ol.geom.Point); + expect(array[1]).toBeA(ol.geom.LineString); + }); + + }); + +}); From d0d397f98f43c7adce06ed97228a874497f8ecc1 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 20 Jan 2013 21:23:19 +0100 Subject: [PATCH 015/180] Tiled vector source This source has features and provides tiles with the feature geometries rendered, using the feature symbolizers. Needs more work because it is still very unefficient, because it renders all feature geometries to all tiles, and uses a collection of features, which we do not have yet. --- src/ol/source/tiledvectorsource.js | 205 +++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 src/ol/source/tiledvectorsource.js diff --git a/src/ol/source/tiledvectorsource.js b/src/ol/source/tiledvectorsource.js new file mode 100644 index 0000000000..a9a9fda49f --- /dev/null +++ b/src/ol/source/tiledvectorsource.js @@ -0,0 +1,205 @@ +goog.provide('ol.source.TiledVector'); +goog.provide('ol.source.TiledVectorOptions'); + + +goog.require('goog.array'); +goog.require('goog.object'); +goog.require('ol.Projection'); +goog.require('ol.source.TileSource'); +goog.require('ol.tilegrid.TileGrid'); + + +/** + * @typedef {{features: (ol.Collection|undefined), + * extent: (ol.Extent|undefined), + * projection: (ol.Projection|undefined), + * tileGrid: (ol.tilegrid.TileGrid|undefined)}} + */ +ol.source.TiledVectorOptions; + + + +/** + * @constructor + * @extends {ol.Tile} + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. + * @private + */ +ol.VectorTile_ = function(tileCoord, tileGrid) { + + goog.base(this, tileCoord); + + this.state = ol.TileState.LOADING; + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.transform_ = goog.vec.Mat4.createNumber(); + + /** + * @private + * @type {Object.} + * FIXME needs to be cleared when the data changes + */ + this.canvasByContext_ = {}; + + /** + * @private + * @type {ol.renderer.canvas.Renderer} + * FIXME Use a shared renderer and cut out tiles; keep track of rendered items + */ + this.renderer_ = this.createRenderer_(tileGrid); + +}; +goog.inherits(ol.VectorTile_, ol.Tile); + + +/** + * @private + * @param {ol.TileGrid} tileGrid tileGrid. + * @return {ol.renderer.canvas.Renderer} The renderer for this tile. + */ +ol.VectorTile_.prototype.createRenderer_ = function(tileGrid) { + var tileSize = tileGrid.getTileSize(); + var canvas = /** @type {HTMLCanvasElement} */ + (goog.dom.createElement(goog.dom.TagName.CANVAS)); + canvas.width = tileSize.width; + canvas.height = tileSize.height; + + var transform = this.transform_; + var origin = tileGrid.getExtent_.getTopLeft(); + var resolution = tileGrid.getResolution(); + goog.vec.Mat4.makeIdentity(transform); + goog.vec.Mat4.scale(transform, resolution, resolution, 1); + goog.vec.Mat4.translate(transform, + origin.x / resolution, -origin.y / resolution, 0); + + this.canvasByContext_[key] = canvas; + + return ol.renderer.canvas.Renderer(canvas, transform); +}; + + +/** + * @param {Array.} geometries Geometries for this tile. + * @param {Array.} symbolizers Symbolizers for the + * geometries, in the same order. + */ +ol.VectorTile_.prototype.setContent = function(geometries, symbolizers) { + this.state = ol.TileState.LOADED; + + var uniqueSymbolizers = []; + goog.array.removeDuplicates(symbolizers, uniqueSymbolizers); + + var geometriesBySymbolizer = goog.array.bucket(geometries, function(e, i) { + var index = goog.array.indexOf(uniqueSymbolizers, symbolizers[i]); + return index === -1 ? undefined : index; + }); + + var buckets, + renderer = this.renderer_, + type = {line: 'line', point: 'point', polygon: 'polygon'}; + function sortByGeometryType(geometry) { + if (geometry instanceof ol.geom.LineString) { + return type['line']; + } else if (geometry instanceof ol.geom.Point) { + return type['point']; + } else if (geometry instanceof ol.geom.Polygon) { + return type['polygon']; + } + } + for (var i = 0, ii = uniqueSymbolizers.length; i < ii; ++i) { + buckets = ol.array.bucket(geometriesBySymbolizer[i], sortByGeometryType); + renderer.renderLineStrings(buckets[type['line']], uniqueSymbolizers[i]); + renderer.renderPoints(buckets[type['point']], uniqueSymbolizers[i]); + renderer.renderPolygons(buckets[type['polygon']], uniqueSymbolizers[i]); + } +}; + + +/** + * @inheritDoc + */ +ol.VectorTile_.prototype.getImage = function(opt_context) { + var key = goog.isDef(opt_context) ? goog.getUid(opt_context) : -1; + if (key in this.canvasByContext_) { + return this.canvasByContext_[key]; + } else { + + return canvas; + + } +}; + + + +/** + * @constructor + * @extends {ol.source.TileSource} + * @param {ol.source.TiledVectorOptions} options options. + */ +ol.source.TiledVector = function(options) { + + /** + * @type {ol.Collection} + * @private + */ + this.features_ = goog.isDef(options.features) ? + options.features : new ol.Collection(); + + /** + * @private + * @type {Object.} + * FIXME will need to expire elements from this cache + * FIXME will need to invalidate tiles when data changes + */ + this.tileCache_ = {}; + + var projection = ol.Projection.createProjection( + options.projection, 'EPSG:3857'); + + goog.base(this, { + extent: goog.isDef(options.extent) ? + options.extent : projection.getExtent(), + projection: projection, + tileGrid: goog.isDef(options.tileGrid) ? + options.tileGrid : ol.tilegrid.createForProjection(projection) + }); + +}; +goog.inherits(ol.source.TiledVector, ol.source.TileSource); + + +/** + * @return {ol.Collection} This layer's features. + */ +ol.source.TiledVector.prototype.getFeatures = function() { + return this.features_; +}; + + +/** + * @inheritDoc + */ +ol.source.TiledVector.prototype.getTile = function(tileCoord) { + var extent = this.tileGrid.getTileCoordExtent(tileCoord); + var key = tileCoord.toString(); + if (goog.object.containsKey(this.tileCache_, key)) { + return this.tileCache_[key]; + } else { + var tile = new ol.VectorTile_(tileCoord, this.tileGrid), + geometries = [], + symbolizers = []; + // FIXME only pass geometries for tile extent + this.features_.forEach(function(feature) { + geometries.push(feature.getGeometry()); + symbolizers.push(feature.getSymbolizer()); + }); + tile.setContent(geometries, symbolizers); + + this.tileCache_[key] = tile; + return tile; + } +}; From d3e63bc98ab5002731472b9dcbe99883171e9961 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 21 Jan 2013 12:16:04 +0100 Subject: [PATCH 016/180] Basic feature implementation --- src/ol/feature/feature.js | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/ol/feature/feature.js diff --git a/src/ol/feature/feature.js b/src/ol/feature/feature.js new file mode 100644 index 0000000000..1ccfda2815 --- /dev/null +++ b/src/ol/feature/feature.js @@ -0,0 +1,64 @@ +goog.provide('ol.feature.Feature'); + + +goog.require('ol.Object'); + + + +/** + * @constructor + * @extends {ol.Object} + * @param {ol.geom.Geometry=} opt_geometry Geometry. + * @param {Object=} opt_values Attributes. + * @param {ol.style.LiteralSymbolizer=} opt_symbolizer Symbolizer. + */ +ol.feature.Feature = function(opt_geometry, opt_values, opt_symbolizer) { + + goog.base(this, opt_values); + + /** + * @private + * @type {?ol.geom.Geometry} + */ + this.geometry_ = goog.isDef(opt_geometry) ? opt_geometry : null; + + /** + * @private + * @type {?ol.style.LiteralSymbolizer} + */ + this.symbolizer_ = goog.isDef(opt_symbolizer) ? opt_symbolizer : null; + +}; +goog.inherits(ol.feature.Feature, ol.Object); + + +/** + * @return {?ol.geom.Geometry} The geometry. + */ +ol.feature.Feature.prototype.getGeometry = function() { + return this.geometry_; +}; + + +/** + * @param {?ol.geom.Geometry} geometry The geometry. + */ +ol.feature.Feature.prototype.setGeometry = function(geometry) { + this.geometry_ = geometry; +}; + + +/** + * @return {?ol.style.LiteralSymbolizer} The symbolizer. + */ +ol.feature.Feature.prototype.getSymbolizer = function() { + return this.symbolizer_; +}; + + +/** + * @param {?ol.style.LiteralSymbolizer} symbolizer The symbolizer. + */ +ol.feature.Feature.prototype.setGeometry = function(symbolizer) { + this.symbolizer_ = symbolizer_; +}; From e060498fadac0044df8e1a23eada0585d959749e Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 21 Jan 2013 13:41:18 +0100 Subject: [PATCH 017/180] Fixing compiler errors (mostly caused by typos) --- src/ol/feature/feature.js | 4 ++-- src/ol/source/tiledvectorsource.js | 23 +++++++++-------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/ol/feature/feature.js b/src/ol/feature/feature.js index 1ccfda2815..e6c79cc821 100644 --- a/src/ol/feature/feature.js +++ b/src/ol/feature/feature.js @@ -59,6 +59,6 @@ ol.feature.Feature.prototype.getSymbolizer = function() { /** * @param {?ol.style.LiteralSymbolizer} symbolizer The symbolizer. */ -ol.feature.Feature.prototype.setGeometry = function(symbolizer) { - this.symbolizer_ = symbolizer_; +ol.feature.Feature.prototype.setSymblizer = function(symbolizer) { + this.symbolizer_ = symbolizer; }; diff --git a/src/ol/source/tiledvectorsource.js b/src/ol/source/tiledvectorsource.js index a9a9fda49f..daba602ac5 100644 --- a/src/ol/source/tiledvectorsource.js +++ b/src/ol/source/tiledvectorsource.js @@ -58,7 +58,7 @@ goog.inherits(ol.VectorTile_, ol.Tile); /** * @private - * @param {ol.TileGrid} tileGrid tileGrid. + * @param {ol.tilegrid.TileGrid} tileGrid tileGrid. * @return {ol.renderer.canvas.Renderer} The renderer for this tile. */ ol.VectorTile_.prototype.createRenderer_ = function(tileGrid) { @@ -69,16 +69,16 @@ ol.VectorTile_.prototype.createRenderer_ = function(tileGrid) { canvas.height = tileSize.height; var transform = this.transform_; - var origin = tileGrid.getExtent_.getTopLeft(); - var resolution = tileGrid.getResolution(); + var origin = tileGrid.getExtent().getTopLeft(); + var resolution = tileGrid.getResolution(this.tileCoord.z); goog.vec.Mat4.makeIdentity(transform); goog.vec.Mat4.scale(transform, resolution, resolution, 1); goog.vec.Mat4.translate(transform, origin.x / resolution, -origin.y / resolution, 0); - this.canvasByContext_[key] = canvas; + this.canvasByContext_[goog.getUid(canvas.getContext('2d'))] = canvas; - return ol.renderer.canvas.Renderer(canvas, transform); + return new ol.renderer.canvas.Renderer(canvas, transform); }; @@ -111,7 +111,7 @@ ol.VectorTile_.prototype.setContent = function(geometries, symbolizers) { } } for (var i = 0, ii = uniqueSymbolizers.length; i < ii; ++i) { - buckets = ol.array.bucket(geometriesBySymbolizer[i], sortByGeometryType); + buckets = goog.array.bucket(geometriesBySymbolizer[i], sortByGeometryType); renderer.renderLineStrings(buckets[type['line']], uniqueSymbolizers[i]); renderer.renderPoints(buckets[type['point']], uniqueSymbolizers[i]); renderer.renderPolygons(buckets[type['polygon']], uniqueSymbolizers[i]); @@ -124,13 +124,8 @@ ol.VectorTile_.prototype.setContent = function(geometries, symbolizers) { */ ol.VectorTile_.prototype.getImage = function(opt_context) { var key = goog.isDef(opt_context) ? goog.getUid(opt_context) : -1; - if (key in this.canvasByContext_) { - return this.canvasByContext_[key]; - } else { - - return canvas; - - } + goog.asserts.assert(key in this.canvasByContext_); + return this.canvasByContext_[key]; }; @@ -151,7 +146,7 @@ ol.source.TiledVector = function(options) { /** * @private - * @type {Object.} + * @type {Object.} * FIXME will need to expire elements from this cache * FIXME will need to invalidate tiles when data changes */ From 300c1048bd4943ae6d9729aedd8c594da1ae5987 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 21 Jan 2013 15:56:35 +0100 Subject: [PATCH 018/180] Cache and bucket fixes --- src/ol/source/tiledvectorsource.js | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/ol/source/tiledvectorsource.js b/src/ol/source/tiledvectorsource.js index daba602ac5..7938d50d88 100644 --- a/src/ol/source/tiledvectorsource.js +++ b/src/ol/source/tiledvectorsource.js @@ -41,9 +41,14 @@ ol.VectorTile_ = function(tileCoord, tileGrid) { /** * @private * @type {Object.} - * FIXME needs to be cleared when the data changes */ this.canvasByContext_ = {}; + + /** + * @private + * @type {HTMLCanvasElement>} + */ + this.canvas_; /** * @private @@ -69,7 +74,7 @@ ol.VectorTile_.prototype.createRenderer_ = function(tileGrid) { canvas.height = tileSize.height; var transform = this.transform_; - var origin = tileGrid.getExtent().getTopLeft(); + var origin = tileGrid.getOrigin(this.tileCoord.z); var resolution = tileGrid.getResolution(this.tileCoord.z); goog.vec.Mat4.makeIdentity(transform); goog.vec.Mat4.scale(transform, resolution, resolution, 1); @@ -77,6 +82,7 @@ ol.VectorTile_.prototype.createRenderer_ = function(tileGrid) { origin.x / resolution, -origin.y / resolution, 0); this.canvasByContext_[goog.getUid(canvas.getContext('2d'))] = canvas; + this.canvas_ = canvas; return new ol.renderer.canvas.Renderer(canvas, transform); }; @@ -112,9 +118,15 @@ ol.VectorTile_.prototype.setContent = function(geometries, symbolizers) { } for (var i = 0, ii = uniqueSymbolizers.length; i < ii; ++i) { buckets = goog.array.bucket(geometriesBySymbolizer[i], sortByGeometryType); - renderer.renderLineStrings(buckets[type['line']], uniqueSymbolizers[i]); - renderer.renderPoints(buckets[type['point']], uniqueSymbolizers[i]); - renderer.renderPolygons(buckets[type['polygon']], uniqueSymbolizers[i]); + if (goog.object.containsKey(buckets, type['line'])) { + renderer.renderLineStrings(buckets[type['line']], uniqueSymbolizers[i]); + } + if (goog.object.containsKey(buckets, type['point'])) { + renderer.renderPoints(buckets[type['point']], uniqueSymbolizers[i]); + } + if (goog.object.containsKey(buckets, type['polygon'])) { + renderer.renderPolygons(buckets[type['polygon']], uniqueSymbolizers[i]); + } } }; @@ -124,8 +136,12 @@ ol.VectorTile_.prototype.setContent = function(geometries, symbolizers) { */ ol.VectorTile_.prototype.getImage = function(opt_context) { var key = goog.isDef(opt_context) ? goog.getUid(opt_context) : -1; - goog.asserts.assert(key in this.canvasByContext_); - return this.canvasByContext_[key]; + if (key in this.canvasByContext_) { + return this.canvasByContext_[key]; + } else { + this.canvasByContext_[key] = this.canvas_; + return this.canvas_; + } }; From 3cf3ef629cc9ca54632eefa87707cca648392e41 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 10:51:38 -0700 Subject: [PATCH 019/180] Lint Caught by `build.py precommit` not `build.py lint`. --- src/ol/source/tiledvectorsource.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/source/tiledvectorsource.js b/src/ol/source/tiledvectorsource.js index 7938d50d88..cc3f8beb4c 100644 --- a/src/ol/source/tiledvectorsource.js +++ b/src/ol/source/tiledvectorsource.js @@ -43,10 +43,10 @@ ol.VectorTile_ = function(tileCoord, tileGrid) { * @type {Object.} */ this.canvasByContext_ = {}; - + /** * @private - * @type {HTMLCanvasElement>} + * @type {HTMLCanvasElement} */ this.canvas_; From 216d30ddc1a948b469ab884f6bd15cf1d6e03db9 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 10:52:17 -0700 Subject: [PATCH 020/180] Getting the nanometer precision we deserve with web mercator --- src/ol/geom/linearring.js | 2 +- src/ol/geom/linestring.js | 6 +++--- src/ol/geom/point.js | 6 +++--- src/ol/geom/polygon.js | 2 +- test/spec/ol/geom/linearring.test.js | 4 ++-- test/spec/ol/geom/linestring.test.js | 4 ++-- test/spec/ol/geom/point.test.js | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/ol/geom/linearring.js b/src/ol/geom/linearring.js index 0af7ef18dc..63fda9fe95 100644 --- a/src/ol/geom/linearring.js +++ b/src/ol/geom/linearring.js @@ -1,7 +1,7 @@ goog.provide('ol.geom.LinearRing'); goog.require('goog.asserts'); -goog.require('goog.vec.Float32Array'); +goog.require('goog.vec.Float64Array'); goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.LineString'); diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index d820674615..a232449af6 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -1,7 +1,7 @@ goog.provide('ol.geom.LineString'); goog.require('goog.asserts'); -goog.require('goog.vec.Float32Array'); +goog.require('goog.vec.Float64Array'); goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); @@ -21,9 +21,9 @@ ol.geom.LineString = function(coordinates) { length = count * dimension; /** - * @type {Float32Array} + * @type {Float64Array} */ - this.coordinates = new Float32Array(length); + this.coordinates = new Float64Array(length); for (var i = 0; i < count; ++i) { goog.asserts.assert(coordinates[i].length === dimension); this.coordinates.set(coordinates[i], i * dimension); diff --git a/src/ol/geom/point.js b/src/ol/geom/point.js index db4bda24fb..931bd72bcf 100644 --- a/src/ol/geom/point.js +++ b/src/ol/geom/point.js @@ -1,7 +1,7 @@ goog.provide('ol.geom.Point'); goog.require('goog.asserts'); -goog.require('goog.vec.Float32Array'); +goog.require('goog.vec.Float64Array'); goog.require('ol.geom.Coordinate'); goog.require('ol.geom.Geometry'); @@ -15,9 +15,9 @@ goog.require('ol.geom.Geometry'); ol.geom.Point = function(coordinates) { /** - * @type {Float32Array} + * @type {Float64Array} */ - this.coordinates = new Float32Array(coordinates); + this.coordinates = new Float64Array(coordinates); /** * @type {number} diff --git a/src/ol/geom/polygon.js b/src/ol/geom/polygon.js index 489cd9255e..ab52e13876 100644 --- a/src/ol/geom/polygon.js +++ b/src/ol/geom/polygon.js @@ -1,7 +1,7 @@ goog.provide('ol.geom.Polygon'); goog.require('goog.asserts'); -goog.require('goog.vec.Float32Array'); +goog.require('goog.vec.Float64Array'); goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.LinearRing'); diff --git a/test/spec/ol/geom/linearring.test.js b/test/spec/ol/geom/linearring.test.js index c32d43651b..adace4fd66 100644 --- a/test/spec/ol/geom/linearring.test.js +++ b/test/spec/ol/geom/linearring.test.js @@ -17,9 +17,9 @@ describe('ol.geom.LinearRing', function() { describe('coordinates', function() { - it('is a Float32Array', function() { + it('is a Float64Array', function() { var ring = new ol.geom.LinearRing([[10, 20], [30, 40]]); - expect(ring.coordinates).toBeA(Float32Array); + expect(ring.coordinates).toBeA(Float64Array); expect(ring.coordinates.length).toBe(4); expect(ring.coordinates[0]).toBe(10); diff --git a/test/spec/ol/geom/linestring.test.js b/test/spec/ol/geom/linestring.test.js index 582c6fdf2a..bdf9e527ed 100644 --- a/test/spec/ol/geom/linestring.test.js +++ b/test/spec/ol/geom/linestring.test.js @@ -17,9 +17,9 @@ describe('ol.geom.LineString', function() { describe('coordinates', function() { - it('is a Float32Array', function() { + it('is a Float64Array', function() { var line = new ol.geom.LineString([[10, 20], [30, 40]]); - expect(line.coordinates).toBeA(Float32Array); + expect(line.coordinates).toBeA(Float64Array); expect(line.coordinates.length).toBe(4); expect(line.coordinates[0]).toBe(10); diff --git a/test/spec/ol/geom/point.test.js b/test/spec/ol/geom/point.test.js index 237b07394b..a9ce41733e 100644 --- a/test/spec/ol/geom/point.test.js +++ b/test/spec/ol/geom/point.test.js @@ -17,9 +17,9 @@ describe('ol.geom.Point', function() { describe('coordinates', function() { - it('is a Float32Array', function() { + it('is a Float64Array', function() { var point = new ol.geom.Point([10, 20]); - expect(point.coordinates).toBeA(Float32Array); + expect(point.coordinates).toBeA(Float64Array); expect(point.coordinates.length).toBe(2); expect(point.coordinates[0]).toBe(10); From b4d44f815f7489f85efe635b0c7347624c2be8d6 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 12:59:04 -0700 Subject: [PATCH 021/180] Add getBounds to geometry --- src/ol/geom/geometry.js | 15 +++++ src/ol/geom/geometrycollection.js | 70 ++++++++++++++++++++++ src/ol/geom/linestring.js | 35 +++++++++++ src/ol/geom/multilinestring.js | 8 ++- src/ol/geom/multipoint.js | 6 +- src/ol/geom/multipolygon.js | 8 ++- src/ol/geom/point.js | 20 +++++++ src/ol/geom/polygon.js | 15 +++++ test/ol.html | 3 + test/spec/ol/geom/linearring.test.js | 4 +- test/spec/ol/geom/linestring.test.js | 17 +++++- test/spec/ol/geom/multilinestring.test.js | 69 ++++++++++++++++++++++ test/spec/ol/geom/multipoint.test.js | 59 +++++++++++++++++++ test/spec/ol/geom/multipolygon.test.js | 72 +++++++++++++++++++++++ test/spec/ol/geom/point.test.js | 16 ++++- test/spec/ol/geom/polygon.test.js | 17 +++++- 16 files changed, 418 insertions(+), 16 deletions(-) create mode 100644 src/ol/geom/geometrycollection.js create mode 100644 test/spec/ol/geom/multilinestring.test.js create mode 100644 test/spec/ol/geom/multipoint.test.js create mode 100644 test/spec/ol/geom/multipolygon.test.js 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); + }); + + }); + }); From 7ef127cfacffb105a493f8ae5aec9fb3afea168b Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 14:46:02 -0700 Subject: [PATCH 022/180] Shorter name for ol.Feature --- src/ol/feature.js | 40 ++++++++++++++++++++++++ src/ol/feature/feature.js | 64 --------------------------------------- 2 files changed, 40 insertions(+), 64 deletions(-) create mode 100644 src/ol/feature.js delete mode 100644 src/ol/feature/feature.js diff --git a/src/ol/feature.js b/src/ol/feature.js new file mode 100644 index 0000000000..920d94aa9b --- /dev/null +++ b/src/ol/feature.js @@ -0,0 +1,40 @@ +goog.provide('ol.Feature'); + +goog.require('ol.Object'); + + + +/** + * @constructor + * @extends {ol.Object} + * @param {ol.geom.Geometry=} opt_geometry Geometry. + * @param {Object=} opt_values Attributes. + */ +ol.Feature = function(opt_geometry, opt_values) { + + goog.base(this, opt_values); + + /** + * @private + * @type {ol.geom.Geometry} + */ + this.geometry_ = goog.isDef(opt_geometry) ? opt_geometry : null; + +}; +goog.inherits(ol.Feature, ol.Object); + + +/** + * @return {ol.geom.Geometry} The geometry (or null if none). + */ +ol.Feature.prototype.getGeometry = function() { + return this.geometry_; +}; + + +/** + * @param {ol.geom.Geometry} geometry The geometry. + */ +ol.Feature.prototype.setGeometry = function(geometry) { + this.geometry_ = geometry; +}; diff --git a/src/ol/feature/feature.js b/src/ol/feature/feature.js deleted file mode 100644 index e6c79cc821..0000000000 --- a/src/ol/feature/feature.js +++ /dev/null @@ -1,64 +0,0 @@ -goog.provide('ol.feature.Feature'); - - -goog.require('ol.Object'); - - - -/** - * @constructor - * @extends {ol.Object} - * @param {ol.geom.Geometry=} opt_geometry Geometry. - * @param {Object=} opt_values Attributes. - * @param {ol.style.LiteralSymbolizer=} opt_symbolizer Symbolizer. - */ -ol.feature.Feature = function(opt_geometry, opt_values, opt_symbolizer) { - - goog.base(this, opt_values); - - /** - * @private - * @type {?ol.geom.Geometry} - */ - this.geometry_ = goog.isDef(opt_geometry) ? opt_geometry : null; - - /** - * @private - * @type {?ol.style.LiteralSymbolizer} - */ - this.symbolizer_ = goog.isDef(opt_symbolizer) ? opt_symbolizer : null; - -}; -goog.inherits(ol.feature.Feature, ol.Object); - - -/** - * @return {?ol.geom.Geometry} The geometry. - */ -ol.feature.Feature.prototype.getGeometry = function() { - return this.geometry_; -}; - - -/** - * @param {?ol.geom.Geometry} geometry The geometry. - */ -ol.feature.Feature.prototype.setGeometry = function(geometry) { - this.geometry_ = geometry; -}; - - -/** - * @return {?ol.style.LiteralSymbolizer} The symbolizer. - */ -ol.feature.Feature.prototype.getSymbolizer = function() { - return this.symbolizer_; -}; - - -/** - * @param {?ol.style.LiteralSymbolizer} symbolizer The symbolizer. - */ -ol.feature.Feature.prototype.setSymblizer = function(symbolizer) { - this.symbolizer_ = symbolizer; -}; From 0fad23970c40c8c4a135d2d800e1baa4a4f2bf13 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 14:46:45 -0700 Subject: [PATCH 023/180] Disallowing mixed geometry collections for now --- src/ol/geom/geometrycollection.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/ol/geom/geometrycollection.js b/src/ol/geom/geometrycollection.js index 419d9239b5..4ff6dd6372 100644 --- a/src/ol/geom/geometrycollection.js +++ b/src/ol/geom/geometrycollection.js @@ -7,8 +7,8 @@ goog.require('ol.geom.Geometry'); /** - * A mixed collection of geometries. This constructor is typically not called - * directly. Instead call @see ol.geom.GeometryCollection#fromGeometries. + * A collection of geometries. This constructor should not called. Instead + * create one of the fixed type collections. * @constructor * @implements {ol.geom.Geometry} */ @@ -57,14 +57,3 @@ ol.geom.GeometryCollection.prototype.getBounds = function() { } 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; -}; From 6ef2184c8352e7cd86ce49cae413dc1e1e5b7a78 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 17:46:41 -0700 Subject: [PATCH 024/180] Geometry type enumeration --- src/ol/geom/geometry.js | 21 +++++++++++++++++++++ src/ol/geom/linestring.js | 9 +++++++++ src/ol/geom/multilinestring.js | 9 +++++++++ src/ol/geom/multipoint.js | 8 ++++++++ src/ol/geom/multipolygon.js | 9 +++++++++ src/ol/geom/point.js | 9 +++++++++ src/ol/geom/polygon.js | 9 +++++++++ 7 files changed, 74 insertions(+) diff --git a/src/ol/geom/geometry.js b/src/ol/geom/geometry.js index 238c9bca51..3b3834d704 100644 --- a/src/ol/geom/geometry.js +++ b/src/ol/geom/geometry.js @@ -2,6 +2,7 @@ goog.require('ol.Extent'); goog.provide('ol.geom.Coordinate'); goog.provide('ol.geom.CoordinateArray'); goog.provide('ol.geom.Geometry'); +goog.provide('ol.geom.GeometryType'); @@ -25,6 +26,13 @@ ol.geom.Geometry.prototype.dimension; ol.geom.Geometry.prototype.getBounds = goog.abstractMethod; +/** + * Get the geometry type. + * @return {ol.geom.GeometryType} The geometry type. + */ +ol.geom.Geometry.prototype.getType = function() {}; + + /** * @typedef {Array.} */ @@ -35,3 +43,16 @@ ol.geom.Coordinate; * @typedef {Array.} */ ol.geom.CoordinateArray; + + +/** + * @enum {string} + */ +ol.geom.GeometryType = { + POINT: 'point', + LINESTRING: 'linestring', + POLYGON: 'polygon', + MULTIPOINT: 'multipoint', + MULTILINESTRING: 'multilinestring', + MULTIPOLYGON: 'multipolygon' +}; diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index a1a5f83f43..256df191a8 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -5,6 +5,7 @@ goog.require('goog.vec.Float64Array'); goog.require('ol.Extent'); goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryType'); @@ -71,3 +72,11 @@ ol.geom.LineString.prototype.getBounds = function() { } return this.bounds_; }; + + +/** + * @inheritDoc + */ +ol.geom.LineString.prototype.getType = function() { + return ol.geom.GeometryType.LINESTRING; +}; diff --git a/src/ol/geom/multilinestring.js b/src/ol/geom/multilinestring.js index 36567eb7eb..c9def94fbe 100644 --- a/src/ol/geom/multilinestring.js +++ b/src/ol/geom/multilinestring.js @@ -3,6 +3,7 @@ goog.provide('ol.geom.MultiLineString'); goog.require('goog.asserts'); goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.GeometryCollection'); +goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); @@ -39,3 +40,11 @@ ol.geom.MultiLineString = function(coordinates) { }; goog.inherits(ol.geom.MultiLineString, ol.geom.GeometryCollection); + + +/** + * @inheritDoc + */ +ol.geom.MultiLineString.prototype.getType = function() { + return ol.geom.GeometryType.MULTILINESTRING; +}; diff --git a/src/ol/geom/multipoint.js b/src/ol/geom/multipoint.js index 4fa8174e3b..bfefe0c957 100644 --- a/src/ol/geom/multipoint.js +++ b/src/ol/geom/multipoint.js @@ -39,3 +39,11 @@ ol.geom.MultiPoint = function(coordinates) { }; goog.inherits(ol.geom.MultiPoint, ol.geom.GeometryCollection); + + +/** + * @inheritDoc + */ +ol.geom.MultiPoint.prototype.getType = function() { + return ol.geom.GeometryType.MULTIPOINT; +}; diff --git a/src/ol/geom/multipolygon.js b/src/ol/geom/multipolygon.js index 8049493784..dcc0ec89c3 100644 --- a/src/ol/geom/multipolygon.js +++ b/src/ol/geom/multipolygon.js @@ -3,6 +3,7 @@ goog.provide('ol.geom.MultiPolygon'); goog.require('goog.asserts'); goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.GeometryCollection'); +goog.require('ol.geom.GeometryType'); goog.require('ol.geom.Polygon'); @@ -40,3 +41,11 @@ ol.geom.MultiPolygon = function(coordinates) { }; goog.inherits(ol.geom.MultiPolygon, ol.geom.GeometryCollection); + + +/** + * @inheritDoc + */ +ol.geom.MultiPolygon.prototype.getType = function() { + return ol.geom.GeometryType.MULTIPOLYGON; +}; diff --git a/src/ol/geom/point.js b/src/ol/geom/point.js index 03f644106d..b2c13f00f2 100644 --- a/src/ol/geom/point.js +++ b/src/ol/geom/point.js @@ -5,6 +5,7 @@ goog.require('goog.vec.Float64Array'); goog.require('ol.Extent'); goog.require('ol.geom.Coordinate'); goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryType'); @@ -46,3 +47,11 @@ ol.geom.Point.prototype.getBounds = function() { } return this.bounds_; }; + + +/** + * @inheritDoc + */ +ol.geom.Point.prototype.getType = function() { + return ol.geom.GeometryType.POINT; +}; diff --git a/src/ol/geom/polygon.js b/src/ol/geom/polygon.js index 90a9b07c88..30ddd5f5be 100644 --- a/src/ol/geom/polygon.js +++ b/src/ol/geom/polygon.js @@ -5,6 +5,7 @@ goog.require('goog.vec.Float64Array'); goog.require('ol.Extent'); goog.require('ol.geom.CoordinateArray'); goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LinearRing'); @@ -54,3 +55,11 @@ ol.geom.Polygon = function(coordinates) { ol.geom.Polygon.prototype.getBounds = function() { return this.rings[0].getBounds(); }; + + +/** + * @inheritDoc + */ +ol.geom.Polygon.prototype.getType = function() { + return ol.geom.GeometryType.POLYGON; +}; From 89bc69ccc1c808c40038e050887fa36418f20361 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 17:56:12 -0700 Subject: [PATCH 025/180] Types for rings and mixed collections --- src/ol/geom/geometry.js | 4 +++- src/ol/geom/geometrycollection.js | 9 +++++++++ src/ol/geom/linearring.js | 11 +++++++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/ol/geom/geometry.js b/src/ol/geom/geometry.js index 3b3834d704..b12a260d44 100644 --- a/src/ol/geom/geometry.js +++ b/src/ol/geom/geometry.js @@ -51,8 +51,10 @@ ol.geom.CoordinateArray; ol.geom.GeometryType = { POINT: 'point', LINESTRING: 'linestring', + LINEARRING: 'linearring', POLYGON: 'polygon', MULTIPOINT: 'multipoint', MULTILINESTRING: 'multilinestring', - MULTIPOLYGON: 'multipolygon' + MULTIPOLYGON: 'multipolygon', + GEOMETRYCOLLECTION: 'geometrycollection' }; diff --git a/src/ol/geom/geometrycollection.js b/src/ol/geom/geometrycollection.js index 4ff6dd6372..4513b171a9 100644 --- a/src/ol/geom/geometrycollection.js +++ b/src/ol/geom/geometrycollection.js @@ -3,6 +3,7 @@ goog.provide('ol.geom.GeometryCollection'); goog.require('goog.asserts'); goog.require('ol.Extent'); goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryType'); @@ -57,3 +58,11 @@ ol.geom.GeometryCollection.prototype.getBounds = function() { } return this.bounds; }; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.getType = function() { + return ol.geom.GeometryType.GEOMETRYCOLLECTION; +}; diff --git a/src/ol/geom/linearring.js b/src/ol/geom/linearring.js index 63fda9fe95..92d181e6e0 100644 --- a/src/ol/geom/linearring.js +++ b/src/ol/geom/linearring.js @@ -1,8 +1,7 @@ goog.provide('ol.geom.LinearRing'); -goog.require('goog.asserts'); -goog.require('goog.vec.Float64Array'); goog.require('ol.geom.CoordinateArray'); +goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); @@ -25,3 +24,11 @@ ol.geom.LinearRing = function(coordinates) { }; goog.inherits(ol.geom.LinearRing, ol.geom.LineString); + + +/** + * @inheritDoc + */ +ol.geom.LinearRing.prototype.getType = function() { + return ol.geom.GeometryType.GEOMETRYCOLLECTION; +}; From 9543ce4d4aed24734eb24f8d2f96ae4dbe290155 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 17:57:12 -0700 Subject: [PATCH 026/180] Initial filter package, geometry type filtering only for now --- src/ol/filter/filter.js | 17 ++++++++++++ src/ol/filter/geometryfilter.js | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 src/ol/filter/filter.js create mode 100644 src/ol/filter/geometryfilter.js diff --git a/src/ol/filter/filter.js b/src/ol/filter/filter.js new file mode 100644 index 0000000000..45f0f3eff8 --- /dev/null +++ b/src/ol/filter/filter.js @@ -0,0 +1,17 @@ +goog.provide('ol.filter.Filter'); + +goog.require('ol.Feature'); + + + +/** + * @interface + */ +ol.filter.Filter = function() {}; + + +/** + * @param {ol.Feature} feature Feature to evaluate. + * @return {boolean} The provided feature passes this filter. + */ +ol.filter.Filter.prototype.evaluate = function(feature) {}; diff --git a/src/ol/filter/geometryfilter.js b/src/ol/filter/geometryfilter.js new file mode 100644 index 0000000000..f9b46176c7 --- /dev/null +++ b/src/ol/filter/geometryfilter.js @@ -0,0 +1,47 @@ +goog.provide('ol.filter.Geometry'); +goog.provide('ol.filter.GeometryType'); + +goog.require('ol.Feature'); +goog.require('ol.filter.Filter'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); + + + +/** + * @constructor + * @implements {ol.filter.Filter} + * @param {ol.geom.GeometryType} type The geometry type. + */ +ol.filter.Geometry = function(type) { + + /** + * @type {ol.geom.GeometryType} + * @private + */ + this.type_ = type; + +}; + + +/** + * @inheritDoc + */ +ol.filter.Geometry.prototype.evaluate = function(feature) { + var geometry = feature.getGeometry(); + return goog.isNull(geometry) ? false : geometry.getType() === this.type_; +}; + + +/** + * @return {ol.geom.GeometryType} The geometry type. + */ +ol.filter.Geometry.prototype.getType = function() { + return this.type_; +}; + From 1a63bf078fdd313d9bbb90b6aa19aacc078c957f Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 17:59:09 -0700 Subject: [PATCH 027/180] Basic vector source and layer The vector source maintains a cache of features. These are indexed by geometry type in anticipation of the renderer's needs. --- src/ol/layer/vectorlayer.js | 24 +++++ src/ol/source/vectorsource.js | 178 ++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 src/ol/layer/vectorlayer.js create mode 100644 src/ol/source/vectorsource.js diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js new file mode 100644 index 0000000000..acdd8d7f27 --- /dev/null +++ b/src/ol/layer/vectorlayer.js @@ -0,0 +1,24 @@ +goog.provide('ol.layer.VectorLayer'); + +goog.require('ol.layer.Layer'); +goog.require('ol.source.Vector'); + + + +/** + * @constructor + * @extends {ol.layer.Layer} + * @param {ol.layer.LayerOptions} layerOptions Layer options. + */ +ol.layer.VectorLayer = function(layerOptions) { + goog.base(this, layerOptions); +}; +goog.inherits(ol.layer.VectorLayer, ol.layer.Layer); + + +/** + * @return {ol.source.Vector} Source. + */ +ol.layer.VectorLayer.prototype.getVectorSource = function() { + return /** @type {ol.source.Vector} */ (this.getSource()); +}; diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js new file mode 100644 index 0000000000..f6a5fa73a1 --- /dev/null +++ b/src/ol/source/vectorsource.js @@ -0,0 +1,178 @@ +goog.provide('ol.source.Vector'); + +goog.require('ol.Feature'); +goog.require('ol.filter.Filter'); +goog.require('ol.filter.Geometry'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.source.Source'); + + + +/** + * @constructor + */ +ol.source.FeatureCache = function() { + + /** + * @type {Object.} + * @private + */ + this.idLookup_; + + + /** + * @type {Object.>} + * @private + */ + this.geometryTypeIndex_; + + this.clear(); + +}; + + +/** + * Clear the cache. + */ +ol.source.FeatureCache.prototype.clear = function() { + this.idLookup_ = {}; + + var geometryTypeIndex_ = {}; + for (var key in ol.geom.GeometryType) { + geometryTypeIndex_[ol.geom.GeometryType[key]] = []; + } + this.geometryTypeIndex_ = geometryTypeIndex_; +}; + + +/** + * Add a feature to the cache. + * @param {ol.Feature} feature Feature to be cached. + */ +ol.source.FeatureCache.prototype.add = function(feature) { + var id = goog.getUid(feature).toString(), + geometry = feature.getGeometry(); + + this.idLookup_[id] = feature; + + // index by geometry type + if (!goog.isNull(geometry)) { + this.geometryTypeIndex_[geometry.getType()].push(id); + } + + /** + * TODO: Index by tile coord. To do this for real requires knowledge about + * the evaluated symbolizer literal for each feature. Initially, a pixel + * buffer could be provided. + */ + +}; + + +/** + * @param {ol.filter.Filter=} opt_filter Optional filter. + * @return {Array.} Array of features. + */ +ol.source.FeatureCache.prototype.getFeatures = function(opt_filter) { + var features; + if (!goog.isDef(opt_filter)) { + features = new Array(); + for (var id in this.idLookup_) { + features.push(this.idLookup_[id]); + } + } else { + if (opt_filter instanceof ol.filter.Geometry) { + features = this.getFeaturesByGeometryType_( + /** @type {ol.filter.Geometry} */ (opt_filter)); + } else { + // TODO: support other filter types + throw new Error('Filter type not supported: ' + opt_filter); + } + } + return features; +}; + + +/** + * @param {ol.filter.Geometry} filter Geometry type filter. + * @return {Array.} Array of features. + * @private + */ +ol.source.FeatureCache.prototype.getFeaturesByGeometryType_ = function(filter) { + var type = filter.getType(), + ids = this.geometryTypeIndex_[filter.getType()]; + + return this.getFeaturesByIds_(ids); +}; + + +/** + * Get features by ids. + * @param {Array.} ids Array of (internal) identifiers. + * @return {Array.} Array of features. + * @private + */ +ol.source.FeatureCache.prototype.getFeaturesByIds_ = function(ids) { + var len = ids.length, + features = new Array(len), + i; + for (i = 0; i < len; ++i) { + features[i] = this.idLookup_[ids[i]]; + } + return features; +}; + + +/** + * @typedef {{attributions: (Array.|undefined), + * extent: (ol.Extent|undefined), + * projection: (ol.Projection|undefined)}} + */ +ol.source.VectorOptions; + + + + +/** + * @constructor + * @extends {ol.source.Source} + * @param {ol.source.VectorOptions} options Source options. + */ +ol.source.Vector = function(options) { + + goog.base(this, { + attributions: options.attributions, + extent: options.extent, + projection: options.projection + }); + + /** + * @type {ol.source.FeatureCache} + * @private + */ + this.featureCache_ = null; + +}; +goog.inherits(ol.source.Vector, ol.source.Source); + + + +/** + * @param {Array.} features Array of features. + */ +ol.source.Vector.prototype.setFeatures = function(features) { + this.featureCache_.clear(); + + for (var i = 0, ii = features.length; i < ii; ++i) { + this.featureCache_.add(features[i]); + } +}; + + +/** + * @param {ol.filter.Filter=} opt_filter Optional filter. + * @return {Array.} Array of features. + */ +ol.source.Vector.prototype.getFeatures = function(opt_filter) { + return this.featureCache_.getFeatures(opt_filter); +}; From 0f33561af2e15a67a0ea55bdf4b420e2d5f98090 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 18:02:28 -0700 Subject: [PATCH 028/180] Skeleton canvas renderer for vector layer --- .../canvas/canvasvectorlayerrenderer.js | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/ol/renderer/canvas/canvasvectorlayerrenderer.js diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js new file mode 100644 index 0000000000..490959cbed --- /dev/null +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -0,0 +1,75 @@ +goog.provide('ol.renderer.canvas.VectorLayer'); + +goog.require('goog.vec.Mat4'); +goog.require('ol.layer.Vector'); + + + +/** + * @constructor + * @extends {ol.renderer.canvas.Layer} + * @param {ol.renderer.Map} mapRenderer Map renderer. + * @param {ol.layer.Vector} layer Vector layer. + */ +ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { + + goog.base(this, mapRenderer, layer); + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = null; + + /** + * @private + * @type {ol.Size} + */ + this.canvasSize_ = null; + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.context_ = null; + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.transform_ = goog.vec.Mat4.createNumber(); + +}; +goog.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); + + +/** + * @inheritDoc + */ +ol.renderer.canvas.VectorLayer.prototype.getImage = function() { + return this.canvas_; +}; + + +/** + * @return {ol.layer.Vector} Vector layer. + */ +ol.renderer.canvas.VectorLayer.prototype.getVectorLayer = function() { + return /** @type {ol.layer.Vector} */ (this.getLayer()); +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.VectorLayer.prototype.getTransform = function() { + return this.transform_; +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.VectorLayer.prototype.renderFrame = + function(frameState, layerState) { +}; From f80f4fab36602592118106fe679698531ee6054b Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 18:03:25 -0700 Subject: [PATCH 029/180] Symbolizer literals will be cached by the renderer --- src/ol/source/tiledvectorsource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/source/tiledvectorsource.js b/src/ol/source/tiledvectorsource.js index cc3f8beb4c..6c3d786503 100644 --- a/src/ol/source/tiledvectorsource.js +++ b/src/ol/source/tiledvectorsource.js @@ -206,7 +206,7 @@ ol.source.TiledVector.prototype.getTile = function(tileCoord) { // FIXME only pass geometries for tile extent this.features_.forEach(function(feature) { geometries.push(feature.getGeometry()); - symbolizers.push(feature.getSymbolizer()); + // symbolizers.push(feature.getSymbolizer()); }); tile.setContent(geometries, symbolizers); From e4132813af567ffd95683e7b632cda94902c95d1 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 18:06:14 -0700 Subject: [PATCH 030/180] Shorter name --- src/ol/layer/vectorlayer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index acdd8d7f27..6ef234f60d 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -1,4 +1,4 @@ -goog.provide('ol.layer.VectorLayer'); +goog.provide('ol.layer.Vector'); goog.require('ol.layer.Layer'); goog.require('ol.source.Vector'); @@ -10,15 +10,15 @@ goog.require('ol.source.Vector'); * @extends {ol.layer.Layer} * @param {ol.layer.LayerOptions} layerOptions Layer options. */ -ol.layer.VectorLayer = function(layerOptions) { +ol.layer.Vector = function(layerOptions) { goog.base(this, layerOptions); }; -goog.inherits(ol.layer.VectorLayer, ol.layer.Layer); +goog.inherits(ol.layer.Vector, ol.layer.Layer); /** * @return {ol.source.Vector} Source. */ -ol.layer.VectorLayer.prototype.getVectorSource = function() { +ol.layer.Vector.prototype.getVectorSource = function() { return /** @type {ol.source.Vector} */ (this.getSource()); }; From 45af404f4bfdfc0ac89e7edd276c0a866e00ce1b Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 18:06:29 -0700 Subject: [PATCH 031/180] Initial tests for filter --- test/ol.html | 1 + test/spec/ol/filter/geometryfilter.test.js | 45 ++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 test/spec/ol/filter/geometryfilter.test.js diff --git a/test/ol.html b/test/ol.html index fd2d2cb985..b21a91519d 100644 --- a/test/ol.html +++ b/test/ol.html @@ -81,6 +81,7 @@ + diff --git a/test/spec/ol/filter/geometryfilter.test.js b/test/spec/ol/filter/geometryfilter.test.js new file mode 100644 index 0000000000..5e07ea2bbd --- /dev/null +++ b/test/spec/ol/filter/geometryfilter.test.js @@ -0,0 +1,45 @@ +describe('ol.filter.Geometry', function() { + + describe('constructor', function() { + it('creates a new filter', function() { + var filter = new ol.filter.Geometry(ol.filter.GeometryType.POINT); + expect(filter).toBeA(ol.filter.Geometry); + }); + }); + + describe('#getType()', function() { + + it('works for point', function() { + var filter = new ol.filter.Geometry(ol.filter.GeometryType.POINT); + expect(filter.getType()).toBe(ol.filter.GeometryType.POINT); + }); + + it('works for linestring', function() { + var filter = new ol.filter.Geometry(ol.filter.GeometryType.LINESTRING); + expect(filter.getType()).toBe(ol.filter.GeometryType.LINESTRING); + }); + + it('works for polygon', function() { + var filter = new ol.filter.Geometry(ol.filter.GeometryType.POLYGON); + expect(filter.getType()).toBe(ol.filter.GeometryType.POLYGON); + }); + + it('works for multi-point', function() { + var filter = new ol.filter.Geometry(ol.filter.GeometryType.MULTIPOINT); + expect(filter.getType()).toBe(ol.filter.GeometryType.MULTIPOINT); + }); + + it('works for multi-linestring', function() { + var filter = new ol.filter.Geometry( + ol.filter.GeometryType.MULTILINESTRING); + expect(filter.getType()).toBe(ol.filter.GeometryType.MULTILINESTRING); + }); + + it('works for multi-polygon', function() { + var filter = new ol.filter.Geometry(ol.filter.GeometryType.MULTIPOLYGON); + expect(filter.getType()).toBe(ol.filter.GeometryType.MULTIPOLYGON); + }); + + }); + +}); From 92c8c9c5df4abbe5451667bc3b38bf5996f87dad Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 21 Jan 2013 23:20:52 -0700 Subject: [PATCH 032/180] Shape extends point --- src/ol/style/point.js | 4 ++-- src/ol/style/shape.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ol/style/point.js b/src/ol/style/point.js index e937a188a5..acbed220c4 100644 --- a/src/ol/style/point.js +++ b/src/ol/style/point.js @@ -5,7 +5,7 @@ goog.require('ol.style.LiteralSymbolizer'); /** - * @interface - * @extends {ol.style.LiteralSymbolizer} + * @constructor + * @implements {ol.style.LiteralSymbolizer} */ ol.style.LiteralPoint = function() {}; diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js index a64e46b437..e188d8c354 100644 --- a/src/ol/style/shape.js +++ b/src/ol/style/shape.js @@ -26,7 +26,7 @@ ol.style.LiteralShapeConfig; /** * @constructor - * @implements {ol.style.LiteralPoint} + * @extends {ol.style.LiteralPoint} * @param {ol.style.LiteralShapeConfig} config Symbolizer properties. */ ol.style.LiteralShape = function(config) { @@ -50,3 +50,4 @@ ol.style.LiteralShape = function(config) { this.opacity = config.opacity; }; +goog.inherits(ol.style.LiteralShape, ol.style.LiteralPoint); From 1dd17fc88f1c407c746c23e2ea9de3c6a1dd4606 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 22 Jan 2013 01:36:32 -0700 Subject: [PATCH 033/180] Rendering vector layers --- src/ol/renderer/canvas/canvasmaprenderer.js | 7 +- src/ol/renderer/canvas/canvasrenderer.js | 95 ++++++++++++++----- .../canvas/canvasvectorlayerrenderer.js | 87 +++++++++++++++++ src/ol/source/vectorsource.js | 8 +- 4 files changed, 166 insertions(+), 31 deletions(-) diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index b664dc977a..79e4478681 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -9,6 +9,7 @@ goog.require('ol.Size'); goog.require('ol.layer.TileLayer'); goog.require('ol.renderer.Map'); goog.require('ol.renderer.canvas.TileLayer'); +goog.require('ol.renderer.canvas.VectorLayer'); @@ -60,6 +61,8 @@ goog.inherits(ol.renderer.canvas.Map, ol.renderer.Map); ol.renderer.canvas.Map.prototype.createLayerRenderer = function(layer) { if (layer instanceof ol.layer.TileLayer) { return new ol.renderer.canvas.TileLayer(this, layer); + } else if (layer instanceof ol.layer.Vector) { + return new ol.renderer.canvas.VectorLayer(this, layer); } else { goog.asserts.assert(false); return null; @@ -141,6 +144,8 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) { context.globalAlpha = 1; context.fillRect(0, 0, size.width, size.height); + this.calculateMatrices2D(frameState); + goog.array.forEach(frameState.layersArray, function(layer) { var layerState = frameState.layerStates[goog.getUid(layer)]; @@ -172,6 +177,4 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) { this.renderedVisible_ = true; } - this.calculateMatrices2D(frameState); - }; diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index cf7660fd6b..7270fe476b 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -14,6 +14,7 @@ goog.require('ol.style.LiteralLine'); goog.require('ol.style.LiteralPoint'); goog.require('ol.style.LiteralPolygon'); goog.require('ol.style.LiteralShape'); +goog.require('ol.style.LiteralSymbolizer'); goog.require('ol.style.ShapeType'); @@ -26,8 +27,8 @@ ol.renderer.canvas.isSupported = ol.canvas.isSupported; /** * @constructor - * @param {!HTMLCanvasElement} canvas Target canvas. - * @param {!goog.vec.Mat4.Number} transform Transform. + * @param {HTMLCanvasElement} canvas Target canvas. + * @param {goog.vec.Mat4.Number} transform Transform. * @param {ol.Pixel=} opt_offset Pixel offset for top-left corner. This is * provided as an optional argument as a convenience in cases where the * transform applies to a separate canvas. @@ -39,6 +40,11 @@ ol.renderer.canvas.Renderer = function(canvas, transform, opt_offset) { dx = goog.isDef(opt_offset) ? opt_offset.x : 0, dy = goog.isDef(opt_offset) ? opt_offset.y : 0; + /** + * @type {goog.vec.Mat4.Number} + * @private + */ + this.transform_ = transform; context.setTransform( goog.vec.Mat4.getElement(transform, 0, 0), goog.vec.Mat4.getElement(transform, 1, 0), @@ -47,6 +53,15 @@ ol.renderer.canvas.Renderer = function(canvas, transform, opt_offset) { goog.vec.Mat4.getElement(transform, 0, 3) + dx, goog.vec.Mat4.getElement(transform, 1, 3) + dy); + var vec = [1, 0, 0]; + goog.vec.Mat4.multVec3NoTranslate(transform, vec, vec); + + /** + * @type {number} + * @private + */ + this.inverseScale_ = 1 / Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]); + /** * @type {CanvasRenderingContext2D} * @private @@ -57,22 +72,52 @@ ol.renderer.canvas.Renderer = function(canvas, transform, opt_offset) { /** - * @param {Array.} lines Line array. - * @param {ol.style.LiteralLine} symbolizer Line symbolizer. + * @param {ol.geom.GeometryType} type Geometry type. + * @param {Array.} features Array of features. + * @param {ol.style.LiteralSymbolizer} symbolizer Symbolizer. */ -ol.renderer.canvas.Renderer.prototype.renderLineStrings = - function(lines, symbolizer) { +ol.renderer.canvas.Renderer.prototype.renderFeaturesByGeometryType = + function(type, features, symbolizer) { + switch (type) { + case ol.geom.GeometryType.POINT: + goog.asserts.assert(symbolizer instanceof ol.style.LiteralPoint); + this.renderPointFeatures_( + features, /** @type {ol.style.LiteralPoint} */ (symbolizer)); + break; + case ol.geom.GeometryType.LINESTRING: + goog.asserts.assert(symbolizer instanceof ol.style.LiteralLine); + this.renderLineStringFeatures_( + features, /** @type {ol.style.LiteralLine} */ (symbolizer)); + break; + case ol.geom.GeometryType.POLYGON: + goog.asserts.assert(symbolizer instanceof ol.style.LiteralPolygon); + this.renderPolygonFeatures_( + features, /** @type {ol.style.LiteralPolygon} */ (symbolizer)); + break; + default: + throw new Error('Rendering not implemented for geometry type: ' + type); + } +}; + + +/** + * @param {Array.} features Array of line features. + * @param {ol.style.LiteralLine} symbolizer Line symbolizer. + * @private + */ +ol.renderer.canvas.Renderer.prototype.renderLineStringFeatures_ = + function(features, symbolizer) { var context = this.context_, i, ii, line, coords, dim, j, jj, x, y; context.globalAlpha = symbolizer.opacity; context.strokeStyle = symbolizer.strokeStyle; - context.lineWidth = symbolizer.strokeWidth; + context.lineWidth = symbolizer.strokeWidth * this.inverseScale_; context.beginPath(); - for (i = 0, ii = lines.length; i < ii; ++i) { - line = lines[i]; + for (i = 0, ii = features.length; i < ii; ++i) { + line = features[i].getGeometry(); dim = line.dimension; coords = line.coordinates; for (j = 0, jj = coords.length; j < jj; j += dim) { @@ -91,14 +136,15 @@ ol.renderer.canvas.Renderer.prototype.renderLineStrings = /** - * @param {Array.} points Point array. + * @param {Array.} features Array of point features. * @param {ol.style.LiteralPoint} symbolizer Point symbolizer. + * @private */ -ol.renderer.canvas.Renderer.prototype.renderPoints = - function(points, symbolizer) { +ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = + function(features, symbolizer) { var context = this.context_, - canvas, i, ii, coords; + canvas, i, ii, coords, vec; if (symbolizer instanceof ol.style.LiteralShape) { canvas = ol.renderer.canvas.Renderer.renderShape(symbolizer); @@ -108,22 +154,25 @@ ol.renderer.canvas.Renderer.prototype.renderPoints = var mid = canvas.width / 2; context.save(); - context.translate(-mid, -mid); + context.setTransform(1, 0, 0, 1, -mid, -mid); context.globalAlpha = 1; - for (i = 0, ii = points.length; i < ii; ++i) { - coords = points[i].coordinates; - context.drawImage(canvas, coords[0], coords[1]); + for (i = 0, ii = features.length; i < ii; ++i) { + coords = features[i].getGeometry().coordinates; + vec = goog.vec.Mat4.multVec3( + this.transform_, [coords[0], coords[1], 0], []); + context.drawImage(canvas, vec[0], vec[1]); } context.restore(); }; /** - * @param {Array.} polygons Array of polygons. + * @param {Array.} features Array of polygon features. * @param {ol.style.LiteralPolygon} symbolizer Polygon symbolizer. + * @private */ -ol.renderer.canvas.Renderer.prototype.renderPolygons = - function(polygons, symbolizer) { +ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = + function(features, symbolizer) { var context = this.context_, strokeStyle = symbolizer.strokeStyle, @@ -133,7 +182,7 @@ ol.renderer.canvas.Renderer.prototype.renderPolygons = context.globalAlpha = symbolizer.opacity; if (strokeStyle) { context.strokeStyle = symbolizer.strokeStyle; - context.lineWidth = symbolizer.strokeWidth; + context.lineWidth = symbolizer.strokeWidth * this.inverseScale_; } if (fillStyle) { context.fillStyle = fillStyle; @@ -147,8 +196,8 @@ ol.renderer.canvas.Renderer.prototype.renderPolygons = * 4) holes - render polygon to sketch canvas first */ context.beginPath(); - for (i = 0, ii = polygons.length; i < ii; ++i) { - poly = polygons[i]; + for (i = 0, ii = features.length; i < ii; ++i) { + poly = features[i].getGeometry(); dim = poly.dimension; rings = poly.rings; numRings = rings.length; diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 490959cbed..010364e5e0 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -1,6 +1,8 @@ goog.provide('ol.renderer.canvas.VectorLayer'); goog.require('goog.vec.Mat4'); +goog.require('ol.filter.Geometry'); +goog.require('ol.geom.GeometryType'); goog.require('ol.layer.Vector'); @@ -38,6 +40,19 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { * @type {!goog.vec.Mat4.Number} */ this.transform_ = goog.vec.Mat4.createNumber(); + goog.vec.Mat4.makeIdentity(this.transform_); + + /** + * Geometry filters in rendering order. + * @private + * @type {Array.} + * TODO: deal with multis + */ + this.geometryFilters_ = [ + new ol.filter.Geometry(ol.geom.GeometryType.POLYGON), + new ol.filter.Geometry(ol.geom.GeometryType.LINESTRING), + new ol.filter.Geometry(ol.geom.GeometryType.POINT) + ]; }; goog.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); @@ -72,4 +87,76 @@ ol.renderer.canvas.VectorLayer.prototype.getTransform = function() { */ ol.renderer.canvas.VectorLayer.prototype.renderFrame = function(frameState, layerState) { + + var view2DState = frameState.view2DState; + + var layer = this.getVectorLayer(); + var source = layer.getVectorSource(); + var extent = frameState.extent; + + var canvasSize = frameState.size; + + var canvas, context; + if (goog.isNull(this.canvas_)) { + canvas = /** @type {HTMLCanvasElement} */ + (goog.dom.createElement(goog.dom.TagName.CANVAS)); + canvas.width = canvasSize.width; + canvas.height = canvasSize.height; + context = /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')); + this.canvas_ = canvas; + this.canvasSize_ = canvasSize; + this.context_ = context; + } else { + canvas = this.canvas_; + context = this.context_; + // force clear the canvas + canvas.width = canvasSize.width; + canvas.height = canvasSize.height; + this.canvasSize_ = canvasSize; + } + + /** + * For now, we create a canvas renderer and have it render all features with + * every call to renderFrame. + * TODO: only render newly visible/dirty areas + */ + var canvasRenderer = new ol.renderer.canvas.Renderer( + canvas, frameState.coordinateToPixelMatrix); + + // TODO: get these elsewhere + var symbolizers = { + point: new ol.style.LiteralShape({ + type: ol.style.ShapeType.CIRCLE, + size: 10, + fillStyle: '#ffcc99', + strokeStyle: '#ff9933', + strokeWidth: 2, + opacity: 0.75 + }), + linestring: new ol.style.LiteralLine({ + strokeStyle: '#ff9933', + strokeWidth: 2, + opacity: 1 + }), + polygon: new ol.style.LiteralPolygon({ + fillStyle: '#ffcc99', + strokeStyle: '#ff9933', + strokeWidth: 2, + opacity: 0.5 + }) + }; + + // render features by geometry type + var filters = this.geometryFilters_, + numFilters = filters.length, + i, filter, type, features, symbolizer; + + for (i = 0; i < numFilters; ++i) { + filter = filters[i]; + type = filter.getType(); + features = source.getFeatures(filter); + symbolizer = symbolizers[type]; + canvasRenderer.renderFeaturesByGeometryType(type, features, symbolizer); + } + }; diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index f6a5fa73a1..3601bf3a6d 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -132,7 +132,6 @@ ol.source.VectorOptions; - /** * @constructor * @extends {ol.source.Source} @@ -150,19 +149,16 @@ ol.source.Vector = function(options) { * @type {ol.source.FeatureCache} * @private */ - this.featureCache_ = null; + this.featureCache_ = new ol.source.FeatureCache(); }; goog.inherits(ol.source.Vector, ol.source.Source); - /** * @param {Array.} features Array of features. */ -ol.source.Vector.prototype.setFeatures = function(features) { - this.featureCache_.clear(); - +ol.source.Vector.prototype.addFeatures = function(features) { for (var i = 0, ii = features.length; i < ii; ++i) { this.featureCache_.add(features[i]); } From c50fcecf42c1048ffe4f40e456d05a4f4d3ae02e Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 22 Jan 2013 01:46:27 -0700 Subject: [PATCH 034/180] Vector rendering example There is a ton of room for optimization here. The vector layer renderer should only render dirty areas and could maintain a cache of rendered canvas tiles. The vector source could have a simple spatial index for features (by tile coord). Need to also discuss how to work with this animation framework (to avoid the excess work while waiting for tiles to load on every other layer). --- examples/vector-layer.html | 22 +++++++++++ examples/vector-layer.js | 39 +++++++++++++++++++ .../canvas/canvasvectorlayerrenderer.js | 6 ++- 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 examples/vector-layer.html create mode 100644 examples/vector-layer.js diff --git a/examples/vector-layer.html b/examples/vector-layer.html new file mode 100644 index 0000000000..fa79b0bcd5 --- /dev/null +++ b/examples/vector-layer.html @@ -0,0 +1,22 @@ + + + + + + + + + ol3 vector demo + + +
+ + + diff --git a/examples/vector-layer.js b/examples/vector-layer.js new file mode 100644 index 0000000000..3060620341 --- /dev/null +++ b/examples/vector-layer.js @@ -0,0 +1,39 @@ +goog.require('ol.Collection'); +goog.require('ol.Coordinate'); +goog.require('ol.Map'); +goog.require('ol.Projection'); +goog.require('ol.layer.TileLayer'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.MapQuestOpenAerial'); +goog.require('ol.source.Vector'); + + +var raster = new ol.layer.TileLayer({ + source: new ol.source.MapQuestOpenAerial() +}); + +var source = new ol.source.Vector({ + projection: ol.Projection.getFromCode('EPSG:3857') +}); + +source.addFeatures([ + new ol.Feature( + new ol.geom.LineString([[-10000000, -10000000], [10000000, 10000000]])), + new ol.Feature( + new ol.geom.LineString([[-10000000, 10000000], [10000000, -10000000]])), + new ol.Feature(new ol.geom.Point([-10000000, 5000000])) +]); + +var vector = new ol.layer.Vector({ + source: source +}); + +var map = new ol.Map({ + layers: new ol.Collection([raster, vector]), + renderer: ol.RendererHint.CANVAS, + target: 'map', + view: new ol.View2D({ + center: new ol.Coordinate(0, 0), + zoom: 0 + }) +}); diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 010364e5e0..0b576701c2 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -155,8 +155,10 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = filter = filters[i]; type = filter.getType(); features = source.getFeatures(filter); - symbolizer = symbolizers[type]; - canvasRenderer.renderFeaturesByGeometryType(type, features, symbolizer); + if (features.length) { + symbolizer = symbolizers[type]; + canvasRenderer.renderFeaturesByGeometryType(type, features, symbolizer); + } } }; From 16865fc1fa3f555a55de93ae5501c115d3826b9b Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 22 Jan 2013 13:24:37 +0100 Subject: [PATCH 035/180] Removing no longer needed Tiled Vector Source. --- src/ol/source/tiledvectorsource.js | 216 ----------------------------- 1 file changed, 216 deletions(-) delete mode 100644 src/ol/source/tiledvectorsource.js diff --git a/src/ol/source/tiledvectorsource.js b/src/ol/source/tiledvectorsource.js deleted file mode 100644 index 6c3d786503..0000000000 --- a/src/ol/source/tiledvectorsource.js +++ /dev/null @@ -1,216 +0,0 @@ -goog.provide('ol.source.TiledVector'); -goog.provide('ol.source.TiledVectorOptions'); - - -goog.require('goog.array'); -goog.require('goog.object'); -goog.require('ol.Projection'); -goog.require('ol.source.TileSource'); -goog.require('ol.tilegrid.TileGrid'); - - -/** - * @typedef {{features: (ol.Collection|undefined), - * extent: (ol.Extent|undefined), - * projection: (ol.Projection|undefined), - * tileGrid: (ol.tilegrid.TileGrid|undefined)}} - */ -ol.source.TiledVectorOptions; - - - -/** - * @constructor - * @extends {ol.Tile} - * @param {ol.TileCoord} tileCoord Tile coordinate. - * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. - * @private - */ -ol.VectorTile_ = function(tileCoord, tileGrid) { - - goog.base(this, tileCoord); - - this.state = ol.TileState.LOADING; - - /** - * @private - * @type {!goog.vec.Mat4.Number} - */ - this.transform_ = goog.vec.Mat4.createNumber(); - - /** - * @private - * @type {Object.} - */ - this.canvasByContext_ = {}; - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.canvas_; - - /** - * @private - * @type {ol.renderer.canvas.Renderer} - * FIXME Use a shared renderer and cut out tiles; keep track of rendered items - */ - this.renderer_ = this.createRenderer_(tileGrid); - -}; -goog.inherits(ol.VectorTile_, ol.Tile); - - -/** - * @private - * @param {ol.tilegrid.TileGrid} tileGrid tileGrid. - * @return {ol.renderer.canvas.Renderer} The renderer for this tile. - */ -ol.VectorTile_.prototype.createRenderer_ = function(tileGrid) { - var tileSize = tileGrid.getTileSize(); - var canvas = /** @type {HTMLCanvasElement} */ - (goog.dom.createElement(goog.dom.TagName.CANVAS)); - canvas.width = tileSize.width; - canvas.height = tileSize.height; - - var transform = this.transform_; - var origin = tileGrid.getOrigin(this.tileCoord.z); - var resolution = tileGrid.getResolution(this.tileCoord.z); - goog.vec.Mat4.makeIdentity(transform); - goog.vec.Mat4.scale(transform, resolution, resolution, 1); - goog.vec.Mat4.translate(transform, - origin.x / resolution, -origin.y / resolution, 0); - - this.canvasByContext_[goog.getUid(canvas.getContext('2d'))] = canvas; - this.canvas_ = canvas; - - return new ol.renderer.canvas.Renderer(canvas, transform); -}; - - -/** - * @param {Array.} geometries Geometries for this tile. - * @param {Array.} symbolizers Symbolizers for the - * geometries, in the same order. - */ -ol.VectorTile_.prototype.setContent = function(geometries, symbolizers) { - this.state = ol.TileState.LOADED; - - var uniqueSymbolizers = []; - goog.array.removeDuplicates(symbolizers, uniqueSymbolizers); - - var geometriesBySymbolizer = goog.array.bucket(geometries, function(e, i) { - var index = goog.array.indexOf(uniqueSymbolizers, symbolizers[i]); - return index === -1 ? undefined : index; - }); - - var buckets, - renderer = this.renderer_, - type = {line: 'line', point: 'point', polygon: 'polygon'}; - function sortByGeometryType(geometry) { - if (geometry instanceof ol.geom.LineString) { - return type['line']; - } else if (geometry instanceof ol.geom.Point) { - return type['point']; - } else if (geometry instanceof ol.geom.Polygon) { - return type['polygon']; - } - } - for (var i = 0, ii = uniqueSymbolizers.length; i < ii; ++i) { - buckets = goog.array.bucket(geometriesBySymbolizer[i], sortByGeometryType); - if (goog.object.containsKey(buckets, type['line'])) { - renderer.renderLineStrings(buckets[type['line']], uniqueSymbolizers[i]); - } - if (goog.object.containsKey(buckets, type['point'])) { - renderer.renderPoints(buckets[type['point']], uniqueSymbolizers[i]); - } - if (goog.object.containsKey(buckets, type['polygon'])) { - renderer.renderPolygons(buckets[type['polygon']], uniqueSymbolizers[i]); - } - } -}; - - -/** - * @inheritDoc - */ -ol.VectorTile_.prototype.getImage = function(opt_context) { - var key = goog.isDef(opt_context) ? goog.getUid(opt_context) : -1; - if (key in this.canvasByContext_) { - return this.canvasByContext_[key]; - } else { - this.canvasByContext_[key] = this.canvas_; - return this.canvas_; - } -}; - - - -/** - * @constructor - * @extends {ol.source.TileSource} - * @param {ol.source.TiledVectorOptions} options options. - */ -ol.source.TiledVector = function(options) { - - /** - * @type {ol.Collection} - * @private - */ - this.features_ = goog.isDef(options.features) ? - options.features : new ol.Collection(); - - /** - * @private - * @type {Object.} - * FIXME will need to expire elements from this cache - * FIXME will need to invalidate tiles when data changes - */ - this.tileCache_ = {}; - - var projection = ol.Projection.createProjection( - options.projection, 'EPSG:3857'); - - goog.base(this, { - extent: goog.isDef(options.extent) ? - options.extent : projection.getExtent(), - projection: projection, - tileGrid: goog.isDef(options.tileGrid) ? - options.tileGrid : ol.tilegrid.createForProjection(projection) - }); - -}; -goog.inherits(ol.source.TiledVector, ol.source.TileSource); - - -/** - * @return {ol.Collection} This layer's features. - */ -ol.source.TiledVector.prototype.getFeatures = function() { - return this.features_; -}; - - -/** - * @inheritDoc - */ -ol.source.TiledVector.prototype.getTile = function(tileCoord) { - var extent = this.tileGrid.getTileCoordExtent(tileCoord); - var key = tileCoord.toString(); - if (goog.object.containsKey(this.tileCache_, key)) { - return this.tileCache_[key]; - } else { - var tile = new ol.VectorTile_(tileCoord, this.tileGrid), - geometries = [], - symbolizers = []; - // FIXME only pass geometries for tile extent - this.features_.forEach(function(feature) { - geometries.push(feature.getGeometry()); - // symbolizers.push(feature.getSymbolizer()); - }); - tile.setContent(geometries, symbolizers); - - this.tileCache_[key] = tile; - return tile; - } -}; From 4e6b864e27afb4b35955d437800f9a3334952286 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 22 Jan 2013 14:09:04 +0100 Subject: [PATCH 036/180] Making default symbolizers compiler safe --- .../canvas/canvasvectorlayerrenderer.js | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 0b576701c2..59711375b6 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -124,27 +124,26 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = canvas, frameState.coordinateToPixelMatrix); // TODO: get these elsewhere - var symbolizers = { - point: new ol.style.LiteralShape({ - type: ol.style.ShapeType.CIRCLE, - size: 10, - fillStyle: '#ffcc99', - strokeStyle: '#ff9933', - strokeWidth: 2, - opacity: 0.75 - }), - linestring: new ol.style.LiteralLine({ - strokeStyle: '#ff9933', - strokeWidth: 2, - opacity: 1 - }), - polygon: new ol.style.LiteralPolygon({ - fillStyle: '#ffcc99', - strokeStyle: '#ff9933', - strokeWidth: 2, - opacity: 0.5 - }) - }; + var symbolizers = {}; + symbolizers[ol.geom.GeometryType.POINT] = new ol.style.LiteralShape({ + type: ol.style.ShapeType.CIRCLE, + size: 10, + fillStyle: '#ffcc99', + strokeStyle: '#ff9933', + strokeWidth: 2, + opacity: 0.75 + }); + symbolizers[ol.geom.GeometryType.LINESTRING] = new ol.style.LiteralLine({ + strokeStyle: '#ff9933', + strokeWidth: 2, + opacity: 1 + }); + symbolizers[ol.geom.GeometryType.POLYGON] = new ol.style.LiteralPolygon({ + fillStyle: '#ffcc99', + strokeStyle: '#ff9933', + strokeWidth: 2, + opacity: 0.5 + }); // render features by geometry type var filters = this.geometryFilters_, From 35f6984e69601215681781ab698b1743deba3713 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 22 Jan 2013 11:21:35 -0700 Subject: [PATCH 037/180] Unused var and some notes --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 59711375b6..41f8fe10b7 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -88,8 +88,6 @@ ol.renderer.canvas.VectorLayer.prototype.getTransform = function() { ol.renderer.canvas.VectorLayer.prototype.renderFrame = function(frameState, layerState) { - var view2DState = frameState.view2DState; - var layer = this.getVectorLayer(); var source = layer.getVectorSource(); var extent = frameState.extent; @@ -123,7 +121,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var canvasRenderer = new ol.renderer.canvas.Renderer( canvas, frameState.coordinateToPixelMatrix); - // TODO: get these elsewhere + // TODO: implement layer.setStyle(style) where style is a set of rules + // and a rule has a filter and array of symbolizers var symbolizers = {}; symbolizers[ol.geom.GeometryType.POINT] = new ol.style.LiteralShape({ type: ol.style.ShapeType.CIRCLE, @@ -155,6 +154,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = type = filter.getType(); features = source.getFeatures(filter); if (features.length) { + // TODO: layer.getSymbolizerLiterals(features) or similar symbolizer = symbolizers[type]; canvasRenderer.renderFeaturesByGeometryType(type, features, symbolizer); } From 07a56bfe3f5728f56cea4de690a39d65133ccde3 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 22 Jan 2013 17:41:22 -0700 Subject: [PATCH 038/180] Parsing GeoJSON FeatureCollection --- src/ol/io/geojson.js | 50 +++++++++++++++++++- test/spec/ol/io/geojson.test.js | 83 +++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 2 deletions(-) diff --git a/src/ol/io/geojson.js b/src/ol/io/geojson.js index 9587a7a5d0..2dd708e641 100644 --- a/src/ol/io/geojson.js +++ b/src/ol/io/geojson.js @@ -1,5 +1,6 @@ goog.provide('ol.io.geojson'); +goog.require('ol.Feature'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.LineString'); goog.require('ol.geom.MultiLineString'); @@ -12,7 +13,8 @@ goog.require('ol.geom.Polygon'); /** * Parse a GeoJSON string. * @param {string} str GeoJSON string. - * @return {ol.geom.Geometry|Array.} Parsed geometry or array + * @return {ol.Feature|Array.| + * ol.geom.Geometry|Array.} Parsed geometry or array * of geometries. */ ol.io.geojson.read = function(str) { @@ -24,13 +26,22 @@ ol.io.geojson.read = function(str) { /** * @param {GeoJSONObject} json GeoJSON object. - * @return {ol.geom.Geometry|Array.} Parsed geometry or array + * @return {ol.Feature|Array.| + * ol.geom.Geometry|Array.} Parsed geometry or array * of geometries. * @private */ ol.io.geojson.parse_ = function(json) { var result; switch (json.type) { + case 'FeatureCollection': + result = ol.io.geojson.parseFeatureCollection_( + /** @type {GeoJSONFeatureCollection} */ (json)); + break; + case 'Feature': + result = ol.io.geojson.parseFeature_( + /** @type {GeoJSONFeature} */ (json)); + break; case 'GeometryCollection': result = ol.io.geojson.parseGeometryCollection_( /** @type {GeoJSONGeometryCollection} */ (json)); @@ -66,6 +77,41 @@ ol.io.geojson.parse_ = function(json) { }; +/** + * @param {GeoJSONFeature} json GeoJSON feature. + * @return {ol.Feature} Parsed feature. + * @private + */ +ol.io.geojson.parseFeature_ = function(json) { + var geomJson = json.geometry, + geometry; + if (geomJson) { + geometry = /** @type {ol.geom.Geometry} */ (ol.io.geojson.parse_( + /** @type {GeoJSONGeometry} */ (geomJson))); + } + return new ol.Feature(geometry, json.properties); +}; + + +/** + * @param {GeoJSONFeatureCollection} json GeoJSON feature collection. + * @return {Array.} Parsed array of features. + * @private + */ +ol.io.geojson.parseFeatureCollection_ = function(json) { + var features = json.features, + len = features.length, + result = new Array(len), + i; + + for (i = 0; i < len; ++i) { + result[i] = ol.io.geojson.parse_( + /** @type {GeoJSONFeature} */ (features[i])); + } + return result; +}; + + /** * @param {GeoJSONGeometryCollection} json GeoJSON geometry collection. * @return {Array.} Parsed array of geometries. diff --git a/test/spec/ol/io/geojson.test.js b/test/spec/ol/io/geojson.test.js index fc2ab65b4b..7ad58d4455 100644 --- a/test/spec/ol/io/geojson.test.js +++ b/test/spec/ol/io/geojson.test.js @@ -1,5 +1,69 @@ describe('ol.io.geojson', function() { + var data = { + 'type': 'FeatureCollection', + 'features': [ + { + 'type': 'Feature', + 'properties': { + 'LINK_ID': 573730499, + 'RP_TYPE': 14, + 'RP_FUNC': 0, + 'DIRECTION': 2, + 'LOGKOD': '', + 'CHANGED': '', + 'USERID': '', + 'ST_NAME': '', + 'L_REFADDR': '', + 'L_NREFADDR': '', + 'R_REFADDR': '', + 'R_NREFADDR': '', + 'SPEED_CAT': '7', + 'ZIPCODE': '59330', + 'SHAPE_LEN': 46.3826 + }, + 'geometry': { + 'type': 'LineString', + 'coordinates': [ + [1549497.66985, 6403707.96], + [1549491.1, 6403710.1], + [1549488.03995, 6403716.7504], + [1549488.5401, 6403724.5504], + [1549494.37985, 6403733.54], + [1549499.6799, 6403738.0504], + [1549506.22, 6403739.2504] + ] + } + }, { + 'type': 'Feature', + 'properties': { + 'LINK_ID': 30760556, + 'RP_TYPE': 12, + 'RP_FUNC': 1, + 'DIRECTION': 0, + 'LOGKOD': '', + 'CHANGED': '', + 'USERID': '', + 'ST_NAME': 'BRUNNSGATAN', + 'L_REFADDR': '24', + 'L_NREFADDR': '16', + 'R_REFADDR': '', + 'R_NREFADDR': '', + 'SPEED_CAT': '7', + 'ZIPCODE': '59330', + 'SHAPE_LEN': 70.3106 + }, + 'geometry': { + 'type': 'LineString', + 'coordinates': [ + [1549754.2769, 6403854.8024], + [1549728.45985, 6403920.2] + ] + } + } + ] + }; + describe('read()', function() { it('parses point', function() { @@ -60,6 +124,25 @@ describe('ol.io.geojson', function() { expect(array[1]).toBeA(ol.geom.LineString); }); + it('parses feature collection', function() { + var str = JSON.stringify(data), + array = ol.io.geojson.read(str); + + expect(array.length).toBe(2); + + var first = array[0]; + expect(first).toBeA(ol.Feature); + expect(first.get('LINK_ID')).toBe(573730499); + var firstGeom = first.getGeometry(); + expect(firstGeom).toBeA(ol.geom.LineString); + + var second = array[1]; + expect(second).toBeA(ol.Feature); + expect(second.get('ST_NAME')).toBe('BRUNNSGATAN'); + var secondGeom = second.getGeometry(); + expect(secondGeom).toBeA(ol.geom.LineString); + }); + }); }); From 4a54cc69ceb549b95ea3c660cbce0a1364d78fbe Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 23 Jan 2013 21:37:06 +0100 Subject: [PATCH 039/180] Tiled vector rendering This needs more work still - see inline TODOs. It also has a major bug - rendered vector features do not scale and do not change their offset during panning. So only the initial view is correct. --- .../canvas/canvasvectorlayerrenderer.js | 109 +++++++++++++++--- src/ol/source/vectorsource.js | 18 ++- 2 files changed, 105 insertions(+), 22 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 41f8fe10b7..dfd3f43629 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -4,6 +4,7 @@ goog.require('goog.vec.Mat4'); goog.require('ol.filter.Geometry'); goog.require('ol.geom.GeometryType'); goog.require('ol.layer.Vector'); +goog.require('ol.tilegrid.TileGrid'); @@ -35,6 +36,18 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { */ this.context_ = null; + /** + * @private + * @type {HTMLCanvasElement} + */ + this.sketchCanvas_ = null; + + /** + * @private + * @type {Object.} + */ + this.tileCache_ = {}; + /** * @private * @type {!goog.vec.Mat4.Number} @@ -91,8 +104,25 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var layer = this.getVectorLayer(); var source = layer.getVectorSource(); var extent = frameState.extent; + var view2DState = frameState.view2DState; + var tileGrid = source.getTileGrid(); - var canvasSize = frameState.size; + if (goog.isNull(tileGrid)) { + // lazy tile source creation to match the view projection + tileGrid = ol.tilegrid.createForProjection( + view2DState.projection, 22); + source.setTileGrid(tileGrid); + } + + var tileSize = tileGrid.getTileSize(); + var z = tileGrid.getZForResolution(view2DState.resolution); + var tileResolution = tileGrid.getResolution(z); + var tileRange = tileGrid.getTileRangeForExtentAndResolution( + frameState.extent, tileResolution); + + var canvasSize = new ol.Size( + tileSize.width * tileRange.getWidth(), + tileSize.height * tileRange.getHeight()); var canvas, context; if (goog.isNull(this.canvas_)) { @@ -104,6 +134,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = this.canvas_ = canvas; this.canvasSize_ = canvasSize; this.context_ = context; + this.sketchCanvas_ = /** @type {HTMLCanvasElement} */ + canvas.cloneNode(false); } else { canvas = this.canvas_; context = this.context_; @@ -113,14 +145,6 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = this.canvasSize_ = canvasSize; } - /** - * For now, we create a canvas renderer and have it render all features with - * every call to renderFrame. - * TODO: only render newly visible/dirty areas - */ - var canvasRenderer = new ol.renderer.canvas.Renderer( - canvas, frameState.coordinateToPixelMatrix); - // TODO: implement layer.setStyle(style) where style is a set of rules // and a rule has a filter and array of symbolizers var symbolizers = {}; @@ -144,19 +168,68 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = opacity: 0.5 }); + var sketchCanvas = this.sketchCanvas_; + // clear the sketch canvas + sketchCanvas.width = canvasSize.width; + sketchCanvas.height = canvasSize.height; + // TODO: Use transform for tile resolution, not frameState resolution + var sketchCanvasRenderer = new ol.renderer.canvas.Renderer( + sketchCanvas, frameState.coordinateToPixelMatrix); + var renderedFeatures = {}; + var tile, tileContext, tileCoord, key, tileExtent, tileState, x, y; // render features by geometry type var filters = this.geometryFilters_, numFilters = filters.length, i, filter, type, features, symbolizer; - - for (i = 0; i < numFilters; ++i) { - filter = filters[i]; - type = filter.getType(); - features = source.getFeatures(filter); - if (features.length) { - // TODO: layer.getSymbolizerLiterals(features) or similar - symbolizer = symbolizers[type]; - canvasRenderer.renderFeaturesByGeometryType(type, features, symbolizer); + for (x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + tileCoord = new ol.TileCoord(z, x, y); + key = tileCoord.toString(); + if (key in this.tileCache_) { + tile = this.tileCache_[key]; + } else { + tileExtent = tileGrid.getTileCoordExtent(tileCoord); + // TODO: instead of filtering here, do this on the source and maintain + // a spatial index + function filterFn(feature) { + var id = goog.getUid(feature); + var include = !(id in renderedFeatures) && + feature.getGeometry().getBounds().intersects(tileExtent); + if (include === true) { + renderedFeatures[id] = true; + } + return include; + } + for (i = 0; i < numFilters; ++i) { + filter = filters[i]; + type = filter.getType(); + features = source.getFeatures(filter); + // TODO: spatial indes of tiles - see filterFn above + features = goog.array.filter(features, filterFn); + if (features.length) { + // TODO: layer.getSymbolizerLiterals(features) or similar + symbolizer = symbolizers[type]; + sketchCanvasRenderer.renderFeaturesByGeometryType( + type, features, symbolizer); + } + } + tile = /** @type {HTMLCanvasElement} */ + goog.dom.createElement(goog.dom.TagName.CANVAS); + tile.width = tileSize.width; + tile.height = tileSize.height; + tileContext = tile.getContext('2d'); + tileContext.drawImage(sketchCanvas, + x * tileSize.width, (tileRange.maxY - y) * tileSize.height, + tileSize.width, tileSize.height, + 0, 0, tileSize.width, tileSize.height); + this.tileCache_[key] = tile; + } + // TODO: transform (scale, offset) + context.drawImage(tile, + 0, 0, + tileSize.width, tileSize.height, + x * tileSize.width, (tileRange.maxY - y) * tileSize.height, + tileSize.width, tileSize.height); } } diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index 3601bf3a6d..69fe06ecc8 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -126,7 +126,8 @@ ol.source.FeatureCache.prototype.getFeaturesByIds_ = function(ids) { /** * @typedef {{attributions: (Array.|undefined), * extent: (ol.Extent|undefined), - * projection: (ol.Projection|undefined)}} + * projection: (ol.Projection|undefined), + * tileGrid: (ol.tilegrid.TileGrid|undefined)}} */ ol.source.VectorOptions; @@ -134,7 +135,7 @@ ol.source.VectorOptions; /** * @constructor - * @extends {ol.source.Source} + * @extends {ol.source.TileSource} * @param {ol.source.VectorOptions} options Source options. */ ol.source.Vector = function(options) { @@ -142,7 +143,8 @@ ol.source.Vector = function(options) { goog.base(this, { attributions: options.attributions, extent: options.extent, - projection: options.projection + projection: options.projection, + tileGrid: options.tileGrid }); /** @@ -152,7 +154,15 @@ ol.source.Vector = function(options) { this.featureCache_ = new ol.source.FeatureCache(); }; -goog.inherits(ol.source.Vector, ol.source.Source); +goog.inherits(ol.source.Vector, ol.source.TileSource); + + +/** + * @param {ol.tilegrid.TileGrid} tileGrid tile grid. + */ +ol.source.Vector.prototype.setTileGrid = function(tileGrid) { + this.tileGrid = tileGrid; +}; /** From 9bd1ab99d16e2e4fb08e58cfc463dae863be1572 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 24 Jan 2013 02:06:40 -0700 Subject: [PATCH 040/180] Toward tiled vector rendering --- .../canvas/canvasvectorlayerrenderer.js | 232 +++++++++++------- 1 file changed, 144 insertions(+), 88 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index dfd3f43629..9087baa589 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -19,41 +19,45 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { goog.base(this, mapRenderer, layer); /** + * Final canvas made available to the map renderer. * @private * @type {HTMLCanvasElement} */ - this.canvas_ = null; - - /** - * @private - * @type {ol.Size} - */ - this.canvasSize_ = null; + this.canvas_ = /** @type {HTMLCanvasElement} */ + (goog.dom.createElement(goog.dom.TagName.CANVAS)); /** * @private * @type {CanvasRenderingContext2D} */ - this.context_ = null; - - /** - * @private - * @type {HTMLCanvasElement} - */ - this.sketchCanvas_ = null; - - /** - * @private - * @type {Object.} - */ - this.tileCache_ = {}; + this.context_ = /** @type {CanvasRenderingContext2D} */ + this.canvas_.getContext('2d'); /** * @private * @type {!goog.vec.Mat4.Number} */ this.transform_ = goog.vec.Mat4.createNumber(); - goog.vec.Mat4.makeIdentity(this.transform_); + + /** + * Interim canvas for drawing newly visible features. + * @private + * @type {HTMLCanvasElement} + */ + this.sketchCanvas_ = /** @type {HTMLCanvasElement} */ + (goog.dom.createElement(goog.dom.TagName.CANVAS)); + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.sketchTransform_ = goog.vec.Mat4.createNumber(); + + /** + * @private + * @type {Object.} + */ + this.tileCache_ = {}; /** * Geometry filters in rendering order. @@ -67,6 +71,50 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { new ol.filter.Geometry(ol.geom.GeometryType.POINT) ]; + /** + * @private + * @type {number} + */ + this.renderedResolution_; + + /** + * @private + * @type {ol.Extent} + */ + this.renderedExtent_; + + /** + * @private + * @type {boolean} + */ + this.layerChanged_ = false; + + + // TODO: implement layer.setStyle(style) where style is a set of rules + // and a rule has a filter and array of symbolizers + var symbolizers = {}; + symbolizers[ol.geom.GeometryType.POINT] = new ol.style.LiteralShape({ + type: ol.style.ShapeType.CIRCLE, + size: 10, + fillStyle: '#ffcc99', + strokeStyle: '#ff9933', + strokeWidth: 2, + opacity: 0.75 + }); + symbolizers[ol.geom.GeometryType.LINESTRING] = new ol.style.LiteralLine({ + strokeStyle: '#ff9933', + strokeWidth: 2, + opacity: 1 + }); + symbolizers[ol.geom.GeometryType.POLYGON] = new ol.style.LiteralPolygon({ + fillStyle: '#ffcc99', + strokeStyle: '#ff9933', + strokeWidth: 2, + opacity: 0.5 + }); + // TODO: remove this + this.symbolizers_ = symbolizers; + }; goog.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); @@ -101,80 +149,77 @@ ol.renderer.canvas.VectorLayer.prototype.getTransform = function() { ol.renderer.canvas.VectorLayer.prototype.renderFrame = function(frameState, layerState) { + var view2DState = frameState.view2DState, + resolution = view2DState.resolution, + extent = frameState.extent; + var layer = this.getVectorLayer(); var source = layer.getVectorSource(); - var extent = frameState.extent; - var view2DState = frameState.view2DState; var tileGrid = source.getTileGrid(); if (goog.isNull(tileGrid)) { // lazy tile source creation to match the view projection tileGrid = ol.tilegrid.createForProjection( - view2DState.projection, 22); + view2DState.projection, /** TODO: get this elsewhere */ 22); source.setTileGrid(tileGrid); } var tileSize = tileGrid.getTileSize(); - var z = tileGrid.getZForResolution(view2DState.resolution); + var z = tileGrid.getZForResolution(resolution); var tileResolution = tileGrid.getResolution(z); var tileRange = tileGrid.getTileRangeForExtentAndResolution( - frameState.extent, tileResolution); + extent, tileResolution); + var tileExtent = tileGrid.getTileRangeExtent(z, tileRange); - var canvasSize = new ol.Size( + // bail out if nothing to do + // TODO: try to make it less work to determine that nothing changed + if (!this.layerChanged_ && this.renderedResolution_ == tileResolution && + // TODO: extent.equals(other) + this.renderedExtent_.toString() == tileExtent.toString()) { + return; + } + + // clear tiles at alt-z + if (this.renderedResolution_ != tileResolution) { + this.tileCache_ = {}; + } + + /** + * Prepare the sketch canvas. This covers the currently visible tile range + * and will have rendered all newly visible features. + */ + var sketchCanvas = this.sketchCanvas_; + var sketchSize = new ol.Size( tileSize.width * tileRange.getWidth(), tileSize.height * tileRange.getHeight()); - var canvas, context; - if (goog.isNull(this.canvas_)) { - canvas = /** @type {HTMLCanvasElement} */ - (goog.dom.createElement(goog.dom.TagName.CANVAS)); - canvas.width = canvasSize.width; - canvas.height = canvasSize.height; - context = /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')); - this.canvas_ = canvas; - this.canvasSize_ = canvasSize; - this.context_ = context; - this.sketchCanvas_ = /** @type {HTMLCanvasElement} */ - canvas.cloneNode(false); - } else { - canvas = this.canvas_; - context = this.context_; - // force clear the canvas - canvas.width = canvasSize.width; - canvas.height = canvasSize.height; - this.canvasSize_ = canvasSize; - } + sketchCanvas.width = sketchSize.width; + sketchCanvas.height = sketchSize.height; - // TODO: implement layer.setStyle(style) where style is a set of rules - // and a rule has a filter and array of symbolizers - var symbolizers = {}; - symbolizers[ol.geom.GeometryType.POINT] = new ol.style.LiteralShape({ - type: ol.style.ShapeType.CIRCLE, - size: 10, - fillStyle: '#ffcc99', - strokeStyle: '#ff9933', - strokeWidth: 2, - opacity: 0.75 - }); - symbolizers[ol.geom.GeometryType.LINESTRING] = new ol.style.LiteralLine({ - strokeStyle: '#ff9933', - strokeWidth: 2, - opacity: 1 - }); - symbolizers[ol.geom.GeometryType.POLYGON] = new ol.style.LiteralPolygon({ - fillStyle: '#ffcc99', - strokeStyle: '#ff9933', - strokeWidth: 2, - opacity: 0.5 - }); + // clear/resize final canvas + var finalCanvas = this.canvas_; + finalCanvas.width = sketchSize.width; + finalCanvas.height = sketchSize.height; + var finalContext = this.context_; + + var sketchTransform = this.sketchTransform_; + goog.vec.Mat4.makeIdentity(sketchTransform); + goog.vec.Mat4.translate(sketchTransform, + sketchSize.width / 2, sketchSize.height / 2, 0); + goog.vec.Mat4.rotateZ(sketchTransform, view2DState.rotation); + goog.vec.Mat4.scale( + sketchTransform, + 1 / tileResolution, + -1 / tileResolution, + 1); + goog.vec.Mat4.translate( + sketchTransform, + -view2DState.center.x, + -view2DState.center.y, + 0); - var sketchCanvas = this.sketchCanvas_; - // clear the sketch canvas - sketchCanvas.width = canvasSize.width; - sketchCanvas.height = canvasSize.height; - // TODO: Use transform for tile resolution, not frameState resolution var sketchCanvasRenderer = new ol.renderer.canvas.Renderer( - sketchCanvas, frameState.coordinateToPixelMatrix); + sketchCanvas, sketchTransform); var renderedFeatures = {}; var tile, tileContext, tileCoord, key, tileExtent, tileState, x, y; // render features by geometry type @@ -185,9 +230,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = for (y = tileRange.minY; y <= tileRange.maxY; ++y) { tileCoord = new ol.TileCoord(z, x, y); key = tileCoord.toString(); - if (key in this.tileCache_) { - tile = this.tileCache_[key]; - } else { + tile = this.tileCache_[key]; + if (tile === undefined) { tileExtent = tileGrid.getTileCoordExtent(tileCoord); // TODO: instead of filtering here, do this on the source and maintain // a spatial index @@ -208,7 +252,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = features = goog.array.filter(features, filterFn); if (features.length) { // TODO: layer.getSymbolizerLiterals(features) or similar - symbolizer = symbolizers[type]; + symbolizer = this.symbolizers_[type]; sketchCanvasRenderer.renderFeaturesByGeometryType( type, features, symbolizer); } @@ -218,19 +262,31 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = tile.width = tileSize.width; tile.height = tileSize.height; tileContext = tile.getContext('2d'); + tileContext.drawImage(sketchCanvas, - x * tileSize.width, (tileRange.maxY - y) * tileSize.height, - tileSize.width, tileSize.height, - 0, 0, tileSize.width, tileSize.height); + -x * tileSize.width, -(tileRange.maxY - y) * tileSize.height); this.tileCache_[key] = tile; } - // TODO: transform (scale, offset) - context.drawImage(tile, - 0, 0, - tileSize.width, tileSize.height, - x * tileSize.width, (tileRange.maxY - y) * tileSize.height, - tileSize.width, tileSize.height); + finalContext.drawImage(tile, + x * tileSize.width, (tileRange.maxY - y) * tileSize.height); } } + var sketchOrigin = tileExtent.getTopLeft(); + var frameOrigin = extent.getTopLeft(); + + var dx = (sketchOrigin.x - frameOrigin.x) / resolution; + var dy = (frameOrigin.y - sketchOrigin.y) / resolution; + + var transform = this.transform_; + goog.vec.Mat4.makeIdentity(transform); + goog.vec.Mat4.translate(transform, dx, dy, 0); + goog.vec.Mat4.scale( + transform, + tileResolution / resolution, + tileResolution / resolution, + 1); + + this.renderedResolution_ = tileResolution; + this.renderedExtent_ = tileExtent; }; From fe605ceebbae166d31005b0d142bbcf002e22124 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 24 Jan 2013 16:21:30 +0100 Subject: [PATCH 041/180] Better, but still not all the way there --- .../canvas/canvasvectorlayerrenderer.js | 63 ++++++++----------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 9087baa589..354f430052 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -77,12 +77,6 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { */ this.renderedResolution_; - /** - * @private - * @type {ol.Extent} - */ - this.renderedExtent_; - /** * @private * @type {boolean} @@ -169,15 +163,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var tileResolution = tileGrid.getResolution(z); var tileRange = tileGrid.getTileRangeForExtentAndResolution( extent, tileResolution); - var tileExtent = tileGrid.getTileRangeExtent(z, tileRange); - - // bail out if nothing to do - // TODO: try to make it less work to determine that nothing changed - if (!this.layerChanged_ && this.renderedResolution_ == tileResolution && - // TODO: extent.equals(other) - this.renderedExtent_.toString() == tileExtent.toString()) { - return; - } + var tileRangeExtent = tileGrid.getTileRangeExtent(z, tileRange); // clear tiles at alt-z if (this.renderedResolution_ != tileResolution) { @@ -202,11 +188,26 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = finalCanvas.height = sketchSize.height; var finalContext = this.context_; + var sketchOrigin = tileRangeExtent.getTopLeft(); + var frameOrigin = extent.getTopLeft(); + var transform = this.transform_; + goog.vec.Mat4.makeIdentity(transform); + goog.vec.Mat4.translate(transform, + frameState.size.width / 2, frameState.size.height / 2, 0); + goog.vec.Mat4.rotateZ(transform, view2DState.rotation); + goog.vec.Mat4.scale( + transform, + tileResolution / view2DState.resolution, + tileResolution / view2DState.resolution, + 1); + goog.vec.Mat4.translate( + transform, + (frameOrigin.x - view2DState.center.x) / tileResolution, + (view2DState.center.y - frameOrigin.y) / tileResolution, + 0); + var sketchTransform = this.sketchTransform_; goog.vec.Mat4.makeIdentity(sketchTransform); - goog.vec.Mat4.translate(sketchTransform, - sketchSize.width / 2, sketchSize.height / 2, 0); - goog.vec.Mat4.rotateZ(sketchTransform, view2DState.rotation); goog.vec.Mat4.scale( sketchTransform, 1 / tileResolution, @@ -214,8 +215,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = 1); goog.vec.Mat4.translate( sketchTransform, - -view2DState.center.x, - -view2DState.center.y, + -sketchOrigin.x, + -sketchOrigin.y, 0); var sketchCanvasRenderer = new ol.renderer.canvas.Renderer( @@ -231,8 +232,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = tileCoord = new ol.TileCoord(z, x, y); key = tileCoord.toString(); tile = this.tileCache_[key]; + tileExtent = tileGrid.getTileCoordExtent(tileCoord); if (tile === undefined) { - tileExtent = tileGrid.getTileCoordExtent(tileCoord); // TODO: instead of filtering here, do this on the source and maintain // a spatial index function filterFn(feature) { @@ -248,7 +249,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = filter = filters[i]; type = filter.getType(); features = source.getFeatures(filter); - // TODO: spatial indes of tiles - see filterFn above + // TODO: spatial index of tiles - see filterFn above features = goog.array.filter(features, filterFn); if (features.length) { // TODO: layer.getSymbolizerLiterals(features) or similar @@ -268,25 +269,11 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = this.tileCache_[key] = tile; } finalContext.drawImage(tile, - x * tileSize.width, (tileRange.maxY - y) * tileSize.height); + (tileExtent.minX - frameOrigin.x) / tileResolution, + (frameOrigin.y - tileExtent.maxY) / tileResolution); } } - var sketchOrigin = tileExtent.getTopLeft(); - var frameOrigin = extent.getTopLeft(); - - var dx = (sketchOrigin.x - frameOrigin.x) / resolution; - var dy = (frameOrigin.y - sketchOrigin.y) / resolution; - - var transform = this.transform_; - goog.vec.Mat4.makeIdentity(transform); - goog.vec.Mat4.translate(transform, dx, dy, 0); - goog.vec.Mat4.scale( - transform, - tileResolution / resolution, - tileResolution / resolution, - 1); this.renderedResolution_ = tileResolution; - this.renderedExtent_ = tileExtent; }; From b0a7badc8f56ea57b09abc2b88cec1c1aee58db5 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 24 Jan 2013 17:29:21 +0100 Subject: [PATCH 042/180] Started on proper date line wrapping What we want in the end is vector tiles repeated just as raster tiles. This change only avoids repeated tiles with the same content being rendered and stored in the cache. --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 354f430052..d6bdabd8f8 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -222,6 +222,9 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var sketchCanvasRenderer = new ol.renderer.canvas.Renderer( sketchCanvas, sketchTransform); var renderedFeatures = {}; + // TODO: wrap date line properly + var numCols = Math.ceil((tileRangeExtent.maxX - tileRangeExtent.minX) / + tileSize.width / tileResolution); var tile, tileContext, tileCoord, key, tileExtent, tileState, x, y; // render features by geometry type var filters = this.geometryFilters_, @@ -229,7 +232,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = i, filter, type, features, symbolizer; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - tileCoord = new ol.TileCoord(z, x, y); + tileCoord = new ol.TileCoord(z, goog.math.modulo(x, numCols), y); key = tileCoord.toString(); tile = this.tileCache_[key]; tileExtent = tileGrid.getTileCoordExtent(tileCoord); From 8d5a45d827cef49e61fb68a9299476d6ae3c8c23 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 24 Jan 2013 16:51:10 -0700 Subject: [PATCH 043/180] Set up example for debugging --- examples/vector-layer.html | 12 +++++++----- examples/vector-layer.js | 4 ++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/examples/vector-layer.html b/examples/vector-layer.html index fa79b0bcd5..29df7ca1f2 100644 --- a/examples/vector-layer.html +++ b/examples/vector-layer.html @@ -5,11 +5,12 @@ @@ -17,6 +18,7 @@
+
diff --git a/examples/vector-layer.js b/examples/vector-layer.js index 3060620341..9052a353b1 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -37,3 +37,7 @@ var map = new ol.Map({ zoom: 0 }) }); + +// TODO: remove me +document.getElementById('canvas').appendChild( + map.renderer_.getLayerRenderer(vector).canvas_); From 033daa425b85c6ce94ddb1fbe2bdc23fb8b0aa63 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 24 Jan 2013 17:15:32 -0700 Subject: [PATCH 044/180] Reverting b0a7badc8f56ea57b09abc2b88cec1c1aee58db5 I think it's complicating things at this point to deal with this. Unfortunately, it's not proper dateline wrapping as is (only arbitrary tile range extent wrapping). --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index d6bdabd8f8..354f430052 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -222,9 +222,6 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var sketchCanvasRenderer = new ol.renderer.canvas.Renderer( sketchCanvas, sketchTransform); var renderedFeatures = {}; - // TODO: wrap date line properly - var numCols = Math.ceil((tileRangeExtent.maxX - tileRangeExtent.minX) / - tileSize.width / tileResolution); var tile, tileContext, tileCoord, key, tileExtent, tileState, x, y; // render features by geometry type var filters = this.geometryFilters_, @@ -232,7 +229,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = i, filter, type, features, symbolizer; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - tileCoord = new ol.TileCoord(z, goog.math.modulo(x, numCols), y); + tileCoord = new ol.TileCoord(z, x, y); key = tileCoord.toString(); tile = this.tileCache_[key]; tileExtent = tileGrid.getTileCoordExtent(tileCoord); From f2b325b430fb01490ad96d090a347e742b0619f3 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 24 Jan 2013 17:16:54 -0700 Subject: [PATCH 045/180] Adding convenient tile labels for debugging This will be removed when behavior is right. --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 354f430052..fa6ad6ddd5 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -264,6 +264,19 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = tile.height = tileSize.height; tileContext = tile.getContext('2d'); + // TODO: remove me + if (goog.DEBUG) { + tileContext.strokeStyle = '#999999'; + tileContext.fillStyle = '#999999'; + tileContext.textAlign = 'center'; + tileContext.textBaseline = 'middle'; + tileContext.font = '24px sans-serif'; + tileContext.strokeRect(0.5, 0.5, tileSize.width - 1, + tileSize.height - 1); + tileContext.fillText(tileCoord.toString(), tileSize.width / 2, + tileSize.height / 2); + } + tileContext.drawImage(sketchCanvas, -x * tileSize.width, -(tileRange.maxY - y) * tileSize.height); this.tileCache_[key] = tile; From 4e3f984796799a8ba70f31522dbae3ae05e22a9b Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 24 Jan 2013 21:57:12 -0700 Subject: [PATCH 046/180] Properly rendering features to canvas tiles It looks like this approach will work well for panning (as anticipated). For animated zooming, it is not going to work as is. It looks like the canvas tile generation is too much for this type of animation loop. Though there are clearly still areas for optimization: * Don't create new tiles while animating between zoom levels. Using existing tiles only while animating should bring a significant performance gain. * Simple spatial index for tiles - each tile coord in the matrix could have a feature lookup object (keyed by id). This needs to account for rendered dimension (as witnessed by the point being cut by a tile). Given that the current example uses only three features, adding the spatial index should only be a minor improvement. * Reuse a fixed set of canvas tiles that are generated at construction (and increased/decreased with view size changes). * If a fixed set of tiles is not used, at least new ones could be cloned from existing ones (minor). * Do some profiling to look for more ideas. In addition, world-wrapping needs addressed. I don't think this renderer is the right (or at least the only) place to address this. And the cache of tiles needs to be managed for real. But hey, at least we've got a working tiled vector renderer now. --- examples/vector-layer.html | 41 ++++-- examples/vector-layer.js | 4 - .../canvas/canvasvectorlayerrenderer.js | 138 ++++++++++-------- 3 files changed, 109 insertions(+), 74 deletions(-) diff --git a/examples/vector-layer.html b/examples/vector-layer.html index 29df7ca1f2..49da90dba9 100644 --- a/examples/vector-layer.html +++ b/examples/vector-layer.html @@ -2,23 +2,46 @@ + - - ol3 vector demo + Full-screen example -
-
+
+
+

Vector rendering example

+
Uses a canvas renderer for drawing vector features.
+
+

See the + vector-layer.js source + to see how this is done.

+
+
+
+
vector, feature, canvas
diff --git a/examples/vector-layer.js b/examples/vector-layer.js index 9052a353b1..3060620341 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -37,7 +37,3 @@ var map = new ol.Map({ zoom: 0 }) }); - -// TODO: remove me -document.getElementById('canvas').appendChild( - map.renderer_.getLayerRenderer(vector).canvas_); diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index fa6ad6ddd5..45dacc85cf 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -78,10 +78,20 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { this.renderedResolution_; /** + * @private + * @type {ol.Extent} + */ + this.renderedExtent_ = null; + + /** + * Flag to be set internally when we know something has changed that suggests + * we need to re-render. + * TODO: discuss setting this for all layers when something changes before + * calling map.render(). * @private * @type {boolean} */ - this.layerChanged_ = false; + this.dirty_ = false; // TODO: implement layer.setStyle(style) where style is a set of rules @@ -143,13 +153,14 @@ ol.renderer.canvas.VectorLayer.prototype.getTransform = function() { ol.renderer.canvas.VectorLayer.prototype.renderFrame = function(frameState, layerState) { + // TODO: consider bailing out here if rendered center and resolution + // have not changed. Requires that other change listeners set a dirty flag. + var view2DState = frameState.view2DState, resolution = view2DState.resolution, - extent = frameState.extent; - - var layer = this.getVectorLayer(); - var source = layer.getVectorSource(); - var tileGrid = source.getTileGrid(); + extent = frameState.extent, + source = this.getVectorLayer().getVectorSource(), + tileGrid = source.getTileGrid(); if (goog.isNull(tileGrid)) { // lazy tile source creation to match the view projection @@ -158,12 +169,40 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = source.setTileGrid(tileGrid); } - var tileSize = tileGrid.getTileSize(); - var z = tileGrid.getZForResolution(resolution); - var tileResolution = tileGrid.getResolution(z); - var tileRange = tileGrid.getTileRangeForExtentAndResolution( - extent, tileResolution); - var tileRangeExtent = tileGrid.getTileRangeExtent(z, tileRange); + // set up transform for the layer canvas to be drawn to the map canvas + var tileSize = tileGrid.getTileSize(), + z = tileGrid.getZForResolution(resolution), + tileResolution = tileGrid.getResolution(z), + tileRange = tileGrid.getTileRangeForExtentAndResolution( + extent, tileResolution), + tileRangeExtent = tileGrid.getTileRangeExtent(z, tileRange), + sketchOrigin = tileRangeExtent.getTopLeft(), + transform = this.transform_; + + goog.vec.Mat4.makeIdentity(transform); + goog.vec.Mat4.translate(transform, + frameState.size.width / 2, + frameState.size.height / 2, + 0); + goog.vec.Mat4.scale(transform, + tileResolution / resolution, tileResolution / resolution, 1); + goog.vec.Mat4.rotateZ(transform, view2DState.rotation); + goog.vec.Mat4.translate(transform, + (sketchOrigin.x - view2DState.center.x) / tileResolution, + (view2DState.center.y - sketchOrigin.y) / tileResolution, + 0); + + /** + * Fastest path out of here. This method is called many many times while + * there is nothing to do (e.g. while waiting for tiles from every other + * layer to load.) Do not put anything above here that is more expensive than + * necessary. And look for ways to get here faster. + */ + if (!this.dirty_ && this.renderedResolution_ === tileResolution && + // TODO: extent.equals() + this.renderedResolution_.toString() === tileRangeExtent.toString()) { + return; + } // clear tiles at alt-z if (this.renderedResolution_ != tileResolution) { @@ -179,48 +218,37 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = tileSize.width * tileRange.getWidth(), tileSize.height * tileRange.getHeight()); + // transform for map coords to sketch canvas pixel coords + var sketchTransform = this.sketchTransform_; + var halfWidth = sketchSize.width / 2; + var halfHeight = sketchSize.height / 2; + goog.vec.Mat4.makeIdentity(sketchTransform); + goog.vec.Mat4.translate(sketchTransform, + halfWidth, + halfHeight, + 0); + goog.vec.Mat4.scale(sketchTransform, + 1 / tileResolution, + -1 / tileResolution, + 1); + goog.vec.Mat4.translate(sketchTransform, + -(sketchOrigin.x + halfWidth * tileResolution), + -(sketchOrigin.y - halfHeight * tileResolution), + 0); + + // clear/resize sketch canvas sketchCanvas.width = sketchSize.width; sketchCanvas.height = sketchSize.height; + var sketchCanvasRenderer = new ol.renderer.canvas.Renderer( + sketchCanvas, sketchTransform); + // clear/resize final canvas var finalCanvas = this.canvas_; finalCanvas.width = sketchSize.width; finalCanvas.height = sketchSize.height; var finalContext = this.context_; - var sketchOrigin = tileRangeExtent.getTopLeft(); - var frameOrigin = extent.getTopLeft(); - var transform = this.transform_; - goog.vec.Mat4.makeIdentity(transform); - goog.vec.Mat4.translate(transform, - frameState.size.width / 2, frameState.size.height / 2, 0); - goog.vec.Mat4.rotateZ(transform, view2DState.rotation); - goog.vec.Mat4.scale( - transform, - tileResolution / view2DState.resolution, - tileResolution / view2DState.resolution, - 1); - goog.vec.Mat4.translate( - transform, - (frameOrigin.x - view2DState.center.x) / tileResolution, - (view2DState.center.y - frameOrigin.y) / tileResolution, - 0); - - var sketchTransform = this.sketchTransform_; - goog.vec.Mat4.makeIdentity(sketchTransform); - goog.vec.Mat4.scale( - sketchTransform, - 1 / tileResolution, - -1 / tileResolution, - 1); - goog.vec.Mat4.translate( - sketchTransform, - -sketchOrigin.x, - -sketchOrigin.y, - 0); - - var sketchCanvasRenderer = new ol.renderer.canvas.Renderer( - sketchCanvas, sketchTransform); var renderedFeatures = {}; var tile, tileContext, tileCoord, key, tileExtent, tileState, x, y; // render features by geometry type @@ -264,26 +292,14 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = tile.height = tileSize.height; tileContext = tile.getContext('2d'); - // TODO: remove me - if (goog.DEBUG) { - tileContext.strokeStyle = '#999999'; - tileContext.fillStyle = '#999999'; - tileContext.textAlign = 'center'; - tileContext.textBaseline = 'middle'; - tileContext.font = '24px sans-serif'; - tileContext.strokeRect(0.5, 0.5, tileSize.width - 1, - tileSize.height - 1); - tileContext.fillText(tileCoord.toString(), tileSize.width / 2, - tileSize.height / 2); - } - tileContext.drawImage(sketchCanvas, - -x * tileSize.width, -(tileRange.maxY - y) * tileSize.height); + (tileRange.minX - x) * tileSize.width, + (y - tileRange.maxY) * tileSize.height); this.tileCache_[key] = tile; } finalContext.drawImage(tile, - (tileExtent.minX - frameOrigin.x) / tileResolution, - (frameOrigin.y - tileExtent.maxY) / tileResolution); + tileSize.width * (x - tileRange.minX), + tileSize.height * (tileRange.maxY - y)); } } From 65b3bb70a15332f2a4f92db4cc49ec03946c44a9 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 25 Jan 2013 16:46:12 +0100 Subject: [PATCH 047/180] Some optimizations * Tiles are now cut out of the sketch renderer in a separate pass. This ensures that point features at tile borders appear at both sides of the border. However, if such features get added in a later tileRange rendering pass, tiles from a previous rendering pass will still not have that feature. * The tile canvas is only created once, and cloneNode(false) is used to get a canvas for a new tile. --- .../canvas/canvasvectorlayerrenderer.js | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 45dacc85cf..94058f5fa2 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -59,6 +59,12 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { */ this.tileCache_ = {}; + /** + * @private + * @type {HTMLCanvasElement} + */ + this.tileArchetype_ = null; + /** * Geometry filters in rendering order. * @private @@ -209,6 +215,13 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = this.tileCache_ = {}; } + if (goog.isNull(this.tileArchetype_)) { + this.tileArchetype_ = /** @type {HTMLCanvasElement} */ + goog.dom.createElement(goog.dom.TagName.CANVAS); + this.tileArchetype_.width = tileSize.width; + this.tileArchetype_.height = tileSize.height; + } + /** * Prepare the sketch canvas. This covers the currently visible tile range * and will have rendered all newly visible features. @@ -250,7 +263,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var finalContext = this.context_; var renderedFeatures = {}; - var tile, tileContext, tileCoord, key, tileExtent, tileState, x, y; + var tilesToRender = {}; + var tile, tileCoord, key, tileExtent, tileState, x, y; // render features by geometry type var filters = this.geometryFilters_, numFilters = filters.length, @@ -260,8 +274,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = tileCoord = new ol.TileCoord(z, x, y); key = tileCoord.toString(); tile = this.tileCache_[key]; - tileExtent = tileGrid.getTileCoordExtent(tileCoord); if (tile === undefined) { + tileExtent = tileGrid.getTileCoordExtent(tileCoord); // TODO: instead of filtering here, do this on the source and maintain // a spatial index function filterFn(feature) { @@ -286,23 +300,27 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = type, features, symbolizer); } } - tile = /** @type {HTMLCanvasElement} */ - goog.dom.createElement(goog.dom.TagName.CANVAS); - tile.width = tileSize.width; - tile.height = tileSize.height; - tileContext = tile.getContext('2d'); - - tileContext.drawImage(sketchCanvas, - (tileRange.minX - x) * tileSize.width, - (y - tileRange.maxY) * tileSize.height); - this.tileCache_[key] = tile; } - finalContext.drawImage(tile, - tileSize.width * (x - tileRange.minX), - tileSize.height * (tileRange.maxY - y)); + tilesToRender[key] = tileCoord; } } - + + for (key in tilesToRender) { + tile = this.tileCache_[key]; + tileCoord = tilesToRender[key]; + if (tile === undefined) { + tile = /** @type {HTMLCanvasElement} */ + this.tileArchetype_.cloneNode(false); + tile.getContext('2d').drawImage(sketchCanvas, + (tileRange.minX - tileCoord.x) * tileSize.width, + (tileCoord.y - tileRange.maxY) * tileSize.height); + this.tileCache_[key] = tile; + } + finalContext.drawImage(tile, + tileSize.width * (tileCoord.x - tileRange.minX), + tileSize.height * (tileRange.maxY - tileCoord.y)); + } + this.renderedResolution_ = tileResolution; }; From e61c26830e3436d5e002547577c15e318625c7bb Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 30 Jan 2013 17:09:43 +0100 Subject: [PATCH 048/180] Manage tile cache This change implements a LRU policy for tile cachint, and keeps 128 tiles in the cache by default. --- .../canvas/canvasvectorlayerrenderer.js | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 94058f5fa2..f772ff80bb 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -59,6 +59,12 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { */ this.tileCache_ = {}; + /** + * @private + * @type {Array.} + */ + this.tileCacheIndex_ = []; + /** * @private * @type {HTMLCanvasElement} @@ -210,11 +216,6 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = return; } - // clear tiles at alt-z - if (this.renderedResolution_ != tileResolution) { - this.tileCache_ = {}; - } - if (goog.isNull(this.tileArchetype_)) { this.tileArchetype_ = /** @type {HTMLCanvasElement} */ goog.dom.createElement(goog.dom.TagName.CANVAS); @@ -304,10 +305,13 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = tilesToRender[key] = tileCoord; } } - + for (key in tilesToRender) { tile = this.tileCache_[key]; tileCoord = tilesToRender[key]; + // LRU - move tile key to the beginning of the index + goog.array.remove(this.tileCacheIndex_, key); + this.tileCacheIndex_.unshift(key); if (tile === undefined) { tile = /** @type {HTMLCanvasElement} */ this.tileArchetype_.cloneNode(false); @@ -315,12 +319,23 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = (tileRange.minX - tileCoord.x) * tileSize.width, (tileCoord.y - tileRange.maxY) * tileSize.height); this.tileCache_[key] = tile; + // manage cache + if (this.tileCacheIndex_.length >= + ol.renderer.canvas.VectorLayer.TILECACHE_SIZE) { + delete this.tileCache_[this.tileCacheIndex_.pop()]; + } } finalContext.drawImage(tile, tileSize.width * (tileCoord.x - tileRange.minX), tileSize.height * (tileRange.maxY - tileCoord.y)); } - + this.renderedResolution_ = tileResolution; }; + + +/** + * @type {number} + */ +ol.renderer.canvas.VectorLayer.TILECACHE_SIZE = 128; From 9a0f509a946d3dc733db7b8702538d6fdf14e0f6 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 30 Jan 2013 18:03:27 +0100 Subject: [PATCH 049/180] Use objects instead of arrays for geometry type cache Also trying to reference features directly here instead of storing feature ids in the index. We'll see if this is good when we have an index for tile coords as well and want to filter with multiple filters, accessing multiple indices. --- src/ol/source/vectorsource.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index 69fe06ecc8..c7ea186c66 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -21,7 +21,7 @@ ol.source.FeatureCache = function() { /** - * @type {Object.>} + * @type {Object.} * @private */ this.geometryTypeIndex_; @@ -37,11 +37,11 @@ ol.source.FeatureCache = function() { ol.source.FeatureCache.prototype.clear = function() { this.idLookup_ = {}; - var geometryTypeIndex_ = {}; + var geometryTypeIndex = {}; for (var key in ol.geom.GeometryType) { - geometryTypeIndex_[ol.geom.GeometryType[key]] = []; + geometryTypeIndex[ol.geom.GeometryType[key]] = {}; } - this.geometryTypeIndex_ = geometryTypeIndex_; + this.geometryTypeIndex_ = geometryTypeIndex; }; @@ -57,7 +57,7 @@ ol.source.FeatureCache.prototype.add = function(feature) { // index by geometry type if (!goog.isNull(geometry)) { - this.geometryTypeIndex_[geometry.getType()].push(id); + this.geometryTypeIndex_[geometry.getType()][id] = feature; } /** @@ -99,10 +99,7 @@ ol.source.FeatureCache.prototype.getFeatures = function(opt_filter) { * @private */ ol.source.FeatureCache.prototype.getFeaturesByGeometryType_ = function(filter) { - var type = filter.getType(), - ids = this.geometryTypeIndex_[filter.getType()]; - - return this.getFeaturesByIds_(ids); + return goog.object.getValues(this.geometryTypeIndex_[filter.getType()]); }; From 43dd9ca68c6d2c1daa5cd8344194a38fef2cb9e8 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sat, 2 Feb 2013 12:29:08 +0100 Subject: [PATCH 050/180] Don't use unshift --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index f772ff80bb..9720ddd501 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -309,9 +309,9 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = for (key in tilesToRender) { tile = this.tileCache_[key]; tileCoord = tilesToRender[key]; - // LRU - move tile key to the beginning of the index + // LRU - move tile key to the end of the index goog.array.remove(this.tileCacheIndex_, key); - this.tileCacheIndex_.unshift(key); + this.tileCacheIndex_.push(key); if (tile === undefined) { tile = /** @type {HTMLCanvasElement} */ this.tileArchetype_.cloneNode(false); @@ -322,7 +322,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = // manage cache if (this.tileCacheIndex_.length >= ol.renderer.canvas.VectorLayer.TILECACHE_SIZE) { - delete this.tileCache_[this.tileCacheIndex_.pop()]; + delete this.tileCache_[this.tileCacheIndex_.shift()]; } } finalContext.drawImage(tile, From 428793cd7ccc023897e799e2727cb0d7de1b5d0c Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 4 Feb 2013 22:50:21 +0100 Subject: [PATCH 051/180] New filters for extent and logical filtering These need unit tests. --- src/ol/filter/extentfilter.js | 36 ++++++++++++++++++++ src/ol/filter/logicalfilter.js | 62 ++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 src/ol/filter/extentfilter.js create mode 100644 src/ol/filter/logicalfilter.js diff --git a/src/ol/filter/extentfilter.js b/src/ol/filter/extentfilter.js new file mode 100644 index 0000000000..3a566c2490 --- /dev/null +++ b/src/ol/filter/extentfilter.js @@ -0,0 +1,36 @@ +goog.provide('ol.filter.Extent'); + +goog.require('ol.Feature'); + + + +/** + * @constructor + * @implements {ol.filter.Filter} + * @param {ol.Extent} extent The extent. + */ +ol.filter.Extent = function(extent) { + + /** + * @type {ol.Extent} + * @private + */ + this.extent_ = extent; + +}; + + +/** + * @return {ol.Extent} The filter extent. + */ +ol.filter.Extent.prototype.getExtent = function() { + return this.extent_; +}; + + +/** + * @inheritDoc + */ +ol.filter.Extent.prototype.evaluate = function(feature) { + return feature.getGeometry().getBounds().intersects(this.extent_); +}; diff --git a/src/ol/filter/logicalfilter.js b/src/ol/filter/logicalfilter.js new file mode 100644 index 0000000000..b702958684 --- /dev/null +++ b/src/ol/filter/logicalfilter.js @@ -0,0 +1,62 @@ +goog.provide('ol.filter.Logical'); +goog.provide('ol.filter.LogicalOperator'); + +goog.require('ol.Feature'); +goog.require('ol.filter.Filter'); + + + +/** + * @constructor + * @implements {ol.filter.Filter} + * @param {Array.} filters Filters to and-combine. + * @param {!ol.filter.LogicalOperator} operator Operator. + */ +ol.filter.Logical = function(filters, operator) { + + /** + * @type {Array.} + * @private + */ + this.filters_ = filters; + + /** + * @type {!ol.filter.LogicalOperator} + */ + this.operator = operator; + +}; + + +/** + * @inheritDoc + */ +ol.filter.Logical.prototype.evaluate = function(feature) { + var filters = this.filters_, + i = 0, ii = filters.length, + operator = this.operator, + start = operator(true, false), + result = start; + while (result === start && i < ii) { + result = operator(result, filters[i].evaluate(feature)); + ++i; + } + return result; +}; + + +/** + * @return {Array.} The filter's filters. + */ +ol.filter.Logical.prototype.getFilters = function() { + return this.filters_; +}; + + +/** + * @enum {!Function} + */ +ol.filter.LogicalOperator = { + AND: /** @return {boolean} result. */ function(a, b) { return a && b; }, + OR: /** @return {boolean} result. */ function(a, b) { return a || b; } +}; From c58f0afcc30cd00d2e9622bd9d16443408a8fc50 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 4 Feb 2013 22:50:59 +0100 Subject: [PATCH 052/180] New R-Tree struct for spatial indexing. This needs unit tests. --- src/ol/structs/rtree.js | 193 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 src/ol/structs/rtree.js diff --git a/src/ol/structs/rtree.js b/src/ol/structs/rtree.js new file mode 100644 index 0000000000..2ea19b91e7 --- /dev/null +++ b/src/ol/structs/rtree.js @@ -0,0 +1,193 @@ +goog.provide('ol.structs.RTree'); + + + +/** + * @private + * @constructor + * @param {number} minX Minimum X. + * @param {number} minY Minimum Y. + * @param {number} maxX Maximum X. + * @param {number} maxY Maximum Y. + * @param {ol.RTreeNode_} parent Parent node. + * @param {number} level Level in the tree hierarchy. + * @extends {ol.Rectangle} + */ +ol.RTreeNode_ = function(minX, minY, maxX, maxY, parent, level) { + + goog.base(this, minX, minY, maxX, maxY); + + /** + * @type {Object} + */ + this.object; + + /** + * @type {string} + */ + this.objectId; + + /** + * @type {ol.RTreeNode_} + */ + this.parent = parent; + + /** + * @type {number} + */ + this.level = level; + + /** + * @type {Array.} + */ + this.children = []; + +}; +goog.inherits(ol.RTreeNode_, ol.Rectangle); + + +/** + * Find all objects intersected by a rectangle. + * @param {ol.Rectangle} bounds Bounding box. + * @param {Object.} results Target object for results. + */ +ol.RTreeNode_.prototype.find = function(bounds, results) { + if (this.intersects(bounds)) { + var numChildren = this.children.length; + if (numChildren === 0) { + results[this.objectId] = this.object; + } else { + for (var i = 0; i < numChildren; ++i) { + this.children[i].find(bounds, results); + } + } + } +}; + + +/** + * Find the appropriate node for insertion. + * @param {ol.Rectangle} bounds Bounding box. + * @return {ol.RTreeNode_|undefined} Matching node. + */ +ol.RTreeNode_.prototype.get = function(bounds) { + if (this.intersects(bounds)) { + var numChildren = this.children.length; + if (numChildren === 0) { + return goog.isNull(this.parent) ? this : this.parent; + } + var node; + for (var i = 0; i < numChildren; ++i) { + node = this.children[i].get(bounds); + if (goog.isDef(node)) { + return node; + } + } + return this; + } +}; + + +/** + * Update boxes up to the root to ensure correct bounding + * @param {ol.Rectangle} bounds Bounding box. + */ +ol.RTreeNode_.prototype.update = function(bounds) { + this.extend(bounds); + if (!goog.isNull(this.parent)) { + this.parent.update(bounds); + } +}; + + +/** + * Divide @this node's children in half and create two new boxes containing + * the split items. The top left will be the topmost leftmost child and the + * bottom right will be the rightmost bottommost child. + */ +ol.RTreeNode_.prototype.divide = function() { + var numChildren = this.children.length; + if (numChildren === 0) { + return; + } + + var half = Math.ceil(numChildren / 2), + child, node; + + for (var i = 0; i < numChildren; ++i) { + child = this.children[i]; + if (i % half === 0) { + node = new ol.RTreeNode_(child.minX, child.minY, child.maxX, child.maxY, + this, this.level + 1); + this.children.push(node); + } + child.parent = /** @type {ol.RTreeNode_} */ node; + node.children.push(child); + node.extend(child); + } +}; + + + +/** + * @constructor + */ +ol.structs.RTree = function() { + + /** + * @private + * @type {ol.RTreeNode_} + */ + this.root_ = new ol.RTreeNode_( + Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, + Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, null, 0); + +}; + + +/** + * @param {ol.Rectangle} bounds Bounding box. + * @return {Object.} Results for the passed bounding box. + */ +ol.structs.RTree.prototype.find = function(bounds) { + var results = /** @type {Object.} */ {}; + this.root_.find(bounds, results); + return results; +}; + + +/** + * @param {ol.Rectangle} bounds Bounding box. + * @param {Object} object Object to store with the passed bounds. + */ +ol.structs.RTree.prototype.put = function(bounds, object) { + var found = this.root_.get(bounds); + if (found) { + var node = new ol.RTreeNode_( + bounds.minX, bounds.minY, bounds.maxX, bounds.maxY, + found, found.level + 1); + node.object = object; + node.objectId = goog.getUid(object).toString(); + + found.children.push(node); + found.update(bounds); + + if (found.children.length >= ol.structs.RTree.MAX_OBJECTS && + found.level < ol.structs.RTree.MAX_SUB_DIVISIONS) { + found.divide(); + } + } +}; + + +/** + * @type {number} + */ +ol.structs.RTree.MAX_SUB_DIVISIONS = 6; + + +/** + * @type {number} + */ +ol.structs.RTree.MAX_OBJECTS = 6; + From dafa48da2e5875e017dbda68171591da1724261a Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 4 Feb 2013 22:51:58 +0100 Subject: [PATCH 053/180] Using spatial index and combined filters This should significantly speed up feature access. Needs unit tests. --- src/ol/source/vectorsource.js | 76 ++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index c7ea186c66..6a2f9e2089 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -1,10 +1,12 @@ goog.provide('ol.source.Vector'); goog.require('ol.Feature'); -goog.require('ol.filter.Filter'); +goog.require('ol.filter.Extent'); goog.require('ol.filter.Geometry'); +goog.require('ol.filter.Logical'); goog.require('ol.geom.GeometryType'); goog.require('ol.source.Source'); +goog.require('ol.structs.RTree'); @@ -19,13 +21,18 @@ ol.source.FeatureCache = function() { */ this.idLookup_; - /** * @type {Object.} * @private */ this.geometryTypeIndex_; + /** + * @type {ol.structs.RTree} + * @private + */ + this.rTree_; + this.clear(); }; @@ -42,6 +49,8 @@ ol.source.FeatureCache.prototype.clear = function() { geometryTypeIndex[ol.geom.GeometryType[key]] = {}; } this.geometryTypeIndex_ = geometryTypeIndex; + + this.rTree_ = new ol.structs.RTree(); }; @@ -55,17 +64,51 @@ ol.source.FeatureCache.prototype.add = function(feature) { this.idLookup_[id] = feature; - // index by geometry type + // index by geometry type and bounding box if (!goog.isNull(geometry)) { this.geometryTypeIndex_[geometry.getType()][id] = feature; + this.rTree_.put(geometry.getBounds(), feature); } +}; - /** - * TODO: Index by tile coord. To do this for real requires knowledge about - * the evaluated symbolizer literal for each feature. Initially, a pixel - * buffer could be provided. - */ +/** + * @param {ol.filter.Filter=} opt_filter Optional filter. + * @return {Object.} Object of features, keyed by id. + * @private + */ +ol.source.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) { + var features; + if (!goog.isDef(opt_filter)) { + features = this.idLookup_; + } else { + if (opt_filter instanceof ol.filter.Logical) { + features = {}; + var filters = opt_filter.getFilters(), + filterFeatures, key, or; + for (var i = filters.length - 1; i >= 0; --i) { + filterFeatures = this.getFeaturesObject_(filters[i]); + goog.object.extend(features, filterFeatures); + if (opt_filter.operator === ol.filter.LogicalOperator.AND) { + or = features; + features = {}; + for (key in or) { + if (filterFeatures[key]) { + features[key] = or[key]; + } + } + } + } + } else if (opt_filter instanceof ol.filter.Geometry) { + features = this.geometryTypeIndex_[opt_filter.getType()]; + } else if (opt_filter instanceof ol.filter.Extent) { + features = this.rTree_.find(opt_filter.getExtent()); + } else { + // TODO: support other filter types + throw new Error('Filter type not supported: ' + opt_filter); + } + } + return features; }; @@ -74,22 +117,7 @@ ol.source.FeatureCache.prototype.add = function(feature) { * @return {Array.} Array of features. */ ol.source.FeatureCache.prototype.getFeatures = function(opt_filter) { - var features; - if (!goog.isDef(opt_filter)) { - features = new Array(); - for (var id in this.idLookup_) { - features.push(this.idLookup_[id]); - } - } else { - if (opt_filter instanceof ol.filter.Geometry) { - features = this.getFeaturesByGeometryType_( - /** @type {ol.filter.Geometry} */ (opt_filter)); - } else { - // TODO: support other filter types - throw new Error('Filter type not supported: ' + opt_filter); - } - } - return features; + return goog.object.getValues(this.getFeaturesObject_(opt_filter)); }; From 619803cdeb9bb1cb879a5a03cadf272ee3ac2675 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 6 Feb 2013 16:36:42 +0100 Subject: [PATCH 054/180] More goog.requires fixes --- src/ol/geom/linestring.js | 1 - src/ol/geom/point.js | 1 - src/ol/geom/polygon.js | 1 - src/ol/renderer/canvas/canvasmaprenderer.js | 1 + src/ol/renderer/canvas/canvasrenderer.js | 1 - src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 1 - 6 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index 256df191a8..1e90688115 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -1,7 +1,6 @@ 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'); diff --git a/src/ol/geom/point.js b/src/ol/geom/point.js index b2c13f00f2..73b6b8338b 100644 --- a/src/ol/geom/point.js +++ b/src/ol/geom/point.js @@ -1,7 +1,6 @@ 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'); diff --git a/src/ol/geom/polygon.js b/src/ol/geom/polygon.js index 30ddd5f5be..4a0b32dad3 100644 --- a/src/ol/geom/polygon.js +++ b/src/ol/geom/polygon.js @@ -1,7 +1,6 @@ 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'); diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index f3b05ed53e..f9bde65550 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -8,6 +8,7 @@ goog.require('goog.style'); goog.require('goog.vec.Mat4'); goog.require('ol.Size'); goog.require('ol.layer.TileLayer'); +goog.require('ol.layer.Vector'); goog.require('ol.renderer.Map'); goog.require('ol.renderer.canvas.TileLayer'); goog.require('ol.renderer.canvas.VectorLayer'); diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index b2880a9c0b..3ccb400144 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -1,4 +1,3 @@ -goog.provide('ol.renderer.canvas'); goog.provide('ol.renderer.canvas.Renderer'); goog.provide('ol.renderer.canvas.isSupported'); diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 86b04618de..3c93c85765 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -7,7 +7,6 @@ goog.require('ol.TileCoord'); goog.require('ol.filter.Geometry'); goog.require('ol.geom.GeometryType'); goog.require('ol.layer.Vector'); -goog.require('ol.renderer.canvas'); goog.require('ol.renderer.canvas.Layer'); goog.require('ol.renderer.canvas.Renderer'); goog.require('ol.style.LiteralLine'); From 4e971b604169e4216dbdc2bc2b173532b9769484 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 6 Feb 2013 16:42:55 +0100 Subject: [PATCH 055/180] Faster extent + geometry type queries By maintaining separate R-Trees for each geometry type, we cover the most critical query from the canvas renderer at higher speeds. Using the filter's own evaluate method, we can now also serve arbitrary queries, but they won't be fast. Still needs unit tests. --- src/ol/source/vectorsource.js | 83 +++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index d9ac0832d2..9898c1c7fc 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -27,16 +27,16 @@ ol.source.FeatureCache = function() { this.idLookup_; /** - * @type {Object.} + * @type {Object.} * @private */ this.geometryTypeIndex_; /** - * @type {ol.structs.RTree} + * @type {Object.} * @private */ - this.rTree_; + this.boundsByGeometryType_; this.clear(); @@ -49,13 +49,16 @@ ol.source.FeatureCache = function() { ol.source.FeatureCache.prototype.clear = function() { this.idLookup_ = {}; - var geometryTypeIndex = {}; + var geometryTypeIndex = {}, + boundsByGeometryType = {}, + geometryType; for (var key in ol.geom.GeometryType) { - geometryTypeIndex[ol.geom.GeometryType[key]] = {}; + geometryType = ol.geom.GeometryType[key]; + geometryTypeIndex[geometryType] = {}; + boundsByGeometryType[geometryType] = new ol.structs.RTree(); } this.geometryTypeIndex_ = geometryTypeIndex; - - this.rTree_ = new ol.structs.RTree(); + this.boundsByGeometryType_ = boundsByGeometryType; }; @@ -71,8 +74,10 @@ ol.source.FeatureCache.prototype.add = function(feature) { // index by geometry type and bounding box if (!goog.isNull(geometry)) { - this.geometryTypeIndex_[geometry.getType()][id] = feature; - this.rTree_.put(geometry.getBounds(), feature); + var geometryType = geometry.getType(); + this.geometryTypeIndex_[geometryType][id] = feature; + this.boundsByGeometryType_[geometryType].put(geometry.getBounds(), + feature); } }; @@ -83,34 +88,48 @@ ol.source.FeatureCache.prototype.add = function(feature) { * @private */ ol.source.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) { - var features; + var i, features; if (!goog.isDef(opt_filter)) { features = this.idLookup_; } else { - if (opt_filter instanceof ol.filter.Logical) { - features = {}; - var filters = opt_filter.getFilters(), - filterFeatures, key, or; - for (var i = filters.length - 1; i >= 0; --i) { - filterFeatures = this.getFeaturesObject_(filters[i]); - goog.object.extend(features, filterFeatures); - if (opt_filter.operator === ol.filter.LogicalOperator.AND) { - or = features; - features = {}; - for (key in or) { - if (filterFeatures[key]) { - features[key] = or[key]; - } - } - } - } - } else if (opt_filter instanceof ol.filter.Geometry) { + if (opt_filter instanceof ol.filter.Geometry) { features = this.geometryTypeIndex_[opt_filter.getType()]; } else if (opt_filter instanceof ol.filter.Extent) { - features = this.rTree_.find(opt_filter.getExtent()); - } else { - // TODO: support other filter types - throw new Error('Filter type not supported: ' + opt_filter); + var boundsByGeometryType = this.boundsByGeometryType_, + extent = opt_filter.getExtent(); + features = {}; + for (i in boundsByGeometryType) { + goog.object.extend(features, boundsByGeometryType[i].find(extent)); + } + } else if (opt_filter instanceof ol.filter.Logical) { + var filters = opt_filter.getFilters(); + if (filters.length === 2) { + var filter, geometryFilter, extentFilter; + for (i = 0; i <= 1; ++i) { + filter = filters[i]; + if (filter instanceof ol.filter.Geometry) { + geometryFilter = filter; + } else if (filter instanceof ol.filter.Extent) { + extentFilter = filter; + } + } + if (extentFilter && geometryFilter) { + features = this.boundsByGeometryType_[geometryFilter.getType()] + .find(extentFilter.getExtent()); + } + } + } + if (!goog.isDef(features)) { + // TODO: support fast lane for other filter types + var candidates = this.idLookup_, + feature; + features = {}; + for (i in candidates) { + feature = candidates[i]; + if (opt_filter.evaluate(feature) === true) { + features[i] = feature; + } + } } } return features; From 11b8c0ff479b7b5924bf9e58d04c79f6b5037cbd Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 6 Feb 2013 17:47:03 -0700 Subject: [PATCH 056/180] Don't generate new tiles while animating --- examples/vector-layer.html | 2 +- .../canvas/canvasvectorlayerrenderer.js | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/examples/vector-layer.html b/examples/vector-layer.html index 49da90dba9..a15adb4e8a 100644 --- a/examples/vector-layer.html +++ b/examples/vector-layer.html @@ -27,7 +27,7 @@ } } - Full-screen example + Vector Layer Example
diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 3c93c85765..a11c755416 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -220,7 +220,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = */ if (!this.dirty_ && this.renderedResolution_ === tileResolution && // TODO: extent.equals() - this.renderedResolution_.toString() === tileRangeExtent.toString()) { + this.renderedExtent_.toString() === tileRangeExtent.toString()) { return; } @@ -283,7 +283,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = tileCoord = new ol.TileCoord(z, x, y); key = tileCoord.toString(); tile = this.tileCache_[key]; - if (tile === undefined) { + if (tile === undefined && !frameState.viewHints[ol.ViewHint.ANIMATING]) { tileExtent = tileGrid.getTileCoordExtent(tileCoord); // TODO: instead of filtering here, do this on the source and maintain // a spatial index @@ -320,7 +320,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = // LRU - move tile key to the end of the index goog.array.remove(this.tileCacheIndex_, key); this.tileCacheIndex_.push(key); - if (tile === undefined) { + if (tile === undefined && !frameState.viewHints[ol.ViewHint.ANIMATING]) { tile = /** @type {HTMLCanvasElement} */ this.tileArchetype_.cloneNode(false); tile.getContext('2d').drawImage(sketchCanvas, @@ -333,12 +333,18 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = delete this.tileCache_[this.tileCacheIndex_.shift()]; } } - finalContext.drawImage(tile, - tileSize.width * (tileCoord.x - tileRange.minX), - tileSize.height * (tileRange.maxY - tileCoord.y)); + if (tile) { + finalContext.drawImage(tile, + tileSize.width * (tileCoord.x - tileRange.minX), + tileSize.height * (tileRange.maxY - tileCoord.y)); + } else { + // we decided not to generate a tile during animation + this.dirty_ = true; + } } this.renderedResolution_ = tileResolution; + this.renderedExtent_ = tileRangeExtent; }; From 5bbd8fd9f3bbd28d3a355159bdabf974fb521790 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 6 Feb 2013 18:23:39 -0700 Subject: [PATCH 057/180] Using ol.TileCache for managing canvas tiles --- .../canvas/canvasvectorlayerrenderer.js | 71 +++++++++++-------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index a11c755416..41abd11448 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -3,7 +3,9 @@ goog.provide('ol.renderer.canvas.VectorLayer'); goog.require('goog.vec.Mat4'); goog.require('ol.Extent'); goog.require('ol.Size'); +goog.require('ol.TileCache'); goog.require('ol.TileCoord'); +goog.require('ol.ViewHint'); goog.require('ol.filter.Geometry'); goog.require('ol.geom.GeometryType'); goog.require('ol.layer.Vector'); @@ -63,15 +65,10 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { /** * @private - * @type {Object.} + * @type {ol.TileCache} */ - this.tileCache_ = {}; - - /** - * @private - * @type {Array.} - */ - this.tileCacheIndex_ = []; + this.tileCache_ = new ol.TileCache( + ol.renderer.canvas.VectorLayer.TILECACHE_SIZE); /** * @private @@ -139,6 +136,12 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { // TODO: remove this this.symbolizers_ = symbolizers; + /** + * @private + * @type {boolean} + */ + this.pendingCachePrune_ = false; + }; goog.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); @@ -282,8 +285,10 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = for (y = tileRange.minY; y <= tileRange.maxY; ++y) { tileCoord = new ol.TileCoord(z, x, y); key = tileCoord.toString(); - tile = this.tileCache_[key]; - if (tile === undefined && !frameState.viewHints[ol.ViewHint.ANIMATING]) { + if (this.tileCache_.containsKey(key)) { + tilesToRender[key] = tileCoord; + } else if (!frameState.viewHints[ol.ViewHint.ANIMATING]) { + tilesToRender[key] = tileCoord; tileExtent = tileGrid.getTileCoordExtent(tileCoord); // TODO: instead of filtering here, do this on the source and maintain // a spatial index @@ -310,45 +315,51 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = } } } - tilesToRender[key] = tileCoord; } } + this.dirty_ = true; for (key in tilesToRender) { - tile = this.tileCache_[key]; tileCoord = tilesToRender[key]; - // LRU - move tile key to the end of the index - goog.array.remove(this.tileCacheIndex_, key); - this.tileCacheIndex_.push(key); - if (tile === undefined && !frameState.viewHints[ol.ViewHint.ANIMATING]) { + if (this.tileCache_.containsKey(key)) { + tile = /** @type {HTMLCanvasElement} */ (this.tileCache_.get(key)); + } else { tile = /** @type {HTMLCanvasElement} */ this.tileArchetype_.cloneNode(false); tile.getContext('2d').drawImage(sketchCanvas, (tileRange.minX - tileCoord.x) * tileSize.width, (tileCoord.y - tileRange.maxY) * tileSize.height); - this.tileCache_[key] = tile; - // manage cache - if (this.tileCacheIndex_.length >= - ol.renderer.canvas.VectorLayer.TILECACHE_SIZE) { - delete this.tileCache_[this.tileCacheIndex_.shift()]; - } - } - if (tile) { - finalContext.drawImage(tile, - tileSize.width * (tileCoord.x - tileRange.minX), - tileSize.height * (tileRange.maxY - tileCoord.y)); - } else { - // we decided not to generate a tile during animation - this.dirty_ = true; + this.tileCache_.set(key, tile); } + finalContext.drawImage(tile, + tileSize.width * (tileCoord.x - tileRange.minX), + tileSize.height * (tileRange.maxY - tileCoord.y)); + this.dirty_ = false; } this.renderedResolution_ = tileResolution; this.renderedExtent_ = tileRangeExtent; + if (!this.pendingCachePrune_) { + this.pendingCachePrune_ = true; + goog.global.setTimeout(goog.bind(this.pruneTileCache_, this), 0); + } }; +/** + * Get rid of tiles that exceed the cache capacity. + * TODO: add a method to the cache to handle this + * @private + */ +ol.renderer.canvas.VectorLayer.prototype.pruneTileCache_ = function() { + while (this.tileCache_.canExpireCache()) { + this.tileCache_.pop(); + } + this.pendingCachePrune_ = false; +}; + + /** * @type {number} */ From e155f870cd58f9a7270503e3d778233fda88353b Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 13 Feb 2013 12:29:14 +0100 Subject: [PATCH 058/180] Fixing RTree, and using it for retrieving geometries --- .../canvas/canvasvectorlayerrenderer.js | 30 +++++++------------ src/ol/structs/rtree.js | 4 ++- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 41abd11448..447b34dc48 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -6,7 +6,10 @@ goog.require('ol.Size'); goog.require('ol.TileCache'); goog.require('ol.TileCoord'); goog.require('ol.ViewHint'); +goog.require('ol.filter.Extent'); goog.require('ol.filter.Geometry'); +goog.require('ol.filter.Logical'); +goog.require('ol.filter.LogicalOperator'); goog.require('ol.geom.GeometryType'); goog.require('ol.layer.Vector'); goog.require('ol.renderer.canvas.Layer'); @@ -276,11 +279,11 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var renderedFeatures = {}; var tilesToRender = {}; - var tile, tileCoord, key, tileExtent, tileState, x, y; + var tile, tileCoord, key, tileState, x, y; // render features by geometry type var filters = this.geometryFilters_, numFilters = filters.length, - i, filter, type, features, symbolizer; + i, spatialFilter, extentFilter, type, features, symbolizer; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { tileCoord = new ol.TileCoord(z, x, y); @@ -289,24 +292,13 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = tilesToRender[key] = tileCoord; } else if (!frameState.viewHints[ol.ViewHint.ANIMATING]) { tilesToRender[key] = tileCoord; - tileExtent = tileGrid.getTileCoordExtent(tileCoord); - // TODO: instead of filtering here, do this on the source and maintain - // a spatial index - function filterFn(feature) { - var id = goog.getUid(feature); - var include = !(id in renderedFeatures) && - feature.getGeometry().getBounds().intersects(tileExtent); - if (include === true) { - renderedFeatures[id] = true; - } - return include; - } + extentFilter = new ol.filter.Extent( + tileGrid.getTileCoordExtent(tileCoord)); for (i = 0; i < numFilters; ++i) { - filter = filters[i]; - type = filter.getType(); - features = source.getFeatures(filter); - // TODO: spatial index of tiles - see filterFn above - features = goog.array.filter(features, filterFn); + spatialFilter = filters[i]; + type = spatialFilter.getType(); + features = source.getFeatures(new ol.filter.Logical( + [spatialFilter, extentFilter], ol.filter.LogicalOperator.AND)); if (features.length) { // TODO: layer.getSymbolizerLiterals(features) or similar symbolizer = this.symbolizers_[type]; diff --git a/src/ol/structs/rtree.js b/src/ol/structs/rtree.js index 3274d72f23..0d72dbe6ee 100644 --- a/src/ol/structs/rtree.js +++ b/src/ol/structs/rtree.js @@ -57,7 +57,9 @@ ol.RTreeNode_.prototype.find = function(bounds, results) { if (this.intersects(bounds)) { var numChildren = this.children.length; if (numChildren === 0) { - results[this.objectId] = this.object; + if (goog.isDef(this.object)) { + results[this.objectId] = this.object; + } } else { for (var i = 0; i < numChildren; ++i) { this.children[i].find(bounds, results); From 2082ba7637d25ee4c3f88fb1522befebaf94abf8 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 13 Feb 2013 15:51:06 -0700 Subject: [PATCH 059/180] Fewer, bigger tiles --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 447b34dc48..6e7a4f4414 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -191,7 +191,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = if (goog.isNull(tileGrid)) { // lazy tile source creation to match the view projection tileGrid = ol.tilegrid.createForProjection( - view2DState.projection, /** TODO: get this elsewhere */ 22); + view2DState.projection, /** TODO: get this elsewhere */ 22, + new ol.Size(512, 512)); source.setTileGrid(tileGrid); } From be255ed6c7b0499e9b6c2202d68bcaa0a6f01103 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 14 Feb 2013 17:19:15 -0700 Subject: [PATCH 060/180] Adding ol.Expression This simple expression constructor will be used for symbolizer properties and the layer will generate symbolizer literals for rendering by evaluating any expressions with a feature as the this argument and feature attributes as the scope. This allows generating labels that concatenate multiple attribute values together or displaying point symbols that are sized according to a population attribute divided by an area attribute, for example. This implementation will not work in environments where the content security policy disallows the use of the Function constructor. This is the case on browser extensions. A more content-security-policy-friendly implementation would be to come up with a restricted grammar and write a lex/parser. This is the road I started down, but this verison is far less code, and I think the security policy limitations are minor at this point. This version will always be faster/lighter than a parser, so one is written in the future, it should only be pulled in where content security policy mandates it. --- src/ol/expression.js | 43 ++++++++++++++++++ test/spec/ol/expression.test.js | 78 +++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 src/ol/expression.js create mode 100644 test/spec/ol/expression.test.js diff --git a/src/ol/expression.js b/src/ol/expression.js new file mode 100644 index 0000000000..8cab09b88d --- /dev/null +++ b/src/ol/expression.js @@ -0,0 +1,43 @@ +goog.provide('ol.Expression'); + + + +/** + * @constructor + * @param {string} source Expression to be evaluated. + */ +ol.Expression = function(source) { + + /** + * @type {string} + * @private + */ + this.source_ = source; + +}; + + +/** + * Evaluate the expression and return the result. + * + * @param {Object=} opt_thisArg Object to use as this when evaluating the + * expression. If not provided, the global object will be used. + * @param {Object=} opt_scope Evaluation scope. All properties of this object + * will be available as variables when evaluating the expression. If not + * provided, the global object will be used. + * @return {*} Result of the expression. + */ +ol.Expression.prototype.evaluate = function(opt_thisArg, opt_scope) { + var thisArg = goog.isDef(opt_thisArg) ? opt_thisArg : goog.global, + scope = goog.isDef(opt_scope) ? opt_scope : goog.global, + names = [], + values = []; + + for (var name in scope) { + names.push(name); + values.push(scope[name]); + } + + var evaluator = new Function(names.join(','), 'return ' + this.source_); + return evaluator.apply(thisArg, values); +}; diff --git a/test/spec/ol/expression.test.js b/test/spec/ol/expression.test.js new file mode 100644 index 0000000000..1cedfd1c67 --- /dev/null +++ b/test/spec/ol/expression.test.js @@ -0,0 +1,78 @@ +goog.provide('ol.test.Expression'); + +describe('ol.Expression', function() { + + describe('constructor', function() { + it('creates an expression', function() { + var exp = new ol.Expression('foo'); + expect(exp).toBeA(ol.Expression); + }); + }); + + describe('#evaluate()', function() { + + it('evaluates and returns the result', function() { + // test cases here with unique values only (lack of messages in expect) + var cases = [{ + source: '42', result: 42 + }, { + source: '10 + 10', result: 20 + }, { + source: '"a" + "b"', result: 'ab' + }, { + source: 'Math.floor(Math.PI)', result: 3 + }, { + source: 'ol', result: ol + }, { + source: 'this', result: goog.global + }]; + + var c, exp; + for (var i = 0, ii = cases.length; i < ii; ++i) { + c = cases[i]; + exp = new ol.Expression(c.source); + expect(exp.evaluate()).toBe(c.result); + } + }); + + it('accepts an optional this argument', function() { + function Thing() { + this.works = true; + }; + + var exp = new ol.Expression('this.works ? "yes" : "no"'); + expect(exp.evaluate(new Thing())).toBe('yes'); + expect(exp.evaluate({})).toBe('no'); + }); + + it('accepts an optional scope argument', function() { + var exp; + var scope = { + greeting: 'hello world', + punctuation: '!', + pick: function(array, index) { + return array[index]; + } + }; + + // access two members in the scope + exp = new ol.Expression('greeting + punctuation'); + expect(exp.evaluate({}, scope)).toBe('hello world!'); + + // call a function in the scope + exp = new ol.Expression( + 'pick([10, 42, "chicken"], 2) + Math.floor(Math.PI)'); + expect(exp.evaluate({}, scope)).toBe('chicken3'); + + }); + + it('throws on error', function() { + var exp = new ol.Expression('@*)$(&'); + expect(function() {exp.evaluate()}).toThrow(); + }); + + }); + +}); + +goog.require('ol.Expression'); From 8cc65756bce55504f0458d8dad17fed5b6449742 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 15 Feb 2013 16:25:29 +0100 Subject: [PATCH 061/180] Only attempt fast lane for AND filters --- src/ol/source/vectorsource.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index 9898c1c7fc..7a63b4bc45 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -101,7 +101,8 @@ ol.source.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) { for (i in boundsByGeometryType) { goog.object.extend(features, boundsByGeometryType[i].find(extent)); } - } else if (opt_filter instanceof ol.filter.Logical) { + } else if (opt_filter instanceof ol.filter.Logical && + opt_filter.operator === ol.filter.LogicalOperator.AND) { var filters = opt_filter.getFilters(); if (filters.length === 2) { var filter, geometryFilter, extentFilter; From 8c03abdaa201f80509ffb7c3df532fb6fb838249 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 15 Feb 2013 16:25:47 +0100 Subject: [PATCH 062/180] Adding ol.source.Vector unit tests --- test/spec/ol/source/vectorsource.test.js | 83 ++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 test/spec/ol/source/vectorsource.test.js diff --git a/test/spec/ol/source/vectorsource.test.js b/test/spec/ol/source/vectorsource.test.js new file mode 100644 index 0000000000..ee63e2ef44 --- /dev/null +++ b/test/spec/ol/source/vectorsource.test.js @@ -0,0 +1,83 @@ +goog.provide('ol.test.source.Vector'); + + +describe('ol.source.Vector', function() { + + var vectorSource, features; + + beforeEach(function() { + features = [ + new ol.Feature(new ol.geom.Point([16.0, 48.0])), + new ol.Feature(new ol.geom.Point([16.1, 48.1])), + new ol.Feature(new ol.geom.Point([16.2, 48.2])), + new ol.Feature(new ol.geom.Point([16.3, 48.3])), + new ol.Feature(new ol.geom.LineString([[16.4, 48.4], [16.5, 48.5]])), + new ol.Feature(new ol.geom.LineString([[16.6, 48.6], [16.7, 48.7]])), + new ol.Feature(new ol.geom.LineString([[16.8, 48.8], [16.9, 48.9]])), + new ol.Feature(new ol.geom.LineString([[17.0, 49.0], [17.1, 49.1]])) + ]; + vectorSource = new ol.source.Vector({ + projection: ol.Projection.getFromCode('EPSG:4326') + }); + vectorSource.addFeatures(features); + }); + + it('has working addFeatures and getFeatures methods', function() { + var featuresInSource = vectorSource.getFeatures(); + expect(featuresInSource.length).toEqual(features.length); + expect(featuresInSource).toContain(features[0]); + vectorSource.addFeatures([new ol.Feature()]); + expect(vectorSource.getFeatures().length).toEqual(features.length + 1); + }); + + var geomFilter = new ol.filter.Geometry(ol.geom.GeometryType.LINESTRING); + var extentFilter = new ol.filter.Extent(new ol.Extent(16, 48, 16.3, 48.3)); + + it('can filter by geometry type using its GeometryType index', function() { + spyOn(geomFilter, 'evaluate'); + var lineStrings = vectorSource.getFeatures(geomFilter); + expect(geomFilter.evaluate).not.toHaveBeenCalled(); + expect(lineStrings.length).toEqual(4); + expect(lineStrings).toContain(features[4]); + }); + + it('can filter by extent using its RTree', function() { + spyOn(extentFilter, 'evaluate'); + var subset = vectorSource.getFeatures(extentFilter); + expect(extentFilter.evaluate).not.toHaveBeenCalled(); + expect(subset.length).toEqual(4); + expect(subset).not.toContain(features[7]); + }); + + it('can filter by extent and geometry type using its index', function() { + var filter1 = new ol.filter.Logical([geomFilter, extentFilter], + ol.filter.LogicalOperator.AND); + var filter2 = new ol.filter.Logical([extentFilter, geomFilter], + ol.filter.LogicalOperator.AND); + spyOn(filter1, 'evaluate'); + spyOn(filter2, 'evaluate'); + var subset1 = vectorSource.getFeatures(filter1); + var subset2 = vectorSource.getFeatures(filter2); + expect(filter1.evaluate).not.toHaveBeenCalled(); + expect(filter2.evaluate).not.toHaveBeenCalled(); + expect(subset1.length).toEqual(0); + expect(subset2.length).toEqual(0); + }); + + it('can handle any query using the filter\'s evaluate function', function() { + var filter = new ol.filter.Logical([geomFilter, extentFilter], + ol.filter.LogicalOperator.OR); + spyOn(filter, 'evaluate').andCallThrough(); + var subset = vectorSource.getFeatures(filter); + expect(filter.evaluate).toHaveBeenCalled(); + expect(subset.length).toEqual(8); + }); + +}); + +goog.require('ol.Projection'); +goog.require('ol.filter.GeometryType'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.LineString'); +goog.require('ol.source.Vector'); From 59fe714fa6b646e797a3274c2ab08130bcc7b3f3 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 15 Feb 2013 17:44:09 +0100 Subject: [PATCH 063/180] Making the linter happy --- src/ol/source/vectorsource.js | 1 + test/spec/ol/source/vectorsource.test.js | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index 7a63b4bc45..27958b83f6 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -8,6 +8,7 @@ goog.require('ol.filter.Extent'); goog.require('ol.filter.Filter'); goog.require('ol.filter.Geometry'); goog.require('ol.filter.Logical'); +goog.require('ol.filter.LogicalOperator'); goog.require('ol.geom.GeometryType'); goog.require('ol.source.TileSource'); goog.require('ol.structs.RTree'); diff --git a/test/spec/ol/source/vectorsource.test.js b/test/spec/ol/source/vectorsource.test.js index ee63e2ef44..d6b7a204f5 100644 --- a/test/spec/ol/source/vectorsource.test.js +++ b/test/spec/ol/source/vectorsource.test.js @@ -2,7 +2,7 @@ goog.provide('ol.test.source.Vector'); describe('ol.source.Vector', function() { - + var vectorSource, features; beforeEach(function() { @@ -29,7 +29,7 @@ describe('ol.source.Vector', function() { vectorSource.addFeatures([new ol.Feature()]); expect(vectorSource.getFeatures().length).toEqual(features.length + 1); }); - + var geomFilter = new ol.filter.Geometry(ol.geom.GeometryType.LINESTRING); var extentFilter = new ol.filter.Extent(new ol.Extent(16, 48, 16.3, 48.3)); @@ -40,7 +40,7 @@ describe('ol.source.Vector', function() { expect(lineStrings.length).toEqual(4); expect(lineStrings).toContain(features[4]); }); - + it('can filter by extent using its RTree', function() { spyOn(extentFilter, 'evaluate'); var subset = vectorSource.getFeatures(extentFilter); @@ -48,7 +48,7 @@ describe('ol.source.Vector', function() { expect(subset.length).toEqual(4); expect(subset).not.toContain(features[7]); }); - + it('can filter by extent and geometry type using its index', function() { var filter1 = new ol.filter.Logical([geomFilter, extentFilter], ol.filter.LogicalOperator.AND); @@ -63,7 +63,7 @@ describe('ol.source.Vector', function() { expect(subset1.length).toEqual(0); expect(subset2.length).toEqual(0); }); - + it('can handle any query using the filter\'s evaluate function', function() { var filter = new ol.filter.Logical([geomFilter, extentFilter], ol.filter.LogicalOperator.OR); @@ -76,7 +76,9 @@ describe('ol.source.Vector', function() { }); goog.require('ol.Projection'); -goog.require('ol.filter.GeometryType'); +goog.require('ol.filter.Geometry'); +goog.require('ol.filter.Logical'); +goog.require('ol.filter.LogicalOperator'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.Point'); goog.require('ol.geom.LineString'); From 77b8688eae2d2e551e16d84086dac2e15387f76f Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 15 Feb 2013 23:42:22 +0100 Subject: [PATCH 064/180] Regrouping tests --- test/spec/ol/source/vectorsource.test.js | 128 ++++++++++++----------- 1 file changed, 69 insertions(+), 59 deletions(-) diff --git a/test/spec/ol/source/vectorsource.test.js b/test/spec/ol/source/vectorsource.test.js index d6b7a204f5..f3ac91aeda 100644 --- a/test/spec/ol/source/vectorsource.test.js +++ b/test/spec/ol/source/vectorsource.test.js @@ -3,74 +3,84 @@ goog.provide('ol.test.source.Vector'); describe('ol.source.Vector', function() { - var vectorSource, features; + var vectorSource; - beforeEach(function() { - features = [ - new ol.Feature(new ol.geom.Point([16.0, 48.0])), - new ol.Feature(new ol.geom.Point([16.1, 48.1])), - new ol.Feature(new ol.geom.Point([16.2, 48.2])), - new ol.Feature(new ol.geom.Point([16.3, 48.3])), - new ol.Feature(new ol.geom.LineString([[16.4, 48.4], [16.5, 48.5]])), - new ol.Feature(new ol.geom.LineString([[16.6, 48.6], [16.7, 48.7]])), - new ol.Feature(new ol.geom.LineString([[16.8, 48.8], [16.9, 48.9]])), - new ol.Feature(new ol.geom.LineString([[17.0, 49.0], [17.1, 49.1]])) - ]; - vectorSource = new ol.source.Vector({ - projection: ol.Projection.getFromCode('EPSG:4326') + describe('#addFeatures()', function() { + + it('works', function() { + vectorSource = new ol.source.Vector({ + projection: ol.Projection.getFromCode('EPSG:4326') + }); + vectorSource.addFeatures([new ol.Feature()]); + expect(vectorSource.getFeatures().length).toEqual(1); }); - vectorSource.addFeatures(features); }); - it('has working addFeatures and getFeatures methods', function() { - var featuresInSource = vectorSource.getFeatures(); - expect(featuresInSource.length).toEqual(features.length); - expect(featuresInSource).toContain(features[0]); - vectorSource.addFeatures([new ol.Feature()]); - expect(vectorSource.getFeatures().length).toEqual(features.length + 1); - }); + describe('#getFeatures()', function() { - var geomFilter = new ol.filter.Geometry(ol.geom.GeometryType.LINESTRING); - var extentFilter = new ol.filter.Extent(new ol.Extent(16, 48, 16.3, 48.3)); + var features; - it('can filter by geometry type using its GeometryType index', function() { - spyOn(geomFilter, 'evaluate'); - var lineStrings = vectorSource.getFeatures(geomFilter); - expect(geomFilter.evaluate).not.toHaveBeenCalled(); - expect(lineStrings.length).toEqual(4); - expect(lineStrings).toContain(features[4]); - }); + beforeEach(function() { + features = [ + new ol.Feature(new ol.geom.Point([16.0, 48.0])), + new ol.Feature(new ol.geom.Point([16.1, 48.1])), + new ol.Feature(new ol.geom.Point([16.2, 48.2])), + new ol.Feature(new ol.geom.Point([16.3, 48.3])), + new ol.Feature(new ol.geom.LineString([[16.4, 48.4], [16.5, 48.5]])), + new ol.Feature(new ol.geom.LineString([[16.6, 48.6], [16.7, 48.7]])), + new ol.Feature(new ol.geom.LineString([[16.8, 48.8], [16.9, 48.9]])), + new ol.Feature(new ol.geom.LineString([[17.0, 49.0], [17.1, 49.1]])) + ]; + vectorSource = new ol.source.Vector({ + projection: ol.Projection.getFromCode('EPSG:4326') + }); + vectorSource.addFeatures(features); + }); - it('can filter by extent using its RTree', function() { - spyOn(extentFilter, 'evaluate'); - var subset = vectorSource.getFeatures(extentFilter); - expect(extentFilter.evaluate).not.toHaveBeenCalled(); - expect(subset.length).toEqual(4); - expect(subset).not.toContain(features[7]); - }); + var geomFilter = new ol.filter.Geometry(ol.geom.GeometryType.LINESTRING); + var extentFilter = new ol.filter.Extent(new ol.Extent(16, 48, 16.3, 48.3)); - it('can filter by extent and geometry type using its index', function() { - var filter1 = new ol.filter.Logical([geomFilter, extentFilter], - ol.filter.LogicalOperator.AND); - var filter2 = new ol.filter.Logical([extentFilter, geomFilter], - ol.filter.LogicalOperator.AND); - spyOn(filter1, 'evaluate'); - spyOn(filter2, 'evaluate'); - var subset1 = vectorSource.getFeatures(filter1); - var subset2 = vectorSource.getFeatures(filter2); - expect(filter1.evaluate).not.toHaveBeenCalled(); - expect(filter2.evaluate).not.toHaveBeenCalled(); - expect(subset1.length).toEqual(0); - expect(subset2.length).toEqual(0); - }); + it('can filter by geometry type using its GeometryType index', function() { + spyOn(geomFilter, 'evaluate'); + var lineStrings = vectorSource.getFeatures(geomFilter); + expect(geomFilter.evaluate).not.toHaveBeenCalled(); + expect(lineStrings.length).toEqual(4); + expect(lineStrings).toContain(features[4]); + }); + + it('can filter by extent using its RTree', function() { + spyOn(extentFilter, 'evaluate'); + var subset = vectorSource.getFeatures(extentFilter); + expect(extentFilter.evaluate).not.toHaveBeenCalled(); + expect(subset.length).toEqual(4); + expect(subset).not.toContain(features[7]); + }); + + it('can filter by extent and geometry type using its index', function() { + var filter1 = new ol.filter.Logical([geomFilter, extentFilter], + ol.filter.LogicalOperator.AND); + var filter2 = new ol.filter.Logical([extentFilter, geomFilter], + ol.filter.LogicalOperator.AND); + spyOn(filter1, 'evaluate'); + spyOn(filter2, 'evaluate'); + var subset1 = vectorSource.getFeatures(filter1); + var subset2 = vectorSource.getFeatures(filter2); + expect(filter1.evaluate).not.toHaveBeenCalled(); + expect(filter2.evaluate).not.toHaveBeenCalled(); + expect(subset1.length).toEqual(0); + expect(subset2.length).toEqual(0); + }); + + it('can handle any query using the filter\'s evaluate function', + function() { + var filter = new ol.filter.Logical([geomFilter, extentFilter], + ol.filter.LogicalOperator.OR); + spyOn(filter, 'evaluate').andCallThrough(); + var subset = vectorSource.getFeatures(filter); + expect(filter.evaluate).toHaveBeenCalled(); + expect(subset.length).toEqual(8); + }); - it('can handle any query using the filter\'s evaluate function', function() { - var filter = new ol.filter.Logical([geomFilter, extentFilter], - ol.filter.LogicalOperator.OR); - spyOn(filter, 'evaluate').andCallThrough(); - var subset = vectorSource.getFeatures(filter); - expect(filter.evaluate).toHaveBeenCalled(); - expect(subset.length).toEqual(8); }); }); From d2a22016e20882b07513a92c9c1cab1e8c738a27 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 15 Feb 2013 23:43:10 +0100 Subject: [PATCH 065/180] Adding extent filter tests --- test/spec/ol/filter/extentfilter.test.js | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 test/spec/ol/filter/extentfilter.test.js diff --git a/test/spec/ol/filter/extentfilter.test.js b/test/spec/ol/filter/extentfilter.test.js new file mode 100644 index 0000000000..68e1a0a2e7 --- /dev/null +++ b/test/spec/ol/filter/extentfilter.test.js @@ -0,0 +1,37 @@ +goog.provide('ol.test.filter.Extent'); + + +describe('ol.filter.Extent', function() { + + var extent, filter; + + beforeEach(function() { + extent = new ol.Extent(0, 0, 45, 90); + filter = new ol.filter.Extent(extent); + }); + + describe('#getExtent()', function() { + + it('returns the configured extent', function() { + expect(filter.getExtent()).toBe(extent); + }); + + }); + + describe('#evaluate()', function() { + + it('returns true if a feature intersects, false if not', function() { + expect(filter.evaluate(new ol.Feature(new ol.geom.Point([44, 89])))) + .toBe(true); + expect(filter.evaluate(new ol.Feature(new ol.geom.Point([46, 91])))) + .toBe(false); + }); + + }); + +}); + +goog.require('ol.Extent'); +goog.require('ol.Feature'); +goog.require('ol.filter.Extent'); +goog.require('ol.geom.Point'); From 266111e5f1c5e0469d9d1dd55739a133ca6c4bb7 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 15 Feb 2013 23:46:32 +0100 Subject: [PATCH 066/180] Adding goog.provide and goog.requires --- test/spec/ol/filter/geometryfilter.test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/spec/ol/filter/geometryfilter.test.js b/test/spec/ol/filter/geometryfilter.test.js index 5e07ea2bbd..b9c41ef7ae 100644 --- a/test/spec/ol/filter/geometryfilter.test.js +++ b/test/spec/ol/filter/geometryfilter.test.js @@ -1,3 +1,6 @@ +goog.provide('ol.test.filter.Geometry'); + + describe('ol.filter.Geometry', function() { describe('constructor', function() { @@ -43,3 +46,6 @@ describe('ol.filter.Geometry', function() { }); }); + +goog.require('ol.filter.Geometry'); +goog.require('ol.filter.GeometryType'); From e9fb83d4ee3b24350994e736f3ffce6204fe8069 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 18 Feb 2013 11:58:43 -0700 Subject: [PATCH 067/180] Use post render frame for tile pruning We should discuss whether post render functions must be run after each render frame or not. If these can be run after multiple render frames, it would make sense to increase the timeout. As it is, it looks like post render functions are run for every render. Hard to see what the benefit is in this case. --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 6e7a4f4414..da1c1f64b6 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -334,7 +334,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = this.renderedExtent_ = tileRangeExtent; if (!this.pendingCachePrune_) { this.pendingCachePrune_ = true; - goog.global.setTimeout(goog.bind(this.pruneTileCache_, this), 0); + frameState.postRenderFunctions.push(goog.bind(this.pruneTileCache_, this)); } }; From ce4b95f96073ad193ecb4bea2c499147ccc8ae28 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 18 Feb 2013 17:54:13 -0700 Subject: [PATCH 068/180] Decoupling canvas tile grid from vector source grid If we have a gridded vector source, the grid should have something to do with the source data (e.g. the vector data is available in a regular grid). The vector layer renderer's internal grid is for rendering canvas tiles and doesn't have anything to do with the source. --- .../renderer/canvas/canvasvectorlayerrenderer.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index da1c1f64b6..6d6e2a1aab 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -145,6 +145,15 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { */ this.pendingCachePrune_ = false; + /** + * Grid used for internal generation of canvas tiles. This is created + * lazily so we have access to the view projection. + * + * @private + * @type {ol.TileGrid} + */ + this.tileGrid_ = null; + }; goog.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); @@ -186,14 +195,15 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = resolution = view2DState.resolution, extent = frameState.extent, source = this.getVectorLayer().getVectorSource(), - tileGrid = source.getTileGrid(); + tileGrid = this.tileGrid_; if (goog.isNull(tileGrid)) { // lazy tile source creation to match the view projection tileGrid = ol.tilegrid.createForProjection( - view2DState.projection, /** TODO: get this elsewhere */ 22, + view2DState.projection, + 22, // should be no harm in going big here - ideally, it would be ∞ new ol.Size(512, 512)); - source.setTileGrid(tileGrid); + this.tileGrid_ = tileGrid; } // set up transform for the layer canvas to be drawn to the map canvas From 46e31f9f9a88c65574c09fff46ec51d5b2472461 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 18 Feb 2013 22:20:50 -0700 Subject: [PATCH 069/180] Lint and names --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 6d6e2a1aab..a170a5b769 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -18,6 +18,7 @@ goog.require('ol.style.LiteralLine'); goog.require('ol.style.LiteralPolygon'); goog.require('ol.style.LiteralShape'); goog.require('ol.style.ShapeType'); +goog.require('ol.tilegrid.TileGrid'); @@ -150,7 +151,7 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { * lazily so we have access to the view projection. * * @private - * @type {ol.TileGrid} + * @type {ol.tilegrid.TileGrid} */ this.tileGrid_ = null; @@ -200,7 +201,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = if (goog.isNull(tileGrid)) { // lazy tile source creation to match the view projection tileGrid = ol.tilegrid.createForProjection( - view2DState.projection, + view2DState.projection, 22, // should be no harm in going big here - ideally, it would be ∞ new ol.Size(512, 512)); this.tileGrid_ = tileGrid; @@ -294,7 +295,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = // render features by geometry type var filters = this.geometryFilters_, numFilters = filters.length, - i, spatialFilter, extentFilter, type, features, symbolizer; + i, geomFilter, extentFilter, type, features, symbolizer; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { tileCoord = new ol.TileCoord(z, x, y); @@ -306,10 +307,10 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = extentFilter = new ol.filter.Extent( tileGrid.getTileCoordExtent(tileCoord)); for (i = 0; i < numFilters; ++i) { - spatialFilter = filters[i]; - type = spatialFilter.getType(); + geomFilter = filters[i]; + type = geomFilter.getType(); features = source.getFeatures(new ol.filter.Logical( - [spatialFilter, extentFilter], ol.filter.LogicalOperator.AND)); + [geomFilter, extentFilter], ol.filter.LogicalOperator.AND)); if (features.length) { // TODO: layer.getSymbolizerLiterals(features) or similar symbolizer = this.symbolizers_[type]; From f15c2951d08df58d37cbd048cf69216b6bd821b7 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 18 Feb 2013 21:13:49 +0100 Subject: [PATCH 070/180] Adding RTree tests --- test/spec/ol/structs/rtree.test.js | 62 ++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 test/spec/ol/structs/rtree.test.js diff --git a/test/spec/ol/structs/rtree.test.js b/test/spec/ol/structs/rtree.test.js new file mode 100644 index 0000000000..f307be320e --- /dev/null +++ b/test/spec/ol/structs/rtree.test.js @@ -0,0 +1,62 @@ +goog.provide('ol.test.structs.RTree'); + + +describe('ol.structs.RTree', function() { + + describe('put and find', function() { + var rTree = new ol.structs.RTree(); + rTree.put(new ol.Rectangle(0, 0, 1, 1), 1); + rTree.put(new ol.Rectangle(1, 1, 4, 4), 2); + rTree.put(new ol.Rectangle(2, 2, 3, 3), 3); + rTree.put(new ol.Rectangle(-5, -5, -4, -4), 4); + rTree.put(new ol.Rectangle(-4, -4, -1, -1), 5); + rTree.put(new ol.Rectangle(-3, -3, -2, -2), 6); + + it('stores items', function() { + expect(goog.object.getCount(rTree.find(new ol.Rectangle( + Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, + Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY)))).toBe(6); + }); + + it('filters by rectangle', function() { + var result; + result = goog.object.getValues(rTree.find(new ol.Rectangle(2, 2, 3, 3))); + expect(result).toContain(2); + expect(result).toContain(3); + expect(result.length).toBe(2); + result = goog.object.getValues( + rTree.find(new ol.Rectangle(-1, -1, 2, 2))); + expect(result).toContain(1); + expect(result).toContain(2); + expect(result).toContain(3); + expect(result).toContain(5); + expect(result.length).toBe(4); + expect(goog.object.getCount(rTree.find(new ol.Rectangle(5, 5, 6, 6)))) + .toBe(0); + }); + + it('can store thosands of items and find fast', function() { + for (var i = 7; i <= 10000; ++i) { + rTree.put(new ol.Rectangle(Math.random() * -10, Math.random() * -10, + Math.random() * 10, Math.random() * 10), i); + } + expect(goog.object.getCount( + rTree.find(new ol.Rectangle(-10, -10, 10, 10)))).toBe(10000); + var result = rTree.find(new ol.Rectangle(0, 0, 0, 0)); + expect(goog.object.getCount(result)).toBe(9995); + var values = goog.object.getValues(result); + expect(values).toContain(1); + expect(values).not.toContain(2); + expect(values).not.toContain(3); + expect(values).not.toContain(4); + expect(values).not.toContain(5); + expect(values).not.toContain(6); + expect(values).toContain(7); + }); + + }); + +}); + +goog.require('ol.Rectangle'); +goog.require('ol.structs.RTree'); From f6c93d85b0a9d3a57cfdf5d332e664792495f8f2 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 19 Feb 2013 14:12:38 +0100 Subject: [PATCH 071/180] Making linter happy --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 6d6e2a1aab..7801bd1cd9 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -200,7 +200,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = if (goog.isNull(tileGrid)) { // lazy tile source creation to match the view projection tileGrid = ol.tilegrid.createForProjection( - view2DState.projection, + view2DState.projection, 22, // should be no harm in going big here - ideally, it would be ∞ new ol.Size(512, 512)); this.tileGrid_ = tileGrid; From 184e1e782c7496262222c3c37266ffa94bc5e6e0 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 19 Feb 2013 07:19:31 -0700 Subject: [PATCH 072/180] Spare space --- test/spec/ol/structs/rtree.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/ol/structs/rtree.test.js b/test/spec/ol/structs/rtree.test.js index f307be320e..dc8581262a 100644 --- a/test/spec/ol/structs/rtree.test.js +++ b/test/spec/ol/structs/rtree.test.js @@ -34,7 +34,7 @@ describe('ol.structs.RTree', function() { expect(goog.object.getCount(rTree.find(new ol.Rectangle(5, 5, 6, 6)))) .toBe(0); }); - + it('can store thosands of items and find fast', function() { for (var i = 7; i <= 10000; ++i) { rTree.put(new ol.Rectangle(Math.random() * -10, Math.random() * -10, From 360e37146cf29b6eacb1ed3a3df10c1005d27f36 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 19 Feb 2013 21:37:40 -0700 Subject: [PATCH 073/180] Rule is filter and symbolizer, style is collection of rules --- src/ol/style/rule.js | 34 ++++++++++++++++++++++++++++++++++ src/ol/style/style.js | 25 +++++++++++++++++++++++++ src/ol/style/symbolizer.js | 17 +++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 src/ol/style/rule.js create mode 100644 src/ol/style/style.js diff --git a/src/ol/style/rule.js b/src/ol/style/rule.js new file mode 100644 index 0000000000..90e7ae8b00 --- /dev/null +++ b/src/ol/style/rule.js @@ -0,0 +1,34 @@ +goog.provide('ol.style.Rule'); + +goog.require('ol.filter.Filter'); +goog.require('ol.style.Symbolizer'); + + +/** + * @typedef {{filter: (ol.filter.Filter), + * symbolizers: (Array.)}} + */ +ol.style.RuleOptions; + + + +/** + * @constructor + * @param {ol.style.RuleOptions} options Rule options. + */ +ol.style.Rule = function(options) { + + /** + * @type {ol.filter.Filter} + * @private + */ + this.filter_ = goog.isDef(options.filter) ? options.filter : null; + + /** + * @type {Array.} + * @private + */ + this.symbolizers_ = goog.isDef(options.symbolizers) ? + options.symbolizers : []; + +}; diff --git a/src/ol/style/style.js b/src/ol/style/style.js new file mode 100644 index 0000000000..536c8dac6c --- /dev/null +++ b/src/ol/style/style.js @@ -0,0 +1,25 @@ +goog.provide('ol.style.Style'); + +goog.require('ol.style.Rule'); + + +/** + * @typedef {{rules: (Array.)}} + */ +ol.style.StyleOptions; + + + +/** + * @constructor + * @param {ol.style.StyleOptions} options Style options. + */ +ol.style.Style = function(options) { + + /** + * @type {Array.} + * @private + */ + this.rules_ = goog.isDef(options.rules) ? options.rules : []; + +}; diff --git a/src/ol/style/symbolizer.js b/src/ol/style/symbolizer.js index 1959afa171..432b0bbcac 100644 --- a/src/ol/style/symbolizer.js +++ b/src/ol/style/symbolizer.js @@ -1,4 +1,7 @@ goog.provide('ol.style.LiteralSymbolizer'); +goog.provide('ol.style.Symbolizer'); + +goog.require('ol.Feature'); @@ -6,3 +9,17 @@ goog.provide('ol.style.LiteralSymbolizer'); * @interface */ ol.style.LiteralSymbolizer = function() {}; + + + +/** + * @interface + */ +ol.style.Symbolizer = function() {}; + + +/** + * @param {ol.Feature} feature Feature for evaluating expressions. + * @return {ol.style.LiteralSymbolizer} Literal symbolizer. + */ +ol.style.Symbolizer.prototype.createLiteral = function(feature) {}; From 1faa6214f11969a971914981a32822fce358a0b2 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 19 Feb 2013 21:38:10 -0700 Subject: [PATCH 074/180] Expression literal --- src/ol/expression.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/ol/expression.js b/src/ol/expression.js index 8cab09b88d..4adf62be27 100644 --- a/src/ol/expression.js +++ b/src/ol/expression.js @@ -1,4 +1,5 @@ goog.provide('ol.Expression'); +goog.provide('ol.ExpressionLiteral'); @@ -41,3 +42,29 @@ ol.Expression.prototype.evaluate = function(opt_thisArg, opt_scope) { var evaluator = new Function(names.join(','), 'return ' + this.source_); return evaluator.apply(thisArg, values); }; + + + +/** + * @constructor + * @extends {ol.Expression} + * @param {*} value Literal value. + */ +ol.ExpressionLiteral = function(value) { + + /** + * @type {*} + * @private + */ + this.value_ = value; + +}; +goog.inherits(ol.ExpressionLiteral, ol.Expression); + + +/** + * @inheritDoc + */ +ol.ExpressionLiteral.prototype.evaluate = function(opt_thisArg, opt_scope) { + return this.value_; +}; From 7141721bb1535a539a9b1394bbd2ae65843f22e9 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 19 Feb 2013 22:24:21 -0700 Subject: [PATCH 075/180] Symbolizers with method for creating literals --- src/ol/feature.js | 18 ++++ src/ol/style/line.js | 88 +++++++++++++++++++- src/ol/style/point.js | 16 ++++ src/ol/style/polygon.js | 103 ++++++++++++++++++++++- src/ol/style/shape.js | 128 ++++++++++++++++++++++++++++- test/spec/ol/style/line.test.js | 51 ++++++++++++ test/spec/ol/style/polygon.test.js | 51 ++++++++++++ test/spec/ol/style/shape.test.js | 51 ++++++++++++ 8 files changed, 500 insertions(+), 6 deletions(-) create mode 100644 test/spec/ol/style/line.test.js create mode 100644 test/spec/ol/style/polygon.test.js create mode 100644 test/spec/ol/style/shape.test.js diff --git a/src/ol/feature.js b/src/ol/feature.js index c3c9b66efe..f4d46ac08a 100644 --- a/src/ol/feature.js +++ b/src/ol/feature.js @@ -25,6 +25,24 @@ ol.Feature = function(opt_geometry, opt_values) { goog.inherits(ol.Feature, ol.Object); +/** + * @return {Object} Attributes object. + */ +ol.Feature.prototype.getAttributes = function() { + // TODO: see https://github.com/openlayers/ol3/pull/217 + // var keys = this.getKeys(), + // len = keys.length, + // attributes = {}, + // i, key + // for (var i = 0; i < len; ++ i) { + // key = keys[i]; + // attributes[key] = this.get(key); + // } + // return attributes; + return this; +}; + + /** * @return {ol.geom.Geometry} The geometry (or null if none). */ diff --git a/src/ol/style/line.js b/src/ol/style/line.js index e3bc7352c7..393affc669 100644 --- a/src/ol/style/line.js +++ b/src/ol/style/line.js @@ -1,6 +1,10 @@ +goog.provide('ol.style.Line'); goog.provide('ol.style.LiteralLine'); +goog.require('ol.Expression'); +goog.require('ol.ExpressionLiteral'); goog.require('ol.style.LiteralSymbolizer'); +goog.require('ol.style.Symbolizer'); /** @@ -8,14 +12,14 @@ goog.require('ol.style.LiteralSymbolizer'); * strokeWidth: (number), * opacity: (number)}} */ -ol.style.LiteralLineConfig; +ol.style.LiteralLineOptions; /** * @constructor * @implements {ol.style.LiteralSymbolizer} - * @param {ol.style.LiteralLineConfig} config Symbolizer properties. + * @param {ol.style.LiteralLineOptions} config Symbolizer properties. */ ol.style.LiteralLine = function(config) { @@ -29,3 +33,83 @@ ol.style.LiteralLine = function(config) { this.opacity = config.opacity; }; + + +/** + * @typedef {{strokeStyle: (string|ol.Expression), + * strokeWidth: (number|ol.Expression), + * opacity: (number|ol.Expression)}} + */ +ol.style.LineOptions; + + + +/** + * @constructor + * @implements {ol.style.Symbolizer} + * @param {ol.style.LineOptions} options Symbolizer properties. + */ +ol.style.Line = function(options) { + + /** + * @type {ol.Expression} + * @private + */ + this.strokeStyle_ = !goog.isDef(options.strokeStyle) ? + new ol.ExpressionLiteral(ol.style.LineDefaults.strokeStyle) : + (options.strokeStyle instanceof ol.Expression) ? + options.strokeStyle : new ol.ExpressionLiteral(options.strokeStyle); + + /** + * @type {ol.Expression} + * @private + */ + this.strokeWidth_ = !goog.isDef(options.strokeWidth) ? + new ol.ExpressionLiteral(ol.style.LineDefaults.strokeWidth) : + (options.strokeWidth instanceof ol.Expression) ? + options.strokeWidth : new ol.ExpressionLiteral(options.strokeWidth); + + /** + * @type {ol.Expression} + * @private + */ + this.opacity_ = !goog.isDef(options.opacity) ? + new ol.ExpressionLiteral(ol.style.LineDefaults.opacity) : + (options.opacity instanceof ol.Expression) ? + options.opacity : new ol.ExpressionLiteral(options.opacity); + +}; + + +/** + * @inheritDoc + * @return {ol.style.LiteralLine} Literal line symbolizer. + */ +ol.style.Line.prototype.createLiteral = function(feature) { + var attrs = feature.getAttributes(); + + var strokeStyle = this.strokeStyle_.evaluate(feature, attrs); + goog.asserts.assertString(strokeStyle, 'strokeStyle must be a string'); + + var strokeWidth = this.strokeWidth_.evaluate(feature, attrs); + goog.asserts.assertNumber(strokeWidth, 'strokeWidth must be a number'); + + var opacity = this.opacity_.evaluate(feature, attrs); + goog.asserts.assertNumber(opacity, 'opacity must be a number'); + + return new ol.style.LiteralLine({ + strokeStyle: strokeStyle, + strokeWidth: strokeWidth, + opacity: opacity + }); +}; + + +/** + * @type {ol.style.LiteralLine} + */ +ol.style.LineDefaults = new ol.style.LiteralLine({ + strokeStyle: '#696969', + strokeWidth: 1.5, + opacity: 0.75 +}); diff --git a/src/ol/style/point.js b/src/ol/style/point.js index acbed220c4..faec50af16 100644 --- a/src/ol/style/point.js +++ b/src/ol/style/point.js @@ -1,6 +1,8 @@ goog.provide('ol.style.LiteralPoint'); +goog.provide('ol.style.Point'); goog.require('ol.style.LiteralSymbolizer'); +goog.require('ol.style.Symbolizer'); @@ -9,3 +11,17 @@ goog.require('ol.style.LiteralSymbolizer'); * @implements {ol.style.LiteralSymbolizer} */ ol.style.LiteralPoint = function() {}; + + + +/** + * @constructor + * @implements {ol.style.Symbolizer} + */ +ol.style.Point = function() {}; + + +/** + * @inheritDoc + */ +ol.style.Point.prototype.createLiteral = goog.abstractMethod; diff --git a/src/ol/style/polygon.js b/src/ol/style/polygon.js index 160cdfa310..3ada41fc67 100644 --- a/src/ol/style/polygon.js +++ b/src/ol/style/polygon.js @@ -1,6 +1,10 @@ goog.provide('ol.style.LiteralPolygon'); +goog.provide('ol.style.Polygon'); +goog.require('ol.Expression'); +goog.require('ol.ExpressionLiteral'); goog.require('ol.style.LiteralSymbolizer'); +goog.require('ol.style.Symbolizer'); /** @@ -9,14 +13,14 @@ goog.require('ol.style.LiteralSymbolizer'); * strokeWidth: (number), * opacity: (number)}} */ -ol.style.LiteralPolygonConfig; +ol.style.LiteralPolygonOptions; /** * @constructor * @implements {ol.style.LiteralSymbolizer} - * @param {ol.style.LiteralPolygonConfig} config Symbolizer properties. + * @param {ol.style.LiteralPolygonOptions} config Symbolizer properties. */ ol.style.LiteralPolygon = function(config) { @@ -33,3 +37,98 @@ ol.style.LiteralPolygon = function(config) { this.opacity = config.opacity; }; + + +/** + * @typedef {{fillStyle: (string|ol.Expression), + * strokeStyle: (string|ol.Expression), + * strokeWidth: (number|ol.Expression), + * opacity: (number|ol.Expression)}} + */ +ol.style.PolygonOptions; + + + +/** + * @constructor + * @implements {ol.style.Symbolizer} + * @param {ol.style.PolygonOptions} options Symbolizer properties. + */ +ol.style.Polygon = function(options) { + + /** + * @type {ol.Expression} + * @private + */ + this.fillStyle_ = !goog.isDef(options.fillStyle) ? + new ol.ExpressionLiteral(ol.style.PolygonDefaults.fillStyle) : + (options.fillStyle instanceof ol.Expression) ? + options.fillStyle : new ol.ExpressionLiteral(options.fillStyle); + + /** + * @type {ol.Expression} + * @private + */ + this.strokeStyle_ = !goog.isDef(options.strokeStyle) ? + new ol.ExpressionLiteral(ol.style.PolygonDefaults.strokeStyle) : + (options.strokeStyle instanceof ol.Expression) ? + options.strokeStyle : new ol.ExpressionLiteral(options.strokeStyle); + + /** + * @type {ol.Expression} + * @private + */ + this.strokeWidth_ = !goog.isDef(options.strokeWidth) ? + new ol.ExpressionLiteral(ol.style.PolygonDefaults.strokeWidth) : + (options.strokeWidth instanceof ol.Expression) ? + options.strokeWidth : new ol.ExpressionLiteral(options.strokeWidth); + + /** + * @type {ol.Expression} + * @private + */ + this.opacity_ = !goog.isDef(options.opacity) ? + new ol.ExpressionLiteral(ol.style.PolygonDefaults.opacity) : + (options.opacity instanceof ol.Expression) ? + options.opacity : new ol.ExpressionLiteral(options.opacity); + +}; + + +/** + * @inheritDoc + * @return {ol.style.LiteralPolygon} Literal shape symbolizer. + */ +ol.style.Polygon.prototype.createLiteral = function(feature) { + var attrs = feature.getAttributes(); + + var fillStyle = this.fillStyle_.evaluate(feature, attrs); + goog.asserts.assertString(fillStyle, 'fillStyle must be a string'); + + var strokeStyle = this.strokeStyle_.evaluate(feature, attrs); + goog.asserts.assertString(strokeStyle, 'strokeStyle must be a string'); + + var strokeWidth = this.strokeWidth_.evaluate(feature, attrs); + goog.asserts.assertNumber(strokeWidth, 'strokeWidth must be a number'); + + var opacity = this.opacity_.evaluate(feature, attrs); + goog.asserts.assertNumber(opacity, 'opacity must be a number'); + + return new ol.style.LiteralPolygon({ + fillStyle: fillStyle, + strokeStyle: strokeStyle, + strokeWidth: strokeWidth, + opacity: opacity + }); +}; + + +/** + * @type {ol.style.LiteralPolygon} + */ +ol.style.PolygonDefaults = new ol.style.LiteralPolygon({ + fillStyle: '#ffffff', + strokeStyle: '#696969', + strokeWidth: 1.5, + opacity: 0.75 +}); diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js index e188d8c354..ce07f4bee9 100644 --- a/src/ol/style/shape.js +++ b/src/ol/style/shape.js @@ -1,7 +1,11 @@ goog.provide('ol.style.LiteralShape'); +goog.provide('ol.style.Shape'); goog.provide('ol.style.ShapeType'); +goog.require('ol.Expression'); +goog.require('ol.ExpressionLiteral'); goog.require('ol.style.LiteralPoint'); +goog.require('ol.style.Point'); /** @@ -20,14 +24,14 @@ ol.style.ShapeType = { * strokeWidth: (number), * opacity: (number)}} */ -ol.style.LiteralShapeConfig; +ol.style.LiteralShapeOptions; /** * @constructor * @extends {ol.style.LiteralPoint} - * @param {ol.style.LiteralShapeConfig} config Symbolizer properties. + * @param {ol.style.LiteralShapeOptions} config Symbolizer properties. */ ol.style.LiteralShape = function(config) { @@ -51,3 +55,123 @@ ol.style.LiteralShape = function(config) { }; goog.inherits(ol.style.LiteralShape, ol.style.LiteralPoint); + + +/** + * @typedef {{type: (ol.style.ShapeType), + * size: (number|ol.Expression), + * fillStyle: (string|ol.Expression), + * strokeStyle: (string|ol.Expression), + * strokeWidth: (number|ol.Expression), + * opacity: (number|ol.Expression)}} + */ +ol.style.ShapeOptions; + + + +/** + * @constructor + * @extends {ol.style.Point} + * @param {ol.style.ShapeOptions} options Symbolizer properties. + */ +ol.style.Shape = function(options) { + + /** + * @type {ol.style.ShapeType} + * @private + */ + this.type_ = /** @type {ol.style.ShapeType} */ goog.isDef(options.type) ? + options.type : ol.style.ShapeDefaults.type; + + /** + * @type {ol.Expression} + * @private + */ + this.size_ = !goog.isDef(options.size) ? + new ol.ExpressionLiteral(ol.style.ShapeDefaults.size) : + (options.size instanceof ol.Expression) ? + options.size : new ol.ExpressionLiteral(options.size); + + /** + * @type {ol.Expression} + * @private + */ + this.fillStyle_ = !goog.isDef(options.fillStyle) ? + new ol.ExpressionLiteral(ol.style.ShapeDefaults.fillStyle) : + (options.fillStyle instanceof ol.Expression) ? + options.fillStyle : new ol.ExpressionLiteral(options.fillStyle); + + /** + * @type {ol.Expression} + * @private + */ + this.strokeStyle_ = !goog.isDef(options.strokeStyle) ? + new ol.ExpressionLiteral(ol.style.ShapeDefaults.strokeStyle) : + (options.strokeStyle instanceof ol.Expression) ? + options.strokeStyle : new ol.ExpressionLiteral(options.strokeStyle); + + /** + * @type {ol.Expression} + * @private + */ + this.strokeWidth_ = !goog.isDef(options.strokeWidth) ? + new ol.ExpressionLiteral(ol.style.ShapeDefaults.strokeWidth) : + (options.strokeWidth instanceof ol.Expression) ? + options.strokeWidth : new ol.ExpressionLiteral(options.strokeWidth); + + /** + * @type {ol.Expression} + * @private + */ + this.opacity_ = !goog.isDef(options.opacity) ? + new ol.ExpressionLiteral(ol.style.ShapeDefaults.opacity) : + (options.opacity instanceof ol.Expression) ? + options.opacity : new ol.ExpressionLiteral(options.opacity); + +}; + + +/** + * @inheritDoc + * @return {ol.style.LiteralShape} Literal shape symbolizer. + */ +ol.style.Shape.prototype.createLiteral = function(feature) { + var attrs = feature.getAttributes(); + + var size = this.size_.evaluate(feature, attrs); + goog.asserts.assertNumber(size, 'size must be a number'); + + var fillStyle = this.fillStyle_.evaluate(feature, attrs); + goog.asserts.assertString(fillStyle, 'fillStyle must be a string'); + + var strokeStyle = this.strokeStyle_.evaluate(feature, attrs); + goog.asserts.assertString(strokeStyle, 'strokeStyle must be a string'); + + var strokeWidth = this.strokeWidth_.evaluate(feature, attrs); + goog.asserts.assertNumber(strokeWidth, 'strokeWidth must be a number'); + + var opacity = this.opacity_.evaluate(feature, attrs); + goog.asserts.assertNumber(opacity, 'opacity must be a number'); + + return new ol.style.LiteralShape({ + type: this.type_, + size: size, + fillStyle: fillStyle, + strokeStyle: strokeStyle, + strokeWidth: strokeWidth, + opacity: opacity + }); +}; + + +/** + * @type {ol.style.LiteralShape} + */ +ol.style.ShapeDefaults = new ol.style.LiteralShape({ + type: ol.style.ShapeType.CIRCLE, + size: 5, + fillStyle: '#ffffff', + strokeStyle: '#696969', + strokeWidth: 1.5, + opacity: 0.75 +}); diff --git a/test/spec/ol/style/line.test.js b/test/spec/ol/style/line.test.js new file mode 100644 index 0000000000..c7ffdc177c --- /dev/null +++ b/test/spec/ol/style/line.test.js @@ -0,0 +1,51 @@ +goog.provide('ol.test.style.Line'); + +describe('ol.style.Line', function() { + + describe('constructor', function() { + + it('accepts literal values', function() { + var symbolizer = new ol.style.Line({ + strokeStyle: '#BADA55', + strokeWidth: 3 + }); + expect(symbolizer).toBeA(ol.style.Line); + }); + + it('accepts expressions', function() { + var symbolizer = new ol.style.Line({ + opacity: new ol.Expression('value / 100'), + strokeWidth: ol.Expression('widthAttr') + }); + expect(symbolizer).toBeA(ol.style.Line); + }); + + }); + + describe('#createLiteral()', function() { + + it('evaluates expressions with the given feature', function() { + var symbolizer = new ol.style.Line({ + opacity: new ol.Expression('value / 100'), + strokeWidth: ol.Expression('widthAttr') + }); + + var feature = new ol.Feature(undefined, { + value: 42, + widthAttr: 1.5 + }); + + var literal = symbolizer.createLiteral(feature); + expect(literal).toBeA(ol.style.LiteralLine); + expect(literal.opacity).toBe(42 / 100); + expect(literal.strokeWidth).toBe(1.5); + }); + + }); + +}); + +goog.require('ol.Expression'); +goog.require('ol.Feature'); +goog.require('ol.style.Line'); +goog.require('ol.style.LiteralLine'); diff --git a/test/spec/ol/style/polygon.test.js b/test/spec/ol/style/polygon.test.js new file mode 100644 index 0000000000..52f639a324 --- /dev/null +++ b/test/spec/ol/style/polygon.test.js @@ -0,0 +1,51 @@ +goog.provide('ol.test.style.Polygon'); + +describe('ol.style.Polygon', function() { + + describe('constructor', function() { + + it('accepts literal values', function() { + var symbolizer = new ol.style.Polygon({ + fillStyle: '#BADA55', + strokeWidth: 3 + }); + expect(symbolizer).toBeA(ol.style.Polygon); + }); + + it('accepts expressions', function() { + var symbolizer = new ol.style.Polygon({ + opacity: new ol.Expression('value / 100'), + fillStyle: ol.Expression('fillAttr') + }); + expect(symbolizer).toBeA(ol.style.Polygon); + }); + + }); + + describe('#createLiteral()', function() { + + it('evaluates expressions with the given feature', function() { + var symbolizer = new ol.style.Polygon({ + opacity: new ol.Expression('value / 100'), + fillStyle: new ol.Expression('fillAttr') + }); + + var feature = new ol.Feature(undefined, { + value: 42, + fillAttr: '#ff0000' + }); + + var literal = symbolizer.createLiteral(feature); + expect(literal).toBeA(ol.style.LiteralPolygon); + expect(literal.opacity).toBe(42 / 100); + expect(literal.fillStyle).toBe('#ff0000'); + }); + + }); + +}); + +goog.require('ol.Expression'); +goog.require('ol.Feature'); +goog.require('ol.style.Polygon'); +goog.require('ol.style.LiteralPolygon'); diff --git a/test/spec/ol/style/shape.test.js b/test/spec/ol/style/shape.test.js new file mode 100644 index 0000000000..17835fb047 --- /dev/null +++ b/test/spec/ol/style/shape.test.js @@ -0,0 +1,51 @@ +goog.provide('ol.test.style.Shape'); + +describe('ol.style.Shape', function() { + + describe('constructor', function() { + + it('accepts literal values', function() { + var symbolizer = new ol.style.Shape({ + size: 4, + fillStyle: '#BADA55' + }); + expect(symbolizer).toBeA(ol.style.Shape); + }); + + it('accepts expressions', function() { + var symbolizer = new ol.style.Shape({ + size: new ol.Expression('sizeAttr'), + strokeStyle: ol.Expression('color') + }); + expect(symbolizer).toBeA(ol.style.Shape); + }); + + }); + + describe('#createLiteral()', function() { + + it('evaluates expressions with the given feature', function() { + var symbolizer = new ol.style.Shape({ + size: new ol.Expression('sizeAttr'), + opacity: new ol.Expression('opacityAttr') + }); + + var feature = new ol.Feature(undefined, { + sizeAttr: 42, + opacityAttr: 0.4 + }); + + var literal = symbolizer.createLiteral(feature); + expect(literal).toBeA(ol.style.LiteralShape); + expect(literal.size).toBe(42); + expect(literal.opacity).toBe(0.4); + }); + + }); + +}); + +goog.require('ol.Expression'); +goog.require('ol.Feature'); +goog.require('ol.style.Shape'); +goog.require('ol.style.LiteralShape'); From 438664a1905e827ba449f1d78093dfe556e721db Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 19 Feb 2013 22:35:29 -0700 Subject: [PATCH 076/180] Renaming literals --- src/ol/renderer/canvas/canvasrenderer.js | 36 +++++++++---------- .../canvas/canvasvectorlayerrenderer.js | 12 +++---- src/ol/style/line.js | 20 +++++------ src/ol/style/point.js | 8 ++--- src/ol/style/polygon.js | 20 +++++------ src/ol/style/shape.js | 22 ++++++------ src/ol/style/symbolizer.js | 6 ++-- test/spec/ol/style/line.test.js | 4 +-- test/spec/ol/style/polygon.test.js | 4 +-- test/spec/ol/style/shape.test.js | 4 +-- 10 files changed, 68 insertions(+), 68 deletions(-) diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index 3ccb400144..83689f42db 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -8,12 +8,12 @@ goog.require('ol.Pixel'); goog.require('ol.canvas'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.GeometryType'); -goog.require('ol.style.LiteralLine'); -goog.require('ol.style.LiteralPoint'); -goog.require('ol.style.LiteralPolygon'); -goog.require('ol.style.LiteralShape'); -goog.require('ol.style.LiteralSymbolizer'); +goog.require('ol.style.LineLiteral'); +goog.require('ol.style.PointLiteral'); +goog.require('ol.style.PolygonLiteral'); +goog.require('ol.style.ShapeLiteral'); goog.require('ol.style.ShapeType'); +goog.require('ol.style.SymbolizerLiteral'); /** @@ -72,25 +72,25 @@ ol.renderer.canvas.Renderer = function(canvas, transform, opt_offset) { /** * @param {ol.geom.GeometryType} type Geometry type. * @param {Array.} features Array of features. - * @param {ol.style.LiteralSymbolizer} symbolizer Symbolizer. + * @param {ol.style.SymbolizerLiteral} symbolizer Symbolizer. */ ol.renderer.canvas.Renderer.prototype.renderFeaturesByGeometryType = function(type, features, symbolizer) { switch (type) { case ol.geom.GeometryType.POINT: - goog.asserts.assert(symbolizer instanceof ol.style.LiteralPoint); + goog.asserts.assert(symbolizer instanceof ol.style.PointLiteral); this.renderPointFeatures_( - features, /** @type {ol.style.LiteralPoint} */ (symbolizer)); + features, /** @type {ol.style.PointLiteral} */ (symbolizer)); break; case ol.geom.GeometryType.LINESTRING: - goog.asserts.assert(symbolizer instanceof ol.style.LiteralLine); + goog.asserts.assert(symbolizer instanceof ol.style.LineLiteral); this.renderLineStringFeatures_( - features, /** @type {ol.style.LiteralLine} */ (symbolizer)); + features, /** @type {ol.style.LineLiteral} */ (symbolizer)); break; case ol.geom.GeometryType.POLYGON: - goog.asserts.assert(symbolizer instanceof ol.style.LiteralPolygon); + goog.asserts.assert(symbolizer instanceof ol.style.PolygonLiteral); this.renderPolygonFeatures_( - features, /** @type {ol.style.LiteralPolygon} */ (symbolizer)); + features, /** @type {ol.style.PolygonLiteral} */ (symbolizer)); break; default: throw new Error('Rendering not implemented for geometry type: ' + type); @@ -100,7 +100,7 @@ ol.renderer.canvas.Renderer.prototype.renderFeaturesByGeometryType = /** * @param {Array.} features Array of line features. - * @param {ol.style.LiteralLine} symbolizer Line symbolizer. + * @param {ol.style.LineLiteral} symbolizer Line symbolizer. * @private */ ol.renderer.canvas.Renderer.prototype.renderLineStringFeatures_ = @@ -135,7 +135,7 @@ ol.renderer.canvas.Renderer.prototype.renderLineStringFeatures_ = /** * @param {Array.} features Array of point features. - * @param {ol.style.LiteralPoint} symbolizer Point symbolizer. + * @param {ol.style.PointLiteral} symbolizer Point symbolizer. * @private */ ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = @@ -144,7 +144,7 @@ ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = var context = this.context_, canvas, i, ii, coords, vec; - if (symbolizer instanceof ol.style.LiteralShape) { + if (symbolizer instanceof ol.style.ShapeLiteral) { canvas = ol.renderer.canvas.Renderer.renderShape(symbolizer); } else { throw new Error('Unsupported symbolizer: ' + symbolizer); @@ -166,7 +166,7 @@ ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = /** * @param {Array.} features Array of polygon features. - * @param {ol.style.LiteralPolygon} symbolizer Polygon symbolizer. + * @param {ol.style.PolygonLiteral} symbolizer Polygon symbolizer. * @private */ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = @@ -237,7 +237,7 @@ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = /** - * @param {ol.style.LiteralShape} circle Shape symbolizer. + * @param {ol.style.ShapeLiteral} circle Shape symbolizer. * @return {!HTMLCanvasElement} Canvas element. * @private */ @@ -279,7 +279,7 @@ ol.renderer.canvas.Renderer.renderCircle_ = function(circle) { /** - * @param {ol.style.LiteralShape} shape Shape symbolizer. + * @param {ol.style.ShapeLiteral} shape Shape symbolizer. * @return {!HTMLCanvasElement} Canvas element. */ ol.renderer.canvas.Renderer.renderShape = function(shape) { diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index a170a5b769..5efe58ade3 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -14,9 +14,9 @@ goog.require('ol.geom.GeometryType'); goog.require('ol.layer.Vector'); goog.require('ol.renderer.canvas.Layer'); goog.require('ol.renderer.canvas.Renderer'); -goog.require('ol.style.LiteralLine'); -goog.require('ol.style.LiteralPolygon'); -goog.require('ol.style.LiteralShape'); +goog.require('ol.style.LineLiteral'); +goog.require('ol.style.PolygonLiteral'); +goog.require('ol.style.ShapeLiteral'); goog.require('ol.style.ShapeType'); goog.require('ol.tilegrid.TileGrid'); @@ -118,7 +118,7 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { // TODO: implement layer.setStyle(style) where style is a set of rules // and a rule has a filter and array of symbolizers var symbolizers = {}; - symbolizers[ol.geom.GeometryType.POINT] = new ol.style.LiteralShape({ + symbolizers[ol.geom.GeometryType.POINT] = new ol.style.ShapeLiteral({ type: ol.style.ShapeType.CIRCLE, size: 10, fillStyle: '#ffcc99', @@ -126,12 +126,12 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { strokeWidth: 2, opacity: 0.75 }); - symbolizers[ol.geom.GeometryType.LINESTRING] = new ol.style.LiteralLine({ + symbolizers[ol.geom.GeometryType.LINESTRING] = new ol.style.LineLiteral({ strokeStyle: '#ff9933', strokeWidth: 2, opacity: 1 }); - symbolizers[ol.geom.GeometryType.POLYGON] = new ol.style.LiteralPolygon({ + symbolizers[ol.geom.GeometryType.POLYGON] = new ol.style.PolygonLiteral({ fillStyle: '#ffcc99', strokeStyle: '#ff9933', strokeWidth: 2, diff --git a/src/ol/style/line.js b/src/ol/style/line.js index 393affc669..32a5987ec4 100644 --- a/src/ol/style/line.js +++ b/src/ol/style/line.js @@ -1,10 +1,10 @@ goog.provide('ol.style.Line'); -goog.provide('ol.style.LiteralLine'); +goog.provide('ol.style.LineLiteral'); goog.require('ol.Expression'); goog.require('ol.ExpressionLiteral'); -goog.require('ol.style.LiteralSymbolizer'); goog.require('ol.style.Symbolizer'); +goog.require('ol.style.SymbolizerLiteral'); /** @@ -12,16 +12,16 @@ goog.require('ol.style.Symbolizer'); * strokeWidth: (number), * opacity: (number)}} */ -ol.style.LiteralLineOptions; +ol.style.LineLiteralOptions; /** * @constructor - * @implements {ol.style.LiteralSymbolizer} - * @param {ol.style.LiteralLineOptions} config Symbolizer properties. + * @implements {ol.style.SymbolizerLiteral} + * @param {ol.style.LineLiteralOptions} config Symbolizer properties. */ -ol.style.LiteralLine = function(config) { +ol.style.LineLiteral = function(config) { /** @type {string} */ this.strokeStyle = config.strokeStyle; @@ -83,7 +83,7 @@ ol.style.Line = function(options) { /** * @inheritDoc - * @return {ol.style.LiteralLine} Literal line symbolizer. + * @return {ol.style.LineLiteral} Literal line symbolizer. */ ol.style.Line.prototype.createLiteral = function(feature) { var attrs = feature.getAttributes(); @@ -97,7 +97,7 @@ ol.style.Line.prototype.createLiteral = function(feature) { var opacity = this.opacity_.evaluate(feature, attrs); goog.asserts.assertNumber(opacity, 'opacity must be a number'); - return new ol.style.LiteralLine({ + return new ol.style.LineLiteral({ strokeStyle: strokeStyle, strokeWidth: strokeWidth, opacity: opacity @@ -106,9 +106,9 @@ ol.style.Line.prototype.createLiteral = function(feature) { /** - * @type {ol.style.LiteralLine} + * @type {ol.style.LineLiteral} */ -ol.style.LineDefaults = new ol.style.LiteralLine({ +ol.style.LineDefaults = new ol.style.LineLiteral({ strokeStyle: '#696969', strokeWidth: 1.5, opacity: 0.75 diff --git a/src/ol/style/point.js b/src/ol/style/point.js index faec50af16..b153203d84 100644 --- a/src/ol/style/point.js +++ b/src/ol/style/point.js @@ -1,16 +1,16 @@ -goog.provide('ol.style.LiteralPoint'); goog.provide('ol.style.Point'); +goog.provide('ol.style.PointLiteral'); -goog.require('ol.style.LiteralSymbolizer'); goog.require('ol.style.Symbolizer'); +goog.require('ol.style.SymbolizerLiteral'); /** * @constructor - * @implements {ol.style.LiteralSymbolizer} + * @implements {ol.style.SymbolizerLiteral} */ -ol.style.LiteralPoint = function() {}; +ol.style.PointLiteral = function() {}; diff --git a/src/ol/style/polygon.js b/src/ol/style/polygon.js index 3ada41fc67..e2b2d52ba2 100644 --- a/src/ol/style/polygon.js +++ b/src/ol/style/polygon.js @@ -1,10 +1,10 @@ -goog.provide('ol.style.LiteralPolygon'); goog.provide('ol.style.Polygon'); +goog.provide('ol.style.PolygonLiteral'); goog.require('ol.Expression'); goog.require('ol.ExpressionLiteral'); -goog.require('ol.style.LiteralSymbolizer'); goog.require('ol.style.Symbolizer'); +goog.require('ol.style.SymbolizerLiteral'); /** @@ -13,16 +13,16 @@ goog.require('ol.style.Symbolizer'); * strokeWidth: (number), * opacity: (number)}} */ -ol.style.LiteralPolygonOptions; +ol.style.PolygonLiteralOptions; /** * @constructor - * @implements {ol.style.LiteralSymbolizer} - * @param {ol.style.LiteralPolygonOptions} config Symbolizer properties. + * @implements {ol.style.SymbolizerLiteral} + * @param {ol.style.PolygonLiteralOptions} config Symbolizer properties. */ -ol.style.LiteralPolygon = function(config) { +ol.style.PolygonLiteral = function(config) { /** @type {string} */ this.fillStyle = config.fillStyle; @@ -97,7 +97,7 @@ ol.style.Polygon = function(options) { /** * @inheritDoc - * @return {ol.style.LiteralPolygon} Literal shape symbolizer. + * @return {ol.style.PolygonLiteral} Literal shape symbolizer. */ ol.style.Polygon.prototype.createLiteral = function(feature) { var attrs = feature.getAttributes(); @@ -114,7 +114,7 @@ ol.style.Polygon.prototype.createLiteral = function(feature) { var opacity = this.opacity_.evaluate(feature, attrs); goog.asserts.assertNumber(opacity, 'opacity must be a number'); - return new ol.style.LiteralPolygon({ + return new ol.style.PolygonLiteral({ fillStyle: fillStyle, strokeStyle: strokeStyle, strokeWidth: strokeWidth, @@ -124,9 +124,9 @@ ol.style.Polygon.prototype.createLiteral = function(feature) { /** - * @type {ol.style.LiteralPolygon} + * @type {ol.style.PolygonLiteral} */ -ol.style.PolygonDefaults = new ol.style.LiteralPolygon({ +ol.style.PolygonDefaults = new ol.style.PolygonLiteral({ fillStyle: '#ffffff', strokeStyle: '#696969', strokeWidth: 1.5, diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js index ce07f4bee9..e3007f5205 100644 --- a/src/ol/style/shape.js +++ b/src/ol/style/shape.js @@ -1,11 +1,11 @@ -goog.provide('ol.style.LiteralShape'); goog.provide('ol.style.Shape'); +goog.provide('ol.style.ShapeLiteral'); goog.provide('ol.style.ShapeType'); goog.require('ol.Expression'); goog.require('ol.ExpressionLiteral'); -goog.require('ol.style.LiteralPoint'); goog.require('ol.style.Point'); +goog.require('ol.style.PointLiteral'); /** @@ -24,16 +24,16 @@ ol.style.ShapeType = { * strokeWidth: (number), * opacity: (number)}} */ -ol.style.LiteralShapeOptions; +ol.style.ShapeLiteralOptions; /** * @constructor - * @extends {ol.style.LiteralPoint} - * @param {ol.style.LiteralShapeOptions} config Symbolizer properties. + * @extends {ol.style.PointLiteral} + * @param {ol.style.ShapeLiteralOptions} config Symbolizer properties. */ -ol.style.LiteralShape = function(config) { +ol.style.ShapeLiteral = function(config) { /** @type {string} */ this.type = config.type; @@ -54,7 +54,7 @@ ol.style.LiteralShape = function(config) { this.opacity = config.opacity; }; -goog.inherits(ol.style.LiteralShape, ol.style.LiteralPoint); +goog.inherits(ol.style.ShapeLiteral, ol.style.PointLiteral); /** @@ -133,7 +133,7 @@ ol.style.Shape = function(options) { /** * @inheritDoc - * @return {ol.style.LiteralShape} Literal shape symbolizer. + * @return {ol.style.ShapeLiteral} Literal shape symbolizer. */ ol.style.Shape.prototype.createLiteral = function(feature) { var attrs = feature.getAttributes(); @@ -153,7 +153,7 @@ ol.style.Shape.prototype.createLiteral = function(feature) { var opacity = this.opacity_.evaluate(feature, attrs); goog.asserts.assertNumber(opacity, 'opacity must be a number'); - return new ol.style.LiteralShape({ + return new ol.style.ShapeLiteral({ type: this.type_, size: size, fillStyle: fillStyle, @@ -165,9 +165,9 @@ ol.style.Shape.prototype.createLiteral = function(feature) { /** - * @type {ol.style.LiteralShape} + * @type {ol.style.ShapeLiteral} */ -ol.style.ShapeDefaults = new ol.style.LiteralShape({ +ol.style.ShapeDefaults = new ol.style.ShapeLiteral({ type: ol.style.ShapeType.CIRCLE, size: 5, fillStyle: '#ffffff', diff --git a/src/ol/style/symbolizer.js b/src/ol/style/symbolizer.js index 432b0bbcac..f15cc79254 100644 --- a/src/ol/style/symbolizer.js +++ b/src/ol/style/symbolizer.js @@ -1,5 +1,5 @@ -goog.provide('ol.style.LiteralSymbolizer'); goog.provide('ol.style.Symbolizer'); +goog.provide('ol.style.SymbolizerLiteral'); goog.require('ol.Feature'); @@ -8,7 +8,7 @@ goog.require('ol.Feature'); /** * @interface */ -ol.style.LiteralSymbolizer = function() {}; +ol.style.SymbolizerLiteral = function() {}; @@ -20,6 +20,6 @@ ol.style.Symbolizer = function() {}; /** * @param {ol.Feature} feature Feature for evaluating expressions. - * @return {ol.style.LiteralSymbolizer} Literal symbolizer. + * @return {ol.style.SymbolizerLiteral} Literal symbolizer. */ ol.style.Symbolizer.prototype.createLiteral = function(feature) {}; diff --git a/test/spec/ol/style/line.test.js b/test/spec/ol/style/line.test.js index c7ffdc177c..78fc4b1a16 100644 --- a/test/spec/ol/style/line.test.js +++ b/test/spec/ol/style/line.test.js @@ -36,7 +36,7 @@ describe('ol.style.Line', function() { }); var literal = symbolizer.createLiteral(feature); - expect(literal).toBeA(ol.style.LiteralLine); + expect(literal).toBeA(ol.style.LineLiteral); expect(literal.opacity).toBe(42 / 100); expect(literal.strokeWidth).toBe(1.5); }); @@ -48,4 +48,4 @@ describe('ol.style.Line', function() { goog.require('ol.Expression'); goog.require('ol.Feature'); goog.require('ol.style.Line'); -goog.require('ol.style.LiteralLine'); +goog.require('ol.style.LineLiteral'); diff --git a/test/spec/ol/style/polygon.test.js b/test/spec/ol/style/polygon.test.js index 52f639a324..436ba77cd4 100644 --- a/test/spec/ol/style/polygon.test.js +++ b/test/spec/ol/style/polygon.test.js @@ -36,7 +36,7 @@ describe('ol.style.Polygon', function() { }); var literal = symbolizer.createLiteral(feature); - expect(literal).toBeA(ol.style.LiteralPolygon); + expect(literal).toBeA(ol.style.PolygonLiteral); expect(literal.opacity).toBe(42 / 100); expect(literal.fillStyle).toBe('#ff0000'); }); @@ -48,4 +48,4 @@ describe('ol.style.Polygon', function() { goog.require('ol.Expression'); goog.require('ol.Feature'); goog.require('ol.style.Polygon'); -goog.require('ol.style.LiteralPolygon'); +goog.require('ol.style.PolygonLiteral'); diff --git a/test/spec/ol/style/shape.test.js b/test/spec/ol/style/shape.test.js index 17835fb047..acf0996e90 100644 --- a/test/spec/ol/style/shape.test.js +++ b/test/spec/ol/style/shape.test.js @@ -36,7 +36,7 @@ describe('ol.style.Shape', function() { }); var literal = symbolizer.createLiteral(feature); - expect(literal).toBeA(ol.style.LiteralShape); + expect(literal).toBeA(ol.style.ShapeLiteral); expect(literal.size).toBe(42); expect(literal.opacity).toBe(0.4); }); @@ -48,4 +48,4 @@ describe('ol.style.Shape', function() { goog.require('ol.Expression'); goog.require('ol.Feature'); goog.require('ol.style.Shape'); -goog.require('ol.style.LiteralShape'); +goog.require('ol.style.ShapeLiteral'); From 083404bd582a57b928e507aa16a6290f5a75acb6 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 19 Feb 2013 23:15:45 -0700 Subject: [PATCH 077/180] Provides for tests --- test/spec/ol/geom/linestring.test.js | 3 +++ test/spec/ol/geom/multilinestring.test.js | 23 +++++++++++++---------- test/spec/ol/geom/multipoint.test.js | 3 +++ test/spec/ol/geom/multipolygon.test.js | 19 +++++++++++-------- test/spec/ol/geom/point.test.js | 3 +++ test/spec/ol/geom/polygon.test.js | 3 +++ 6 files changed, 36 insertions(+), 18 deletions(-) diff --git a/test/spec/ol/geom/linestring.test.js b/test/spec/ol/geom/linestring.test.js index 99abe77b32..1fae0ee361 100644 --- a/test/spec/ol/geom/linestring.test.js +++ b/test/spec/ol/geom/linestring.test.js @@ -1,3 +1,5 @@ +goog.provide('ol.test.geom.LineString'); + describe('ol.geom.LineString', function() { describe('constructor', function() { @@ -60,3 +62,4 @@ describe('ol.geom.LineString', function() { }); +goog.require('ol.geom.LineString'); diff --git a/test/spec/ol/geom/multilinestring.test.js b/test/spec/ol/geom/multilinestring.test.js index d5935f5f4d..9870eaf8e2 100644 --- a/test/spec/ol/geom/multilinestring.test.js +++ b/test/spec/ol/geom/multilinestring.test.js @@ -1,11 +1,13 @@ +goog.provide('ol.test.geom.MultiLineString'); + 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]]]); + [[10, 20], [30, 40]], + [[20, 30], [40, 50]]]); expect(multi).toBeA(ol.geom.MultiLineString); }); @@ -21,8 +23,8 @@ describe('ol.geom.MultiLineString', function() { it('is an array of linestrings', function() { var multi = new ol.geom.MultiLineString([ - [[10, 20], [30, 40]], - [[20, 30], [40, 50]]]); + [[10, 20], [30, 40]], + [[20, 30], [40, 50]]]); expect(multi.components.length).toBe(2); expect(multi.components[0]).toBeA(ol.geom.LineString); @@ -36,15 +38,15 @@ describe('ol.geom.MultiLineString', function() { it('can be 2', function() { var multi = new ol.geom.MultiLineString([ - [[10, 20], [30, 40]], - [[20, 30], [40, 50]]]); + [[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]]]); + [[10, 20, 30], [30, 40, 50]], + [[20, 30, 40], [40, 50, 60]]]); expect(multi.dimension).toBe(3); }); @@ -54,8 +56,8 @@ describe('ol.geom.MultiLineString', function() { it('returns the bounding extent', function() { var multi = new ol.geom.MultiLineString([ - [[10, 20], [30, 40]], - [[20, 30], [40, 50]]]); + [[10, 20], [30, 40]], + [[20, 30], [40, 50]]]); var bounds = multi.getBounds(); expect(bounds.minX).toBe(10); expect(bounds.minY).toBe(20); @@ -67,3 +69,4 @@ describe('ol.geom.MultiLineString', function() { }); +goog.require('ol.geom.MultiLineString'); diff --git a/test/spec/ol/geom/multipoint.test.js b/test/spec/ol/geom/multipoint.test.js index 03368c359a..496a573f9a 100644 --- a/test/spec/ol/geom/multipoint.test.js +++ b/test/spec/ol/geom/multipoint.test.js @@ -1,3 +1,5 @@ +goog.provide('ol.test.geom.MultiPoint'); + describe('ol.geom.MultiPoint', function() { describe('constructor', function() { @@ -57,3 +59,4 @@ describe('ol.geom.MultiPoint', function() { }); +goog.require('ol.geom.MultiPoint'); diff --git a/test/spec/ol/geom/multipolygon.test.js b/test/spec/ol/geom/multipolygon.test.js index b9b57c3e09..176df22dcf 100644 --- a/test/spec/ol/geom/multipolygon.test.js +++ b/test/spec/ol/geom/multipolygon.test.js @@ -1,3 +1,5 @@ +goog.provide('ol.test.geom.MultiPolygon'); + describe('ol.geom.MultiPolygon', function() { var outer1 = [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]], @@ -9,8 +11,8 @@ describe('ol.geom.MultiPolygon', function() { it('creates a multi-linestring from an array', function() { var multi = new ol.geom.MultiPolygon([ - [outer1, inner1a, inner1b], - [outer2]]); + [outer1, inner1a, inner1b], + [outer2]]); expect(multi).toBeA(ol.geom.MultiPolygon); }); @@ -26,8 +28,8 @@ describe('ol.geom.MultiPolygon', function() { it('is an array of polygons', function() { var multi = new ol.geom.MultiPolygon([ - [outer1, inner1a, inner1b], - [outer2]]); + [outer1, inner1a, inner1b], + [outer2]]); expect(multi.components.length).toBe(2); expect(multi.components[0]).toBeA(ol.geom.Polygon); @@ -41,8 +43,8 @@ describe('ol.geom.MultiPolygon', function() { it('can be 2', function() { var multi = new ol.geom.MultiPolygon([ - [outer1, inner1a, inner1b], - [outer2]]); + [outer1, inner1a, inner1b], + [outer2]]); expect(multi.dimension).toBe(2); }); @@ -57,8 +59,8 @@ describe('ol.geom.MultiPolygon', function() { it('returns the bounding extent', function() { var multi = new ol.geom.MultiPolygon([ - [outer1, inner1a, inner1b], - [outer2]]); + [outer1, inner1a, inner1b], + [outer2]]); var bounds = multi.getBounds(); expect(bounds.minX).toBe(0); expect(bounds.minY).toBe(0); @@ -70,3 +72,4 @@ describe('ol.geom.MultiPolygon', function() { }); +goog.require('ol.geom.MultiPolygon'); diff --git a/test/spec/ol/geom/point.test.js b/test/spec/ol/geom/point.test.js index 9ec8c67879..b550646e70 100644 --- a/test/spec/ol/geom/point.test.js +++ b/test/spec/ol/geom/point.test.js @@ -1,3 +1,5 @@ +goog.provide('ol.test.geom.Point'); + describe('ol.geom.Point', function() { describe('constructor', function() { @@ -58,3 +60,4 @@ describe('ol.geom.Point', function() { }); +goog.require('ol.geom.Point'); diff --git a/test/spec/ol/geom/polygon.test.js b/test/spec/ol/geom/polygon.test.js index db500d1096..1a12b02770 100644 --- a/test/spec/ol/geom/polygon.test.js +++ b/test/spec/ol/geom/polygon.test.js @@ -1,3 +1,5 @@ +gooog.require('ol.test.geom.Polygon'); + describe('ol.geom.Polygon', function() { var outer = [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]], @@ -62,3 +64,4 @@ describe('ol.geom.Polygon', function() { }); +goog.require('ol.geom.Polygon'); From 0015e466dc182c6ccdd10c7962675383306ecfbb Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 19 Feb 2013 23:21:05 -0700 Subject: [PATCH 078/180] Allow instanceof checks for geometries --- src/ol/geom/geometry.js | 4 ++-- src/ol/geom/geometrycollection.js | 5 ++++- src/ol/geom/linestring.js | 5 ++++- src/ol/geom/point.js | 5 ++++- src/ol/geom/polygon.js | 5 ++++- test/spec/ol/geom/linestring.test.js | 2 ++ test/spec/ol/geom/multilinestring.test.js | 2 ++ test/spec/ol/geom/multipoint.test.js | 2 ++ test/spec/ol/geom/multipolygon.test.js | 2 ++ test/spec/ol/geom/point.test.js | 2 ++ test/spec/ol/geom/polygon.test.js | 2 ++ 11 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/ol/geom/geometry.js b/src/ol/geom/geometry.js index b12a260d44..b2ab428bf5 100644 --- a/src/ol/geom/geometry.js +++ b/src/ol/geom/geometry.js @@ -7,7 +7,7 @@ goog.provide('ol.geom.GeometryType'); /** - * @interface + * @constructor */ ol.geom.Geometry = function() {}; @@ -30,7 +30,7 @@ ol.geom.Geometry.prototype.getBounds = goog.abstractMethod; * Get the geometry type. * @return {ol.geom.GeometryType} The geometry type. */ -ol.geom.Geometry.prototype.getType = function() {}; +ol.geom.Geometry.prototype.getType = goog.abstractMethod; /** diff --git a/src/ol/geom/geometrycollection.js b/src/ol/geom/geometrycollection.js index b0e8f7bd75..277eafa899 100644 --- a/src/ol/geom/geometrycollection.js +++ b/src/ol/geom/geometrycollection.js @@ -10,10 +10,12 @@ goog.require('ol.geom.GeometryType'); * A collection of geometries. This constructor should not called. Instead * create one of the fixed type collections. * @constructor - * @implements {ol.geom.Geometry} + * @extends {ol.geom.Geometry} */ ol.geom.GeometryCollection = function() { + goog.base(this); + /** * @type {Array.} */ @@ -31,6 +33,7 @@ ol.geom.GeometryCollection = function() { this.bounds = null; }; +goog.inherits(ol.geom.GeometryCollection, ol.geom.Geometry); /** diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index 1e90688115..5dbe11936e 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -10,12 +10,14 @@ goog.require('ol.geom.GeometryType'); /** * @constructor - * @implements {ol.geom.Geometry} + * @extends {ol.geom.Geometry} * @param {ol.geom.CoordinateArray} coordinates Coordinates array (e.g. * [[x0, y0], [x1, y1]]). */ ol.geom.LineString = function(coordinates) { + goog.base(this); + // assume the same dimension for all coordinates var dimension = coordinates[0].length, count = coordinates.length, @@ -43,6 +45,7 @@ ol.geom.LineString = function(coordinates) { this.bounds_ = null; }; +goog.inherits(ol.geom.LineString, ol.geom.Geometry); /** diff --git a/src/ol/geom/point.js b/src/ol/geom/point.js index 73b6b8338b..0409929adf 100644 --- a/src/ol/geom/point.js +++ b/src/ol/geom/point.js @@ -10,11 +10,13 @@ goog.require('ol.geom.GeometryType'); /** * @constructor - * @implements {ol.geom.Geometry} + * @extends {ol.geom.Geometry} * @param {ol.geom.Coordinate} coordinates Coordinates array (e.g. [x, y]). */ ol.geom.Point = function(coordinates) { + goog.base(this); + /** * @type {Float64Array} */ @@ -33,6 +35,7 @@ ol.geom.Point = function(coordinates) { this.bounds_ = null; }; +goog.inherits(ol.geom.Point, ol.geom.Geometry); /** diff --git a/src/ol/geom/polygon.js b/src/ol/geom/polygon.js index 4a0b32dad3..c3c3e88a76 100644 --- a/src/ol/geom/polygon.js +++ b/src/ol/geom/polygon.js @@ -11,12 +11,14 @@ goog.require('ol.geom.LinearRing'); /** * @constructor - * @implements {ol.geom.Geometry} + * @extends {ol.geom.Geometry} * @param {Array.} coordinates Array of rings. First * is outer, any remaining are inner. */ ol.geom.Polygon = function(coordinates) { + goog.base(this); + var numRings = coordinates.length, dimension; @@ -46,6 +48,7 @@ ol.geom.Polygon = function(coordinates) { this.bounds_ = null; }; +goog.inherits(ol.geom.Polygon, ol.geom.Geometry); /** diff --git a/test/spec/ol/geom/linestring.test.js b/test/spec/ol/geom/linestring.test.js index 1fae0ee361..3ddf89d8af 100644 --- a/test/spec/ol/geom/linestring.test.js +++ b/test/spec/ol/geom/linestring.test.js @@ -7,6 +7,7 @@ describe('ol.geom.LineString', function() { it('creates a linestring from an array', function() { var line = new ol.geom.LineString([[10, 20], [30, 40]]); expect(line).toBeA(ol.geom.LineString); + expect(line).toBeA(ol.geom.Geometry); }); it('throws when given mismatched dimension', function() { @@ -62,4 +63,5 @@ describe('ol.geom.LineString', function() { }); +goog.require('ol.geom.Geometry'); goog.require('ol.geom.LineString'); diff --git a/test/spec/ol/geom/multilinestring.test.js b/test/spec/ol/geom/multilinestring.test.js index 9870eaf8e2..356a70fc92 100644 --- a/test/spec/ol/geom/multilinestring.test.js +++ b/test/spec/ol/geom/multilinestring.test.js @@ -9,6 +9,7 @@ describe('ol.geom.MultiLineString', function() { [[10, 20], [30, 40]], [[20, 30], [40, 50]]]); expect(multi).toBeA(ol.geom.MultiLineString); + expect(multi).toBeA(ol.geom.Geometry); }); it('throws when given with insufficient dimensions', function() { @@ -69,4 +70,5 @@ describe('ol.geom.MultiLineString', function() { }); +goog.require('ol.geom.Geometry'); goog.require('ol.geom.MultiLineString'); diff --git a/test/spec/ol/geom/multipoint.test.js b/test/spec/ol/geom/multipoint.test.js index 496a573f9a..0e1c641ff2 100644 --- a/test/spec/ol/geom/multipoint.test.js +++ b/test/spec/ol/geom/multipoint.test.js @@ -7,6 +7,7 @@ describe('ol.geom.MultiPoint', 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); + expect(multi).toBeA(ol.geom.Geometry); }); it('throws when given with insufficient dimensions', function() { @@ -59,4 +60,5 @@ describe('ol.geom.MultiPoint', function() { }); +goog.require('ol.geom.Geometry'); goog.require('ol.geom.MultiPoint'); diff --git a/test/spec/ol/geom/multipolygon.test.js b/test/spec/ol/geom/multipolygon.test.js index 176df22dcf..0dd9dd9a80 100644 --- a/test/spec/ol/geom/multipolygon.test.js +++ b/test/spec/ol/geom/multipolygon.test.js @@ -14,6 +14,7 @@ describe('ol.geom.MultiPolygon', function() { [outer1, inner1a, inner1b], [outer2]]); expect(multi).toBeA(ol.geom.MultiPolygon); + expect(multi).toBeA(ol.geom.Geometry); }); it('throws when given with insufficient dimensions', function() { @@ -72,4 +73,5 @@ describe('ol.geom.MultiPolygon', function() { }); +goog.require('ol.geom.Geometry'); goog.require('ol.geom.MultiPolygon'); diff --git a/test/spec/ol/geom/point.test.js b/test/spec/ol/geom/point.test.js index b550646e70..51031e02e9 100644 --- a/test/spec/ol/geom/point.test.js +++ b/test/spec/ol/geom/point.test.js @@ -7,6 +7,7 @@ describe('ol.geom.Point', function() { it('creates a point from an array', function() { var point = new ol.geom.Point([10, 20]); expect(point).toBeA(ol.geom.Point); + expect(point).toBeA(ol.geom.Geometry); }); it('throws when given with insufficient dimensions', function() { @@ -60,4 +61,5 @@ describe('ol.geom.Point', function() { }); +goog.require('ol.geom.Geometry'); goog.require('ol.geom.Point'); diff --git a/test/spec/ol/geom/polygon.test.js b/test/spec/ol/geom/polygon.test.js index 1a12b02770..591b896643 100644 --- a/test/spec/ol/geom/polygon.test.js +++ b/test/spec/ol/geom/polygon.test.js @@ -11,6 +11,7 @@ describe('ol.geom.Polygon', function() { it('creates a polygon from an array', function() { var poly = new ol.geom.Polygon([outer, inner1, inner2]); expect(poly).toBeA(ol.geom.Polygon); + expect(poly).toBeA(ol.geom.Geometry); }); it('throws when given mismatched dimension', function() { @@ -64,4 +65,5 @@ describe('ol.geom.Polygon', function() { }); +goog.require('ol.geom.Geometry'); goog.require('ol.geom.Polygon'); From 735f490f56fa5b7842509d938e0ae0310c6f6c42 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 20 Feb 2013 00:02:50 -0700 Subject: [PATCH 079/180] Treating geometry as just another attribute The first set geometry is considered the default. As an added bonus, we're back to a single argument constructor. Later, we could allow a schema to be set. This would be done before setting values (calling constructor with no args). --- examples/vector-layer.js | 12 +- src/ol/feature.js | 36 ++++- src/ol/io/geojson.js | 7 +- test/spec/ol/feature.test.js | 161 +++++++++++++++++++++++ test/spec/ol/filter/extentfilter.test.js | 4 +- test/spec/ol/source/vectorsource.test.js | 35 +++-- test/spec/ol/style/line.test.js | 2 +- test/spec/ol/style/polygon.test.js | 2 +- test/spec/ol/style/shape.test.js | 2 +- 9 files changed, 233 insertions(+), 28 deletions(-) create mode 100644 test/spec/ol/feature.test.js diff --git a/examples/vector-layer.js b/examples/vector-layer.js index cad588b797..ef568efd69 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -22,11 +22,13 @@ var source = new ol.source.Vector({ }); source.addFeatures([ - new ol.Feature( - new ol.geom.LineString([[-10000000, -10000000], [10000000, 10000000]])), - new ol.Feature( - new ol.geom.LineString([[-10000000, 10000000], [10000000, -10000000]])), - new ol.Feature(new ol.geom.Point([-10000000, 5000000])) + new ol.Feature({ + g: new ol.geom.LineString([[-10000000, -10000000], [10000000, 10000000]]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[-10000000, 10000000], [10000000, -10000000]]) + }, + new ol.Feature({g: new ol.geom.Point([-10000000, 5000000])}) ]); var vector = new ol.layer.Vector({ diff --git a/src/ol/feature.js b/src/ol/feature.js index f4d46ac08a..f12deb2366 100644 --- a/src/ol/feature.js +++ b/src/ol/feature.js @@ -8,18 +8,17 @@ goog.require('ol.geom.Geometry'); /** * @constructor * @extends {ol.Object} - * @param {ol.geom.Geometry=} opt_geometry Geometry. * @param {Object=} opt_values Attributes. */ -ol.Feature = function(opt_geometry, opt_values) { +ol.Feature = function(opt_values) { goog.base(this, opt_values); /** + * @type {string|undefined} * @private - * @type {ol.geom.Geometry} */ - this.geometry_ = goog.isDef(opt_geometry) ? opt_geometry : null; + this.geometryName_; }; goog.inherits(ol.Feature, ol.Object); @@ -47,7 +46,22 @@ ol.Feature.prototype.getAttributes = function() { * @return {ol.geom.Geometry} The geometry (or null if none). */ ol.Feature.prototype.getGeometry = function() { - return this.geometry_; + return goog.isDef(this.geometryName_) ? + /** @type {ol.geom.Geometry} */ (this.get(this.geometryName_)) : + null; +}; + + +/** + * @inheritDoc + * @param {string} key Key. + * @param {*} value Value. + */ +ol.Feature.prototype.set = function(key, value) { + if (!goog.isDef(this.geometryName_) && (value instanceof ol.geom.Geometry)) { + this.geometryName_ = key; + } + goog.base(this, 'set', key, value); }; @@ -55,5 +69,15 @@ ol.Feature.prototype.getGeometry = function() { * @param {ol.geom.Geometry} geometry The geometry. */ ol.Feature.prototype.setGeometry = function(geometry) { - this.geometry_ = geometry; + if (!goog.isDef(this.geometryName_)) { + this.geometryName_ = ol.Feature.DEFAULT_GEOMETRY; + } + this.set(this.geometryName_, geometry); }; + + +/** + * @const + * @type {string} + */ +ol.Feature.DEFAULT_GEOMETRY = 'geometry'; diff --git a/src/ol/io/geojson.js b/src/ol/io/geojson.js index 2dd708e641..bfd8bd8250 100644 --- a/src/ol/io/geojson.js +++ b/src/ol/io/geojson.js @@ -84,12 +84,15 @@ ol.io.geojson.parse_ = function(json) { */ ol.io.geojson.parseFeature_ = function(json) { var geomJson = json.geometry, - geometry; + geometry = null; if (geomJson) { geometry = /** @type {ol.geom.Geometry} */ (ol.io.geojson.parse_( /** @type {GeoJSONGeometry} */ (geomJson))); } - return new ol.Feature(geometry, json.properties); + var feature = new ol.Feature(); + feature.setGeometry(geometry); + feature.setValues(json.properties); + return feature; }; diff --git a/test/spec/ol/feature.test.js b/test/spec/ol/feature.test.js new file mode 100644 index 0000000000..20cf3579ce --- /dev/null +++ b/test/spec/ol/feature.test.js @@ -0,0 +1,161 @@ +goog.provide('ol.test.Feature'); + +describe('ol.Feature', function() { + + describe('constructor', function() { + + it('creates a new feature', function() { + var feature = new ol.Feature(); + expect(feature).toBeA(ol.Feature); + }); + + it('takes attribute values', function() { + var feature = new ol.Feature({ + foo: 'bar' + }); + expect(feature.get('foo')).toBe('bar'); + }); + + it('will set the default geometry', function() { + var feature = new ol.Feature({ + loc: new ol.geom.Point([10, 20]), + foo: 'bar' + }); + var geometry = feature.getGeometry(); + expect(geometry).toBeA(ol.geom.Point); + expect(feature.get('loc')).toBe(geometry); + }); + + }); + + describe('#get()', function() { + + it('returns values set at construction', function() { + var feature = new ol.Feature({ + a: 'first', + b: 'second' + }); + expect(feature.get('a')).toBe('first'); + expect(feature.get('b')).toBe('second'); + }); + + it('returns undefined for unset attributes', function() { + var feature = new ol.Feature(); + expect(feature.get('a')).toBeUndefined(); + }); + + it('returns values set by set', function() { + var feature = new ol.Feature(); + feature.set('a', 'b'); + expect(feature.get('a')).toBe('b'); + }); + + }); + + describe('#getGeometry()', function() { + + var point = new ol.geom.Point([15, 30]); + + it('returns null for no geometry', function() { + var feature = new ol.Feature(); + expect(feature.getGeometry()).toBeNull(); + }); + + it('gets the geometry set at construction', function() { + var feature = new ol.Feature({ + geom: point + }); + expect(feature.getGeometry()).toBe(point); + }); + + it('gets any geometry set by setGeometry', function() { + var feature = new ol.Feature(); + feature.setGeometry(point); + expect(feature.getGeometry()).toBe(point); + + var point2 = new ol.geom.Point([1, 2]); + feature.setGeometry(point2); + expect(feature.getGeometry()).toBe(point2); + }); + + it('gets the first geometry set by set', function() { + var feature = new ol.Feature(); + feature.set('foo', point); + expect(feature.getGeometry()).toBe(point); + + feature.set('bar', new ol.geom.Point([1, 2])); + expect(feature.getGeometry()).toBe(point); + }); + + }); + + describe('#set()', function() { + + it('sets values', function() { + var feature = new ol.Feature({ + a: 'first', + b: 'second' + }); + feature.set('a', 'new'); + expect(feature.get('a')).toBe('new'); + }); + + it('can be used to set the geometry', function() { + var point = new ol.geom.Point([3, 4]); + var feature = new ol.Feature({ + loc: new ol.geom.Point([1, 2]) + }); + feature.set('loc', point); + expect(feature.get('loc')).toBe(point); + expect(feature.getGeometry()).toBe(point); + }); + + }); + + describe('#setGeometry()', function() { + + var point = new ol.geom.Point([15, 30]); + + it('sets the default geometry', function() { + var feature = new ol.Feature(); + feature.setGeometry(point); + expect(feature.get(ol.Feature.DEFAULT_GEOMETRY)).toBe(point); + }); + + it('replaces previous default geometry', function() { + var feature = new ol.Feature({ + geom: point + }); + expect(feature.getGeometry()).toBe(point); + + var point2 = new ol.geom.Point([1, 2]); + feature.setGeometry(point2); + expect(feature.getGeometry()).toBe(point2); + }); + + it('gets any geometry set by setGeometry', function() { + var feature = new ol.Feature(); + feature.setGeometry(point); + expect(feature.getGeometry()).toBe(point); + + var point2 = new ol.geom.Point([1, 2]); + feature.setGeometry(point2); + expect(feature.getGeometry()).toBe(point2); + }); + + it('gets the first geometry set by set', function() { + var feature = new ol.Feature(); + feature.set('foo', point); + expect(feature.getGeometry()).toBe(point); + + feature.set('bar', new ol.geom.Point([1, 2])); + expect(feature.getGeometry()).toBe(point); + }); + + }); + +}); + + +goog.require('ol.Feature'); +goog.require('ol.geom.Point'); diff --git a/test/spec/ol/filter/extentfilter.test.js b/test/spec/ol/filter/extentfilter.test.js index 68e1a0a2e7..f4040d72f8 100644 --- a/test/spec/ol/filter/extentfilter.test.js +++ b/test/spec/ol/filter/extentfilter.test.js @@ -21,9 +21,9 @@ describe('ol.filter.Extent', function() { describe('#evaluate()', function() { it('returns true if a feature intersects, false if not', function() { - expect(filter.evaluate(new ol.Feature(new ol.geom.Point([44, 89])))) + expect(filter.evaluate(new ol.Feature({g: new ol.geom.Point([44, 89])}))) .toBe(true); - expect(filter.evaluate(new ol.Feature(new ol.geom.Point([46, 91])))) + expect(filter.evaluate(new ol.Feature({g: new ol.geom.Point([46, 91])}))) .toBe(false); }); diff --git a/test/spec/ol/source/vectorsource.test.js b/test/spec/ol/source/vectorsource.test.js index f3ac91aeda..8bf8df1f43 100644 --- a/test/spec/ol/source/vectorsource.test.js +++ b/test/spec/ol/source/vectorsource.test.js @@ -22,14 +22,30 @@ describe('ol.source.Vector', function() { beforeEach(function() { features = [ - new ol.Feature(new ol.geom.Point([16.0, 48.0])), - new ol.Feature(new ol.geom.Point([16.1, 48.1])), - new ol.Feature(new ol.geom.Point([16.2, 48.2])), - new ol.Feature(new ol.geom.Point([16.3, 48.3])), - new ol.Feature(new ol.geom.LineString([[16.4, 48.4], [16.5, 48.5]])), - new ol.Feature(new ol.geom.LineString([[16.6, 48.6], [16.7, 48.7]])), - new ol.Feature(new ol.geom.LineString([[16.8, 48.8], [16.9, 48.9]])), - new ol.Feature(new ol.geom.LineString([[17.0, 49.0], [17.1, 49.1]])) + new ol.Feature({ + g: new ol.geom.Point([16.0, 48.0]) + }), + new ol.Feature({ + g: new ol.geom.Point([16.1, 48.1]) + }), + new ol.Feature({ + g: new ol.geom.Point([16.2, 48.2]) + }), + new ol.Feature({ + g: new ol.geom.Point([16.3, 48.3]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[16.4, 48.4], [16.5, 48.5]]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[16.6, 48.6], [16.7, 48.7]]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[16.8, 48.8], [16.9, 48.9]]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[17.0, 49.0], [17.1, 49.1]]) + }) ]; vectorSource = new ol.source.Vector({ projection: ol.Projection.getFromCode('EPSG:4326') @@ -71,8 +87,7 @@ describe('ol.source.Vector', function() { expect(subset2.length).toEqual(0); }); - it('can handle any query using the filter\'s evaluate function', - function() { + it('can handle query using the filter\'s evaluate function', function() { var filter = new ol.filter.Logical([geomFilter, extentFilter], ol.filter.LogicalOperator.OR); spyOn(filter, 'evaluate').andCallThrough(); diff --git a/test/spec/ol/style/line.test.js b/test/spec/ol/style/line.test.js index 78fc4b1a16..aac6cebe4a 100644 --- a/test/spec/ol/style/line.test.js +++ b/test/spec/ol/style/line.test.js @@ -30,7 +30,7 @@ describe('ol.style.Line', function() { strokeWidth: ol.Expression('widthAttr') }); - var feature = new ol.Feature(undefined, { + var feature = new ol.Feature({ value: 42, widthAttr: 1.5 }); diff --git a/test/spec/ol/style/polygon.test.js b/test/spec/ol/style/polygon.test.js index 436ba77cd4..b4c4722a9f 100644 --- a/test/spec/ol/style/polygon.test.js +++ b/test/spec/ol/style/polygon.test.js @@ -30,7 +30,7 @@ describe('ol.style.Polygon', function() { fillStyle: new ol.Expression('fillAttr') }); - var feature = new ol.Feature(undefined, { + var feature = new ol.Feature({ value: 42, fillAttr: '#ff0000' }); diff --git a/test/spec/ol/style/shape.test.js b/test/spec/ol/style/shape.test.js index acf0996e90..26405624e6 100644 --- a/test/spec/ol/style/shape.test.js +++ b/test/spec/ol/style/shape.test.js @@ -30,7 +30,7 @@ describe('ol.style.Shape', function() { opacity: new ol.Expression('opacityAttr') }); - var feature = new ol.Feature(undefined, { + var feature = new ol.Feature({ sizeAttr: 42, opacityAttr: 0.4 }); From 9cac8ec2f66b9ef33078569f639912956c8de06f Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 20 Feb 2013 00:09:38 -0700 Subject: [PATCH 080/180] Moving GeoJSON into parser package Need to discuss whether we want all to require constructing a new parser. I liked the simple `ol.io.geojson.read` function. --- src/ol/{io => parser}/geojson.js | 50 ++++++++++----------- test/spec/ol/{io => parser}/geojson.test.js | 16 ++++--- 2 files changed, 35 insertions(+), 31 deletions(-) rename src/ol/{io => parser}/geojson.js (75%) rename test/spec/ol/{io => parser}/geojson.test.js (91%) diff --git a/src/ol/io/geojson.js b/src/ol/parser/geojson.js similarity index 75% rename from src/ol/io/geojson.js rename to src/ol/parser/geojson.js index bfd8bd8250..1df8ebf4d4 100644 --- a/src/ol/io/geojson.js +++ b/src/ol/parser/geojson.js @@ -1,4 +1,4 @@ -goog.provide('ol.io.geojson'); +goog.provide('ol.parser.geojson'); goog.require('ol.Feature'); goog.require('ol.geom.Geometry'); @@ -17,10 +17,10 @@ goog.require('ol.geom.Polygon'); * ol.geom.Geometry|Array.} Parsed geometry or array * of geometries. */ -ol.io.geojson.read = function(str) { +ol.parser.geojson.read = function(str) { // TODO: add options and accept projection var json = /** @type {GeoJSONObject} */ (JSON.parse(str)); - return ol.io.geojson.parse_(json); + return ol.parser.geojson.parse_(json); }; @@ -31,43 +31,43 @@ ol.io.geojson.read = function(str) { * of geometries. * @private */ -ol.io.geojson.parse_ = function(json) { +ol.parser.geojson.parse_ = function(json) { var result; switch (json.type) { case 'FeatureCollection': - result = ol.io.geojson.parseFeatureCollection_( + result = ol.parser.geojson.parseFeatureCollection_( /** @type {GeoJSONFeatureCollection} */ (json)); break; case 'Feature': - result = ol.io.geojson.parseFeature_( + result = ol.parser.geojson.parseFeature_( /** @type {GeoJSONFeature} */ (json)); break; case 'GeometryCollection': - result = ol.io.geojson.parseGeometryCollection_( + result = ol.parser.geojson.parseGeometryCollection_( /** @type {GeoJSONGeometryCollection} */ (json)); break; case 'Point': - result = ol.io.geojson.parsePoint_( + result = ol.parser.geojson.parsePoint_( /** @type {GeoJSONGeometry} */ (json)); break; case 'LineString': - result = ol.io.geojson.parseLineString_( + result = ol.parser.geojson.parseLineString_( /** @type {GeoJSONGeometry} */ (json)); break; case 'Polygon': - result = ol.io.geojson.parsePolygon_( + result = ol.parser.geojson.parsePolygon_( /** @type {GeoJSONGeometry} */ (json)); break; case 'MultiPoint': - result = ol.io.geojson.parseMultiPoint_( + result = ol.parser.geojson.parseMultiPoint_( /** @type {GeoJSONGeometry} */ (json)); break; case 'MultiLineString': - result = ol.io.geojson.parseMultiLineString_( + result = ol.parser.geojson.parseMultiLineString_( /** @type {GeoJSONGeometry} */ (json)); break; case 'MultiPolygon': - result = ol.io.geojson.parseMultiPolygon_( + result = ol.parser.geojson.parseMultiPolygon_( /** @type {GeoJSONGeometry} */ (json)); break; default: @@ -82,11 +82,11 @@ ol.io.geojson.parse_ = function(json) { * @return {ol.Feature} Parsed feature. * @private */ -ol.io.geojson.parseFeature_ = function(json) { +ol.parser.geojson.parseFeature_ = function(json) { var geomJson = json.geometry, geometry = null; if (geomJson) { - geometry = /** @type {ol.geom.Geometry} */ (ol.io.geojson.parse_( + geometry = /** @type {ol.geom.Geometry} */ (ol.parser.geojson.parse_( /** @type {GeoJSONGeometry} */ (geomJson))); } var feature = new ol.Feature(); @@ -101,14 +101,14 @@ ol.io.geojson.parseFeature_ = function(json) { * @return {Array.} Parsed array of features. * @private */ -ol.io.geojson.parseFeatureCollection_ = function(json) { +ol.parser.geojson.parseFeatureCollection_ = function(json) { var features = json.features, len = features.length, result = new Array(len), i; for (i = 0; i < len; ++i) { - result[i] = ol.io.geojson.parse_( + result[i] = ol.parser.geojson.parse_( /** @type {GeoJSONFeature} */ (features[i])); } return result; @@ -120,14 +120,14 @@ ol.io.geojson.parseFeatureCollection_ = function(json) { * @return {Array.} Parsed array of geometries. * @private */ -ol.io.geojson.parseGeometryCollection_ = function(json) { +ol.parser.geojson.parseGeometryCollection_ = function(json) { var geometries = json.geometries, len = geometries.length, result = new Array(len), i; for (i = 0; i < len; ++i) { - result[i] = ol.io.geojson.parse_( + result[i] = ol.parser.geojson.parse_( /** @type {GeoJSONGeometry} */ (geometries[i])); } return result; @@ -139,7 +139,7 @@ ol.io.geojson.parseGeometryCollection_ = function(json) { * @return {ol.geom.LineString} Parsed linestring. * @private */ -ol.io.geojson.parseLineString_ = function(json) { +ol.parser.geojson.parseLineString_ = function(json) { return new ol.geom.LineString(json.coordinates); }; @@ -149,7 +149,7 @@ ol.io.geojson.parseLineString_ = function(json) { * @return {ol.geom.MultiLineString} Parsed multi-linestring. * @private */ -ol.io.geojson.parseMultiLineString_ = function(json) { +ol.parser.geojson.parseMultiLineString_ = function(json) { return new ol.geom.MultiLineString(json.coordinates); }; @@ -159,7 +159,7 @@ ol.io.geojson.parseMultiLineString_ = function(json) { * @return {ol.geom.MultiPoint} Parsed multi-point. * @private */ -ol.io.geojson.parseMultiPoint_ = function(json) { +ol.parser.geojson.parseMultiPoint_ = function(json) { return new ol.geom.MultiPoint(json.coordinates); }; @@ -169,7 +169,7 @@ ol.io.geojson.parseMultiPoint_ = function(json) { * @return {ol.geom.MultiPolygon} Parsed multi-polygon. * @private */ -ol.io.geojson.parseMultiPolygon_ = function(json) { +ol.parser.geojson.parseMultiPolygon_ = function(json) { return new ol.geom.MultiPolygon(json.coordinates); }; @@ -179,7 +179,7 @@ ol.io.geojson.parseMultiPolygon_ = function(json) { * @return {ol.geom.Point} Parsed multi-point. * @private */ -ol.io.geojson.parsePoint_ = function(json) { +ol.parser.geojson.parsePoint_ = function(json) { return new ol.geom.Point(json.coordinates); }; @@ -189,6 +189,6 @@ ol.io.geojson.parsePoint_ = function(json) { * @return {ol.geom.Polygon} Parsed polygon. * @private */ -ol.io.geojson.parsePolygon_ = function(json) { +ol.parser.geojson.parsePolygon_ = function(json) { return new ol.geom.Polygon(json.coordinates); }; diff --git a/test/spec/ol/io/geojson.test.js b/test/spec/ol/parser/geojson.test.js similarity index 91% rename from test/spec/ol/io/geojson.test.js rename to test/spec/ol/parser/geojson.test.js index 7ad58d4455..dfbd3ae70d 100644 --- a/test/spec/ol/io/geojson.test.js +++ b/test/spec/ol/parser/geojson.test.js @@ -1,4 +1,6 @@ -describe('ol.io.geojson', function() { +goog.provide('ol.test.parser.geojson'); + +describe('ol.parser.geojson', function() { var data = { 'type': 'FeatureCollection', @@ -72,7 +74,7 @@ describe('ol.io.geojson', function() { coordinates: [10, 20] }); - var obj = ol.io.geojson.read(str); + 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); @@ -84,7 +86,7 @@ describe('ol.io.geojson', function() { coordinates: [[10, 20], [30, 40]] }); - var obj = ol.io.geojson.read(str); + 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); @@ -101,7 +103,7 @@ describe('ol.io.geojson', function() { coordinates: [outer, inner1, inner2] }); - var obj = ol.io.geojson.read(str); + var obj = ol.parser.geojson.read(str); expect(obj).toBeA(ol.geom.Polygon); expect(obj.rings.length).toBe(3); expect(obj.rings[0]).toBeA(ol.geom.LinearRing); @@ -118,7 +120,7 @@ describe('ol.io.geojson', function() { ] }); - var array = ol.io.geojson.read(str); + var array = ol.parser.geojson.read(str); expect(array.length).toBe(2); expect(array[0]).toBeA(ol.geom.Point); expect(array[1]).toBeA(ol.geom.LineString); @@ -126,7 +128,7 @@ describe('ol.io.geojson', function() { it('parses feature collection', function() { var str = JSON.stringify(data), - array = ol.io.geojson.read(str); + array = ol.parser.geojson.read(str); expect(array.length).toBe(2); @@ -146,3 +148,5 @@ describe('ol.io.geojson', function() { }); }); + +goog.require('ol.parser.geojson'); From d642628545d4dcc05288e63689844bf840181252 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 20 Feb 2013 00:21:08 -0700 Subject: [PATCH 081/180] Example syntax --- examples/vector-layer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/vector-layer.js b/examples/vector-layer.js index ef568efd69..8e33676521 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -27,7 +27,7 @@ source.addFeatures([ }), new ol.Feature({ g: new ol.geom.LineString([[-10000000, 10000000], [10000000, -10000000]]) - }, + }), new ol.Feature({g: new ol.geom.Point([-10000000, 5000000])}) ]); From 8f578b88b18a072e06f3ef731ba8c6bc5228f305 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 20 Feb 2013 01:15:39 -0700 Subject: [PATCH 082/180] Removing the typed arrays Its not clear that we will be able to leverage the Float64Array. It may also be that the overhead of constructing these small arrays negates any benefit of faster access. And for all coordinate arrays, we'll need arrays that grow/shrink. --- src/ol/geom/linestring.js | 12 ++++++++---- src/ol/geom/point.js | 4 ++-- test/spec/ol/geom/linearring.test.js | 6 ++++-- test/spec/ol/geom/linestring.test.js | 3 +-- test/spec/ol/geom/point.test.js | 3 +-- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index 5dbe11936e..6d8be32f5c 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -24,12 +24,16 @@ ol.geom.LineString = function(coordinates) { length = count * dimension; /** - * @type {Float64Array} + * @type {Array} */ - this.coordinates = new Float64Array(length); - for (var i = 0; i < count; ++i) { + this.coordinates = new Array(length); + var i, offset, j; + for (i = 0; i < count; ++i) { goog.asserts.assert(coordinates[i].length === dimension); - this.coordinates.set(coordinates[i], i * dimension); + offset = i * dimension; + for (j = 0; j < dimension; ++j) { + this.coordinates[offset + j] = coordinates[i][j]; + } } /** diff --git a/src/ol/geom/point.js b/src/ol/geom/point.js index 0409929adf..59790ce6ec 100644 --- a/src/ol/geom/point.js +++ b/src/ol/geom/point.js @@ -18,9 +18,9 @@ ol.geom.Point = function(coordinates) { goog.base(this); /** - * @type {Float64Array} + * @type {Array} */ - this.coordinates = new Float64Array(coordinates); + this.coordinates = coordinates; /** * @type {number} diff --git a/test/spec/ol/geom/linearring.test.js b/test/spec/ol/geom/linearring.test.js index ed67faa20d..117289b3fe 100644 --- a/test/spec/ol/geom/linearring.test.js +++ b/test/spec/ol/geom/linearring.test.js @@ -1,3 +1,5 @@ +goog.provide('ol.test.geom.LinearRing'); + describe('ol.geom.LinearRing', function() { describe('constructor', function() { @@ -17,9 +19,8 @@ describe('ol.geom.LinearRing', function() { describe('#coordinates', function() { - it('is a Float64Array', function() { + it('is an array', function() { var ring = new ol.geom.LinearRing([[10, 20], [30, 40]]); - expect(ring.coordinates).toBeA(Float64Array); expect(ring.coordinates.length).toBe(4); expect(ring.coordinates[0]).toBe(10); @@ -47,3 +48,4 @@ describe('ol.geom.LinearRing', function() { }); +goog.require('ol.geom.LinearRing'); diff --git a/test/spec/ol/geom/linestring.test.js b/test/spec/ol/geom/linestring.test.js index 3ddf89d8af..654ffcf208 100644 --- a/test/spec/ol/geom/linestring.test.js +++ b/test/spec/ol/geom/linestring.test.js @@ -20,9 +20,8 @@ describe('ol.geom.LineString', function() { describe('#coordinates', function() { - it('is a Float64Array', function() { + it('is an array', function() { var line = new ol.geom.LineString([[10, 20], [30, 40]]); - expect(line.coordinates).toBeA(Float64Array); expect(line.coordinates.length).toBe(4); expect(line.coordinates[0]).toBe(10); diff --git a/test/spec/ol/geom/point.test.js b/test/spec/ol/geom/point.test.js index 51031e02e9..b4276cfa45 100644 --- a/test/spec/ol/geom/point.test.js +++ b/test/spec/ol/geom/point.test.js @@ -20,9 +20,8 @@ describe('ol.geom.Point', function() { describe('#coordinates', function() { - it('is a Float64Array', function() { + it('is an array', function() { var point = new ol.geom.Point([10, 20]); - expect(point.coordinates).toBeA(Float64Array); expect(point.coordinates.length).toBe(2); expect(point.coordinates[0]).toBe(10); From 7ce89f1fbe692bc794c6a420e8812e49abb9895f Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 20 Feb 2013 10:32:51 -0700 Subject: [PATCH 083/180] Proper getAttributes method With a59aa9bb37765c602da1ec457631ae5a742c3af8 we can now provide an object with user set attributes. --- src/ol/feature.js | 20 +++++++++----------- test/spec/ol/feature.test.js | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/ol/feature.js b/src/ol/feature.js index f12deb2366..162a5d7a3a 100644 --- a/src/ol/feature.js +++ b/src/ol/feature.js @@ -28,17 +28,15 @@ goog.inherits(ol.Feature, ol.Object); * @return {Object} Attributes object. */ ol.Feature.prototype.getAttributes = function() { - // TODO: see https://github.com/openlayers/ol3/pull/217 - // var keys = this.getKeys(), - // len = keys.length, - // attributes = {}, - // i, key - // for (var i = 0; i < len; ++ i) { - // key = keys[i]; - // attributes[key] = this.get(key); - // } - // return attributes; - return this; + var keys = this.getKeys(), + len = keys.length, + attributes = {}, + i, key; + for (i = 0; i < len; ++ i) { + key = keys[i]; + attributes[key] = this.get(key); + } + return attributes; }; diff --git a/test/spec/ol/feature.test.js b/test/spec/ol/feature.test.js index 20cf3579ce..3431f46241 100644 --- a/test/spec/ol/feature.test.js +++ b/test/spec/ol/feature.test.js @@ -52,6 +52,29 @@ describe('ol.Feature', function() { }); + describe('#getAttributes()', function() { + + it('returns an object with all attributes', function() { + var point = new ol.geom.Point([15, 30]); + var feature = new ol.Feature({ + foo: 'bar', + ten: 10, + loc: point + }); + + var attributes = feature.getAttributes(); + + var keys = goog.object.getKeys(attributes); + expect(keys.sort()).toEqual(['foo', 'loc', 'ten']); + + expect(attributes.foo).toBe('bar'); + expect(attributes.loc).toBe(point); + expect(attributes.ten).toBe(10); + }); + + }); + + describe('#getGeometry()', function() { var point = new ol.geom.Point([15, 30]); @@ -157,5 +180,6 @@ describe('ol.Feature', function() { }); +goog.require('goog.object'); goog.require('ol.Feature'); goog.require('ol.geom.Point'); From e23ca1655c205ecbee9a00e7fb2ec04c2132f227 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 20 Feb 2013 10:34:10 -0700 Subject: [PATCH 084/180] Confirm that arbitrary attribute names can be used --- test/spec/ol/feature.test.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/spec/ol/feature.test.js b/test/spec/ol/feature.test.js index 3431f46241..ca932a9e1e 100644 --- a/test/spec/ol/feature.test.js +++ b/test/spec/ol/feature.test.js @@ -133,6 +133,22 @@ describe('ol.Feature', function() { expect(feature.getGeometry()).toBe(point); }); + it('can be used to set attributes with arbitrary names', function() { + + var feature = new ol.Feature(); + + feature.set('toString', 'string'); + expect(feature.get('toString')).toBe('string'); + expect(typeof feature.toString).toBe('function'); + + feature.set('getGeometry', 'x'); + expect(feature.get('getGeometry')).toBe('x'); + + feature.set('geom', new ol.geom.Point([1, 2])); + expect(feature.getGeometry()).toBeA(ol.geom.Point); + + }); + }); describe('#setGeometry()', function() { From da7820121fa5b11bad4b984b0fd0d5773497a909 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sat, 2 Mar 2013 12:50:53 +0100 Subject: [PATCH 085/180] Renaming the 'evaluate' method to 'applies' --- src/ol/filter/extentfilter.js | 2 +- src/ol/filter/filter.js | 2 +- src/ol/filter/geometryfilter.js | 2 +- src/ol/filter/logicalfilter.js | 4 ++-- src/ol/source/vectorsource.js | 2 +- test/spec/ol/filter/extentfilter.test.js | 4 ++-- test/spec/ol/source/vectorsource.test.js | 22 +++++++++++----------- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/ol/filter/extentfilter.js b/src/ol/filter/extentfilter.js index aeceaf446c..d919b03954 100644 --- a/src/ol/filter/extentfilter.js +++ b/src/ol/filter/extentfilter.js @@ -32,6 +32,6 @@ ol.filter.Extent.prototype.getExtent = function() { /** * @inheritDoc */ -ol.filter.Extent.prototype.evaluate = function(feature) { +ol.filter.Extent.prototype.applies = function(feature) { return feature.getGeometry().getBounds().intersects(this.extent_); }; diff --git a/src/ol/filter/filter.js b/src/ol/filter/filter.js index 45f0f3eff8..d41a6bed23 100644 --- a/src/ol/filter/filter.js +++ b/src/ol/filter/filter.js @@ -14,4 +14,4 @@ ol.filter.Filter = function() {}; * @param {ol.Feature} feature Feature to evaluate. * @return {boolean} The provided feature passes this filter. */ -ol.filter.Filter.prototype.evaluate = function(feature) {}; +ol.filter.Filter.prototype.applies = function(feature) {}; diff --git a/src/ol/filter/geometryfilter.js b/src/ol/filter/geometryfilter.js index df482353bb..11deacedd6 100644 --- a/src/ol/filter/geometryfilter.js +++ b/src/ol/filter/geometryfilter.js @@ -25,7 +25,7 @@ ol.filter.Geometry = function(type) { /** * @inheritDoc */ -ol.filter.Geometry.prototype.evaluate = function(feature) { +ol.filter.Geometry.prototype.applies = function(feature) { var geometry = feature.getGeometry(); return goog.isNull(geometry) ? false : geometry.getType() === this.type_; }; diff --git a/src/ol/filter/logicalfilter.js b/src/ol/filter/logicalfilter.js index 56842d8635..ccc8a1bda7 100644 --- a/src/ol/filter/logicalfilter.js +++ b/src/ol/filter/logicalfilter.js @@ -30,14 +30,14 @@ ol.filter.Logical = function(filters, operator) { /** * @inheritDoc */ -ol.filter.Logical.prototype.evaluate = function(feature) { +ol.filter.Logical.prototype.applies = function(feature) { var filters = this.filters_, i = 0, ii = filters.length, operator = this.operator, start = operator(true, false), result = start; while (result === start && i < ii) { - result = operator(result, filters[i].evaluate(feature)); + result = operator(result, filters[i].applies(feature)); ++i; } return result; diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index 27958b83f6..edbe74546f 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -128,7 +128,7 @@ ol.source.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) { features = {}; for (i in candidates) { feature = candidates[i]; - if (opt_filter.evaluate(feature) === true) { + if (opt_filter.applies(feature) === true) { features[i] = feature; } } diff --git a/test/spec/ol/filter/extentfilter.test.js b/test/spec/ol/filter/extentfilter.test.js index f4040d72f8..3a13cd4a8c 100644 --- a/test/spec/ol/filter/extentfilter.test.js +++ b/test/spec/ol/filter/extentfilter.test.js @@ -21,9 +21,9 @@ describe('ol.filter.Extent', function() { describe('#evaluate()', function() { it('returns true if a feature intersects, false if not', function() { - expect(filter.evaluate(new ol.Feature({g: new ol.geom.Point([44, 89])}))) + expect(filter.applies(new ol.Feature({g: new ol.geom.Point([44, 89])}))) .toBe(true); - expect(filter.evaluate(new ol.Feature({g: new ol.geom.Point([46, 91])}))) + expect(filter.applies(new ol.Feature({g: new ol.geom.Point([46, 91])}))) .toBe(false); }); diff --git a/test/spec/ol/source/vectorsource.test.js b/test/spec/ol/source/vectorsource.test.js index 8bf8df1f43..b84a1114b4 100644 --- a/test/spec/ol/source/vectorsource.test.js +++ b/test/spec/ol/source/vectorsource.test.js @@ -57,17 +57,17 @@ describe('ol.source.Vector', function() { var extentFilter = new ol.filter.Extent(new ol.Extent(16, 48, 16.3, 48.3)); it('can filter by geometry type using its GeometryType index', function() { - spyOn(geomFilter, 'evaluate'); + spyOn(geomFilter, 'applies'); var lineStrings = vectorSource.getFeatures(geomFilter); - expect(geomFilter.evaluate).not.toHaveBeenCalled(); + expect(geomFilter.applies).not.toHaveBeenCalled(); expect(lineStrings.length).toEqual(4); expect(lineStrings).toContain(features[4]); }); it('can filter by extent using its RTree', function() { - spyOn(extentFilter, 'evaluate'); + spyOn(extentFilter, 'applies'); var subset = vectorSource.getFeatures(extentFilter); - expect(extentFilter.evaluate).not.toHaveBeenCalled(); + expect(extentFilter.applies).not.toHaveBeenCalled(); expect(subset.length).toEqual(4); expect(subset).not.toContain(features[7]); }); @@ -77,22 +77,22 @@ describe('ol.source.Vector', function() { ol.filter.LogicalOperator.AND); var filter2 = new ol.filter.Logical([extentFilter, geomFilter], ol.filter.LogicalOperator.AND); - spyOn(filter1, 'evaluate'); - spyOn(filter2, 'evaluate'); + spyOn(filter1, 'applies'); + spyOn(filter2, 'applies'); var subset1 = vectorSource.getFeatures(filter1); var subset2 = vectorSource.getFeatures(filter2); - expect(filter1.evaluate).not.toHaveBeenCalled(); - expect(filter2.evaluate).not.toHaveBeenCalled(); + expect(filter1.applies).not.toHaveBeenCalled(); + expect(filter2.applies).not.toHaveBeenCalled(); expect(subset1.length).toEqual(0); expect(subset2.length).toEqual(0); }); - it('can handle query using the filter\'s evaluate function', function() { + it('can handle query using the filter\'s applies function', function() { var filter = new ol.filter.Logical([geomFilter, extentFilter], ol.filter.LogicalOperator.OR); - spyOn(filter, 'evaluate').andCallThrough(); + spyOn(filter, 'applies').andCallThrough(); var subset = vectorSource.getFeatures(filter); - expect(filter.evaluate).toHaveBeenCalled(); + expect(filter.applies).toHaveBeenCalled(); expect(subset.length).toEqual(8); }); From fcd5804d2d08e501c584f0ad51885e37def93bf2 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sat, 2 Mar 2013 14:08:52 +0100 Subject: [PATCH 086/180] Symbolizer and SymbolizerLiteral as base class, not interface --- src/ol/style/line.js | 8 ++++++-- src/ol/style/point.js | 14 ++++++++++---- src/ol/style/polygon.js | 8 ++++++-- src/ol/style/symbolizer.js | 6 +++--- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/ol/style/line.js b/src/ol/style/line.js index 32a5987ec4..3b0c6cd1cf 100644 --- a/src/ol/style/line.js +++ b/src/ol/style/line.js @@ -18,10 +18,11 @@ ol.style.LineLiteralOptions; /** * @constructor - * @implements {ol.style.SymbolizerLiteral} + * @extends {ol.style.SymbolizerLiteral} * @param {ol.style.LineLiteralOptions} config Symbolizer properties. */ ol.style.LineLiteral = function(config) { + goog.base(this); /** @type {string} */ this.strokeStyle = config.strokeStyle; @@ -33,6 +34,7 @@ ol.style.LineLiteral = function(config) { this.opacity = config.opacity; }; +goog.inherits(ol.style.LineLiteral, ol.style.SymbolizerLiteral); /** @@ -46,10 +48,11 @@ ol.style.LineOptions; /** * @constructor - * @implements {ol.style.Symbolizer} + * @extends {ol.style.Symbolizer} * @param {ol.style.LineOptions} options Symbolizer properties. */ ol.style.Line = function(options) { + goog.base(this); /** * @type {ol.Expression} @@ -79,6 +82,7 @@ ol.style.Line = function(options) { options.opacity : new ol.ExpressionLiteral(options.opacity); }; +goog.inherits(ol.style.Line, ol.style.Symbolizer); /** diff --git a/src/ol/style/point.js b/src/ol/style/point.js index b153203d84..20587391d1 100644 --- a/src/ol/style/point.js +++ b/src/ol/style/point.js @@ -8,17 +8,23 @@ goog.require('ol.style.SymbolizerLiteral'); /** * @constructor - * @implements {ol.style.SymbolizerLiteral} + * @extends {ol.style.SymbolizerLiteral} */ -ol.style.PointLiteral = function() {}; +ol.style.PointLiteral = function() { + goog.base(this); +}; +goog.inherits(ol.style.PointLiteral, ol.style.SymbolizerLiteral); /** * @constructor - * @implements {ol.style.Symbolizer} + * @extends {ol.style.Symbolizer} */ -ol.style.Point = function() {}; +ol.style.Point = function() { + goog.base(this); +}; +goog.inherits(ol.style.Point, ol.style.Symbolizer); /** diff --git a/src/ol/style/polygon.js b/src/ol/style/polygon.js index e2b2d52ba2..fe67bf677c 100644 --- a/src/ol/style/polygon.js +++ b/src/ol/style/polygon.js @@ -19,10 +19,11 @@ ol.style.PolygonLiteralOptions; /** * @constructor - * @implements {ol.style.SymbolizerLiteral} + * @extends {ol.style.SymbolizerLiteral} * @param {ol.style.PolygonLiteralOptions} config Symbolizer properties. */ ol.style.PolygonLiteral = function(config) { + goog.base(this); /** @type {string} */ this.fillStyle = config.fillStyle; @@ -37,6 +38,7 @@ ol.style.PolygonLiteral = function(config) { this.opacity = config.opacity; }; +goog.inherits(ol.style.PolygonLiteral, ol.style.SymbolizerLiteral); /** @@ -51,10 +53,11 @@ ol.style.PolygonOptions; /** * @constructor - * @implements {ol.style.Symbolizer} + * @extends {ol.style.Symbolizer} * @param {ol.style.PolygonOptions} options Symbolizer properties. */ ol.style.Polygon = function(options) { + goog.base(this); /** * @type {ol.Expression} @@ -93,6 +96,7 @@ ol.style.Polygon = function(options) { options.opacity : new ol.ExpressionLiteral(options.opacity); }; +goog.inherits(ol.style.Polygon, ol.style.Symbolizer); /** diff --git a/src/ol/style/symbolizer.js b/src/ol/style/symbolizer.js index f15cc79254..72bda71e16 100644 --- a/src/ol/style/symbolizer.js +++ b/src/ol/style/symbolizer.js @@ -6,14 +6,14 @@ goog.require('ol.Feature'); /** - * @interface + * @constructor */ ol.style.SymbolizerLiteral = function() {}; /** - * @interface + * @constructor */ ol.style.Symbolizer = function() {}; @@ -22,4 +22,4 @@ ol.style.Symbolizer = function() {}; * @param {ol.Feature} feature Feature for evaluating expressions. * @return {ol.style.SymbolizerLiteral} Literal symbolizer. */ -ol.style.Symbolizer.prototype.createLiteral = function(feature) {}; +ol.style.Symbolizer.prototype.createLiteral = goog.abstractMethod; From f4a4522eb41bed2b7072c3a5696529001005b6c1 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sat, 2 Mar 2013 17:38:01 +0100 Subject: [PATCH 087/180] Giving vector layers the style they deserve Now vector layers can have a style. ol.Style instances have an apply method to get the symbolizer literals for a feature. If the layer does not have a style defined, there is also a static applyDefaultStyle function on ol.Style to get the default symbolizer literals for a feature. The vector layer also got a groupFeaturesBySymbolizerLiteral method, which returns an array with features grouped by symbolizer, as needed by the canvas renderer. --- src/ol/layer/vectorlayer.js | 44 ++++++++++++++++++ .../canvas/canvasvectorlayerrenderer.js | 46 +++++-------------- src/ol/style/line.js | 10 ++++ src/ol/style/polygon.js | 11 +++++ src/ol/style/rule.js | 19 ++++++++ src/ol/style/shape.js | 13 ++++++ src/ol/style/style.js | 46 +++++++++++++++++++ src/ol/style/symbolizer.js | 8 ++++ 8 files changed, 162 insertions(+), 35 deletions(-) diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 6ef234f60d..38b7367a22 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -1,7 +1,9 @@ goog.provide('ol.layer.Vector'); +goog.require('ol.Feature'); goog.require('ol.layer.Layer'); goog.require('ol.source.Vector'); +goog.require('ol.style.Style'); @@ -12,6 +14,13 @@ goog.require('ol.source.Vector'); */ ol.layer.Vector = function(layerOptions) { goog.base(this, layerOptions); + + /** + * @private + * @type {ol.style.Style} + */ + this.style_ = goog.isDef(layerOptions.style) ? layerOptions.style : null; + }; goog.inherits(ol.layer.Vector, ol.layer.Layer); @@ -22,3 +31,38 @@ goog.inherits(ol.layer.Vector, ol.layer.Layer); ol.layer.Vector.prototype.getVectorSource = function() { return /** @type {ol.source.Vector} */ (this.getSource()); }; + + +/** + * @param {Array.} features Features. + * @return {Array.} symbolizers for features. + */ +ol.layer.Vector.prototype.groupFeaturesBySymbolizerLiteral = + function(features) { + var uniqueLiterals = {}, + featuresBySymbolizer = [], + style = this.style_, + feature, literals, literal, key; + for (var i = 0, ii = features.length; i < ii; ++i) { + feature = features[i]; + literals = goog.isNull(style) ? + ol.style.Style.applyDefaultStyle(feature) : + style.apply(feature); + for (var j = 0, jj = literals.length; j < jj; ++j) { + literal = literals[j]; + for (var l in uniqueLiterals) { + if (literal.equals(uniqueLiterals[l])) { + literal = uniqueLiterals[l]; + break; + } + } + key = goog.getUid(literal); + if (!goog.object.containsKey(uniqueLiterals, key)) { + uniqueLiterals[key] = featuresBySymbolizer.length; + featuresBySymbolizer.push([[], literal]); + } + featuresBySymbolizer[uniqueLiterals[key]][0].push(feature); + } + } + return featuresBySymbolizer; +}; diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 5efe58ade3..38a82edfb7 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -14,10 +14,6 @@ goog.require('ol.geom.GeometryType'); goog.require('ol.layer.Vector'); goog.require('ol.renderer.canvas.Layer'); goog.require('ol.renderer.canvas.Renderer'); -goog.require('ol.style.LineLiteral'); -goog.require('ol.style.PolygonLiteral'); -goog.require('ol.style.ShapeLiteral'); -goog.require('ol.style.ShapeType'); goog.require('ol.tilegrid.TileGrid'); @@ -115,31 +111,6 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { this.dirty_ = false; - // TODO: implement layer.setStyle(style) where style is a set of rules - // and a rule has a filter and array of symbolizers - var symbolizers = {}; - symbolizers[ol.geom.GeometryType.POINT] = new ol.style.ShapeLiteral({ - type: ol.style.ShapeType.CIRCLE, - size: 10, - fillStyle: '#ffcc99', - strokeStyle: '#ff9933', - strokeWidth: 2, - opacity: 0.75 - }); - symbolizers[ol.geom.GeometryType.LINESTRING] = new ol.style.LineLiteral({ - strokeStyle: '#ff9933', - strokeWidth: 2, - opacity: 1 - }); - symbolizers[ol.geom.GeometryType.POLYGON] = new ol.style.PolygonLiteral({ - fillStyle: '#ffcc99', - strokeStyle: '#ff9933', - strokeWidth: 2, - opacity: 0.5 - }); - // TODO: remove this - this.symbolizers_ = symbolizers; - /** * @private * @type {boolean} @@ -195,7 +166,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var view2DState = frameState.view2DState, resolution = view2DState.resolution, extent = frameState.extent, - source = this.getVectorLayer().getVectorSource(), + layer = this.getVectorLayer(), + source = layer.getVectorSource(), tileGrid = this.tileGrid_; if (goog.isNull(tileGrid)) { @@ -295,7 +267,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = // render features by geometry type var filters = this.geometryFilters_, numFilters = filters.length, - i, geomFilter, extentFilter, type, features, symbolizer; + i, geomFilter, extentFilter, type, features, + groups, group, j, numGroups; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { tileCoord = new ol.TileCoord(z, x, y); @@ -312,10 +285,13 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = features = source.getFeatures(new ol.filter.Logical( [geomFilter, extentFilter], ol.filter.LogicalOperator.AND)); if (features.length) { - // TODO: layer.getSymbolizerLiterals(features) or similar - symbolizer = this.symbolizers_[type]; - sketchCanvasRenderer.renderFeaturesByGeometryType( - type, features, symbolizer); + groups = layer.groupFeaturesBySymbolizerLiteral(features); + numGroups = groups.length; + for (j = 0; j < numGroups; ++j) { + group = groups[j]; + sketchCanvasRenderer.renderFeaturesByGeometryType(type, + group[0], group[1]); + } } } } diff --git a/src/ol/style/line.js b/src/ol/style/line.js index 3b0c6cd1cf..7d8519067f 100644 --- a/src/ol/style/line.js +++ b/src/ol/style/line.js @@ -37,6 +37,16 @@ ol.style.LineLiteral = function(config) { goog.inherits(ol.style.LineLiteral, ol.style.SymbolizerLiteral); +/** + * @inheritDoc + */ +ol.style.LineLiteral.prototype.equals = function(lineLiteral) { + return this.strokeStyle == lineLiteral.strokeStyle && + this.strokeWidth == lineLiteral.strokeWidth && + this.opacity == lineLiteral.opacity; +}; + + /** * @typedef {{strokeStyle: (string|ol.Expression), * strokeWidth: (number|ol.Expression), diff --git a/src/ol/style/polygon.js b/src/ol/style/polygon.js index fe67bf677c..5886ccf474 100644 --- a/src/ol/style/polygon.js +++ b/src/ol/style/polygon.js @@ -41,6 +41,17 @@ ol.style.PolygonLiteral = function(config) { goog.inherits(ol.style.PolygonLiteral, ol.style.SymbolizerLiteral); +/** + * @inheritDoc + */ +ol.style.PolygonLiteral.prototype.equals = function(polygonLiteral) { + return this.fillStyle == polygonLiteral.fillStyle && + this.strokeStyle == polygonLiteral.strokeStyle && + this.strokeWidth == polygonLiteral.strokeWidth && + this.opacity == polygonLiteral.opacity; +}; + + /** * @typedef {{fillStyle: (string|ol.Expression), * strokeStyle: (string|ol.Expression), diff --git a/src/ol/style/rule.js b/src/ol/style/rule.js index 90e7ae8b00..14b054a3d3 100644 --- a/src/ol/style/rule.js +++ b/src/ol/style/rule.js @@ -1,5 +1,6 @@ goog.provide('ol.style.Rule'); +goog.require('ol.Feature'); goog.require('ol.filter.Filter'); goog.require('ol.style.Symbolizer'); @@ -32,3 +33,21 @@ ol.style.Rule = function(options) { options.symbolizers : []; }; + + +/** + * @param {ol.Feature} feature Feature. + * @return {boolean} Does the rule apply to the feature? + */ +ol.style.Rule.prototype.applies = function(feature) { + return goog.isNull(this.filter_) ? true : this.filter_.applies(feature); +}; + + +/** + * @return {Array.} Symbolizers. + */ +ol.style.Rule.prototype.getSymbolizers = function() { + return this.symbolizers_; +}; + diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js index e3007f5205..92c407c452 100644 --- a/src/ol/style/shape.js +++ b/src/ol/style/shape.js @@ -57,6 +57,19 @@ ol.style.ShapeLiteral = function(config) { goog.inherits(ol.style.ShapeLiteral, ol.style.PointLiteral); +/** + * @inheritDoc + */ +ol.style.ShapeLiteral.prototype.equals = function(shapeLiteral) { + return this.type == shapeLiteral.type && + this.size == shapeLiteral.size && + this.fillStyle == shapeLiteral.fillStyle && + this.strokeStyle == shapeLiteral.strokeStyle && + this.strokeWidth == shapeLiteral.strokeWidth && + this.opacity == shapeLiteral.opacity; +}; + + /** * @typedef {{type: (ol.style.ShapeType), * size: (number|ol.Expression), diff --git a/src/ol/style/style.js b/src/ol/style/style.js index 536c8dac6c..86271dcaf9 100644 --- a/src/ol/style/style.js +++ b/src/ol/style/style.js @@ -1,6 +1,9 @@ goog.provide('ol.style.Style'); +goog.require('ol.Feature'); +goog.require('ol.geom.GeometryType'); goog.require('ol.style.Rule'); +goog.require('ol.style.SymbolizerLiteral'); /** @@ -23,3 +26,46 @@ ol.style.Style = function(options) { this.rules_ = goog.isDef(options.rules) ? options.rules : []; }; + + +/** + * @param {ol.Feature} feature Feature. + * @return {Array.} Symbolizer literals for the + * feature. + */ +ol.style.Style.prototype.apply = function(feature) { + var rules = this.rules_, + literals = [], + rule, symbolizers; + for (var i = 0, ii = rules.length; i < ii; ++i) { + rule = rules[i]; + if (rule.applies(feature)) { + symbolizers = rule.getSymbolizers(); + for (var j = 0, jj = symbolizers.length; j < jj; ++j) { + literals.push(symbolizers[j].createLiteral(feature)); + } + } + } + return literals; +}; + + +/** + * @param {ol.Feature} feature Feature. + * @return {Array.} Default symbolizer literals for + * the feature. + */ +ol.style.Style.applyDefaultStyle = function(feature) { + var type = feature.getGeometry().getType(); + if (type === ol.geom.GeometryType.POINT || + type === ol.geom.GeometryType.MULTIPOINT) { + return [ol.style.ShapeDefaults]; + } else if (type === ol.geom.GeometryType.LINESTRING || + type === ol.geom.GeometryType.MULTILINESTRING) { + return [ol.style.LineDefaults]; + } else if (type === ol.geom.GeometryType.LINEARRING || + type === ol.geom.GeometryType.POLYGON || + type === ol.geom.GeometryType.MULTIPOLYGON) { + return [ol.style.PolygonDefaults]; + } +}; diff --git a/src/ol/style/symbolizer.js b/src/ol/style/symbolizer.js index 72bda71e16..36bec5fbdc 100644 --- a/src/ol/style/symbolizer.js +++ b/src/ol/style/symbolizer.js @@ -11,6 +11,14 @@ goog.require('ol.Feature'); ol.style.SymbolizerLiteral = function() {}; +/** + * @param {ol.style.SymbolizerLiteral} symbolizerLiteral Symbolizer literal to + * compare to. + * @return {boolean} Is the passed symbolizer literal equal to this instance? + */ +ol.style.SymbolizerLiteral.prototype.equals = goog.abstractMethod; + + /** * @constructor From bdfa2cc88c2f9b523fa228e02f714bf16899eef9 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sat, 2 Mar 2013 17:44:15 +0100 Subject: [PATCH 088/180] Fixing return type to make Closure compiler happy --- src/ol/style/style.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ol/style/style.js b/src/ol/style/style.js index 86271dcaf9..573c3c80f9 100644 --- a/src/ol/style/style.js +++ b/src/ol/style/style.js @@ -56,16 +56,18 @@ ol.style.Style.prototype.apply = function(feature) { * the feature. */ ol.style.Style.applyDefaultStyle = function(feature) { - var type = feature.getGeometry().getType(); + var type = feature.getGeometry().getType(), + symbolizerLiterals = []; if (type === ol.geom.GeometryType.POINT || type === ol.geom.GeometryType.MULTIPOINT) { - return [ol.style.ShapeDefaults]; + symbolizerLiterals.push(ol.style.ShapeDefaults); } else if (type === ol.geom.GeometryType.LINESTRING || type === ol.geom.GeometryType.MULTILINESTRING) { - return [ol.style.LineDefaults]; + symbolizerLiterals.push(ol.style.LineDefaults); } else if (type === ol.geom.GeometryType.LINEARRING || type === ol.geom.GeometryType.POLYGON || type === ol.geom.GeometryType.MULTIPOLYGON) { - return [ol.style.PolygonDefaults]; + symbolizerLiterals.push(ol.style.PolygonDefaults); } + return symbolizerLiterals; }; From b52d2836414d6f3b823b789c2cc996fa76f9325f Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 2 Mar 2013 18:39:24 +0100 Subject: [PATCH 089/180] 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() { From 69b6fae744ed731aea46fdbf21308dd8cfe05803 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 3 Mar 2013 11:50:51 +0100 Subject: [PATCH 090/180] Parser base class GeoJSON is now a parser. The idea is that we'll want to pass parsers to sources or protocols and want a known type. --- src/ol/parser/geojson.js | 60 +++++++++++++++++------------ src/ol/parser/parser.js | 21 ++++++++++ src/ol/parser/xml.js | 4 ++ test/spec/ol/parser/geojson.test.js | 23 +++++++---- 4 files changed, 75 insertions(+), 33 deletions(-) create mode 100644 src/ol/parser/parser.js diff --git a/src/ol/parser/geojson.js b/src/ol/parser/geojson.js index 1df8ebf4d4..9bbabaff23 100644 --- a/src/ol/parser/geojson.js +++ b/src/ol/parser/geojson.js @@ -1,4 +1,4 @@ -goog.provide('ol.parser.geojson'); +goog.provide('ol.parser.GeoJSON'); goog.require('ol.Feature'); goog.require('ol.geom.Geometry'); @@ -8,6 +8,16 @@ goog.require('ol.geom.MultiPoint'); goog.require('ol.geom.MultiPolygon'); goog.require('ol.geom.Point'); goog.require('ol.geom.Polygon'); +goog.require('ol.parser.Parser'); + + + +/** + * @constructor + * @extends {ol.parser.Parser} + */ +ol.parser.GeoJSON = function() {}; +goog.inherits(ol.parser.GeoJSON, ol.parser.Parser); /** @@ -17,10 +27,10 @@ goog.require('ol.geom.Polygon'); * ol.geom.Geometry|Array.} Parsed geometry or array * of geometries. */ -ol.parser.geojson.read = function(str) { +ol.parser.GeoJSON.prototype.read = function(str) { // TODO: add options and accept projection var json = /** @type {GeoJSONObject} */ (JSON.parse(str)); - return ol.parser.geojson.parse_(json); + return ol.parser.GeoJSON.prototype.parse_(json); }; @@ -31,43 +41,43 @@ ol.parser.geojson.read = function(str) { * of geometries. * @private */ -ol.parser.geojson.parse_ = function(json) { +ol.parser.GeoJSON.prototype.parse_ = function(json) { var result; switch (json.type) { case 'FeatureCollection': - result = ol.parser.geojson.parseFeatureCollection_( + result = this.parseFeatureCollection_( /** @type {GeoJSONFeatureCollection} */ (json)); break; case 'Feature': - result = ol.parser.geojson.parseFeature_( + result = this.parseFeature_( /** @type {GeoJSONFeature} */ (json)); break; case 'GeometryCollection': - result = ol.parser.geojson.parseGeometryCollection_( + result = this.parseGeometryCollection_( /** @type {GeoJSONGeometryCollection} */ (json)); break; case 'Point': - result = ol.parser.geojson.parsePoint_( + result = this.parsePoint_( /** @type {GeoJSONGeometry} */ (json)); break; case 'LineString': - result = ol.parser.geojson.parseLineString_( + result = this.parseLineString_( /** @type {GeoJSONGeometry} */ (json)); break; case 'Polygon': - result = ol.parser.geojson.parsePolygon_( + result = this.parsePolygon_( /** @type {GeoJSONGeometry} */ (json)); break; case 'MultiPoint': - result = ol.parser.geojson.parseMultiPoint_( + result = this.parseMultiPoint_( /** @type {GeoJSONGeometry} */ (json)); break; case 'MultiLineString': - result = ol.parser.geojson.parseMultiLineString_( + result = this.parseMultiLineString_( /** @type {GeoJSONGeometry} */ (json)); break; case 'MultiPolygon': - result = ol.parser.geojson.parseMultiPolygon_( + result = this.parseMultiPolygon_( /** @type {GeoJSONGeometry} */ (json)); break; default: @@ -82,11 +92,11 @@ ol.parser.geojson.parse_ = function(json) { * @return {ol.Feature} Parsed feature. * @private */ -ol.parser.geojson.parseFeature_ = function(json) { +ol.parser.GeoJSON.prototype.parseFeature_ = function(json) { var geomJson = json.geometry, geometry = null; if (geomJson) { - geometry = /** @type {ol.geom.Geometry} */ (ol.parser.geojson.parse_( + geometry = /** @type {ol.geom.Geometry} */ (this.parse_( /** @type {GeoJSONGeometry} */ (geomJson))); } var feature = new ol.Feature(); @@ -101,14 +111,14 @@ ol.parser.geojson.parseFeature_ = function(json) { * @return {Array.} Parsed array of features. * @private */ -ol.parser.geojson.parseFeatureCollection_ = function(json) { +ol.parser.GeoJSON.prototype.parseFeatureCollection_ = function(json) { var features = json.features, len = features.length, result = new Array(len), i; for (i = 0; i < len; ++i) { - result[i] = ol.parser.geojson.parse_( + result[i] = this.parse_( /** @type {GeoJSONFeature} */ (features[i])); } return result; @@ -120,14 +130,14 @@ ol.parser.geojson.parseFeatureCollection_ = function(json) { * @return {Array.} Parsed array of geometries. * @private */ -ol.parser.geojson.parseGeometryCollection_ = function(json) { +ol.parser.GeoJSON.prototype.parseGeometryCollection_ = function(json) { var geometries = json.geometries, len = geometries.length, result = new Array(len), i; for (i = 0; i < len; ++i) { - result[i] = ol.parser.geojson.parse_( + result[i] = this.parse_( /** @type {GeoJSONGeometry} */ (geometries[i])); } return result; @@ -139,7 +149,7 @@ ol.parser.geojson.parseGeometryCollection_ = function(json) { * @return {ol.geom.LineString} Parsed linestring. * @private */ -ol.parser.geojson.parseLineString_ = function(json) { +ol.parser.GeoJSON.prototype.parseLineString_ = function(json) { return new ol.geom.LineString(json.coordinates); }; @@ -149,7 +159,7 @@ ol.parser.geojson.parseLineString_ = function(json) { * @return {ol.geom.MultiLineString} Parsed multi-linestring. * @private */ -ol.parser.geojson.parseMultiLineString_ = function(json) { +ol.parser.GeoJSON.prototype.parseMultiLineString_ = function(json) { return new ol.geom.MultiLineString(json.coordinates); }; @@ -159,7 +169,7 @@ ol.parser.geojson.parseMultiLineString_ = function(json) { * @return {ol.geom.MultiPoint} Parsed multi-point. * @private */ -ol.parser.geojson.parseMultiPoint_ = function(json) { +ol.parser.GeoJSON.prototype.parseMultiPoint_ = function(json) { return new ol.geom.MultiPoint(json.coordinates); }; @@ -169,7 +179,7 @@ ol.parser.geojson.parseMultiPoint_ = function(json) { * @return {ol.geom.MultiPolygon} Parsed multi-polygon. * @private */ -ol.parser.geojson.parseMultiPolygon_ = function(json) { +ol.parser.GeoJSON.prototype.parseMultiPolygon_ = function(json) { return new ol.geom.MultiPolygon(json.coordinates); }; @@ -179,7 +189,7 @@ ol.parser.geojson.parseMultiPolygon_ = function(json) { * @return {ol.geom.Point} Parsed multi-point. * @private */ -ol.parser.geojson.parsePoint_ = function(json) { +ol.parser.GeoJSON.prototype.parsePoint_ = function(json) { return new ol.geom.Point(json.coordinates); }; @@ -189,6 +199,6 @@ ol.parser.geojson.parsePoint_ = function(json) { * @return {ol.geom.Polygon} Parsed polygon. * @private */ -ol.parser.geojson.parsePolygon_ = function(json) { +ol.parser.GeoJSON.prototype.parsePolygon_ = function(json) { return new ol.geom.Polygon(json.coordinates); }; diff --git a/src/ol/parser/parser.js b/src/ol/parser/parser.js new file mode 100644 index 0000000000..1c96acc41a --- /dev/null +++ b/src/ol/parser/parser.js @@ -0,0 +1,21 @@ +goog.provide('ol.parser.Parser'); + + + +/** + * @constructor + */ +ol.parser.Parser = function() {}; + +/** + * @param {*} data Data to deserialize. + * @return {*} Parsed data. + */ +ol.parser.Parser.prototype.read = goog.abstractMethod; + + +/** + * @param {*} obj Object to serialize. + * @return {*} Serialized object. + */ +ol.parser.Parser.prototype.write = goog.abstractMethod; diff --git a/src/ol/parser/xml.js b/src/ol/parser/xml.js index 6e07cf645c..20c14d21af 100644 --- a/src/ol/parser/xml.js +++ b/src/ol/parser/xml.js @@ -1,9 +1,12 @@ goog.provide('ol.parser.XML'); +goog.require('ol.parser.Parser'); + /** * @constructor + * @extends {ol.parser.Parser} */ ol.parser.XML = function() { this.regExes = { @@ -13,6 +16,7 @@ ol.parser.XML = function() { trimComma: (/\s*,\s*/g) }; }; +goog.inherits(ol.parser.XML, ol.parser.Parser); /** diff --git a/test/spec/ol/parser/geojson.test.js b/test/spec/ol/parser/geojson.test.js index 624b95b07b..9914a06b44 100644 --- a/test/spec/ol/parser/geojson.test.js +++ b/test/spec/ol/parser/geojson.test.js @@ -1,6 +1,8 @@ -goog.provide('ol.test.parser.geojson'); +goog.provide('ol.test.parser.GeoJSON'); -describe('ol.parser.geojson', function() { +describe('ol.parser.GeoJSON', function() { + + var parser = new ol.parser.GeoJSON(); var data = { 'type': 'FeatureCollection', @@ -74,7 +76,7 @@ describe('ol.parser.geojson', function() { coordinates: [10, 20] }); - var obj = ol.parser.geojson.read(str); + var obj = parser.read(str); expect(obj).toBeA(ol.geom.Point); expect(obj.getCoordinates()).toEqual([10, 20]); }); @@ -85,7 +87,7 @@ describe('ol.parser.geojson', function() { coordinates: [[10, 20], [30, 40]] }); - var obj = ol.parser.geojson.read(str); + var obj = parser.read(str); expect(obj).toBeA(ol.geom.LineString); expect(obj.getCoordinates()).toEqual([[10, 20], [30, 40]]); }); @@ -99,7 +101,7 @@ describe('ol.parser.geojson', function() { coordinates: [outer, inner1, inner2] }); - var obj = ol.parser.geojson.read(str); + var obj = parser.read(str); expect(obj).toBeA(ol.geom.Polygon); expect(obj.rings.length).toBe(3); expect(obj.rings[0]).toBeA(ol.geom.LinearRing); @@ -116,7 +118,7 @@ describe('ol.parser.geojson', function() { ] }); - var array = ol.parser.geojson.read(str); + var array = parser.read(str); expect(array.length).toBe(2); expect(array[0]).toBeA(ol.geom.Point); expect(array[1]).toBeA(ol.geom.LineString); @@ -124,7 +126,7 @@ describe('ol.parser.geojson', function() { it('parses feature collection', function() { var str = JSON.stringify(data), - array = ol.parser.geojson.read(str); + array = parser.read(str); expect(array.length).toBe(2); @@ -145,4 +147,9 @@ describe('ol.parser.geojson', function() { }); -goog.require('ol.parser.geojson'); +goog.require('ol.Feature'); +goog.require('ol.geom.LinearRing'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.parser.GeoJSON'); From 5d917ebed603061bc0831c3c132ad6355f565dcb Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 3 Mar 2013 14:29:22 +0100 Subject: [PATCH 091/180] Helper functions for running tests after xhr --- test/jasmine-extensions.js | 84 ++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/test/jasmine-extensions.js b/test/jasmine-extensions.js index 4649130908..93fe89f152 100644 --- a/test/jasmine-extensions.js +++ b/test/jasmine-extensions.js @@ -1,11 +1,77 @@ beforeEach(function() { - var parent = this.getMatchersClass_(); - this.addMatchers({ - toBeA: function(type) { - return this.actual instanceof type; - }, - toRoughlyEqual: function(other, tol) { - return Math.abs(this.actual - other) <= tol; - } - }); + var parent = this.getMatchersClass_(); + this.addMatchers({ + toBeA: function(type) { + return this.actual instanceof type; + }, + toRoughlyEqual: function(other, tol) { + return Math.abs(this.actual - other) <= tol; + } + }); }); + +// helper functions for async testing +(function(global) { + + function afterLoad(type, path, next) { + var done, error, data; + runs(function() { + goog.net.XhrIo.send(path, function(event) { + var xhr = event.target; + if (xhr.isSuccess()) { + if (type === 'xml') { + data = xhr.getResponseXml(); + } else if (type === 'json') { + data = xhr.getResponseJson(); + } else { + data = xhr.getResponseText(); + } + } else { + error = new Error(path + ' loading failed: ' + xhr.getStatus()); + } + done = true; + }); + }); + waitsFor(function() { + return done; + }); + runs(function() { + if (error) { + throw error; + } + next(data); + }); + } + + + /** + * @param {string} path Relative path to file (e.g. 'spec/ol/foo.json'). + * @param {function(Object)} next Function to call with response object on + * success. On failure, an error is thrown with the reason. + */ + global.afterLoadJson = function(path, next) { + afterLoad('json', path, next); + }; + + + /** + * @param {string} path Relative path to file (e.g. 'spec/ol/foo.txt'). + * @param {function(string)} next Function to call with response text on + * success. On failure, an error is thrown with the reason. + */ + global.afterLoadText = function(path, next) { + afterLoad('text', path, next); + }; + + + /** + * @param {string} path Relative path to file (e.g. 'spec/ol/foo.xml'). + * @param {function(Document)} next Function to call with response xml on + * success. On failure, an error is thrown with the reason. + */ + global.afterLoadXml = function(path, next) { + afterLoad('xml', path, next); + }; + +})(this); + From 9d1f737bcedd02608d439b7b85c68d33d047555f Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 3 Mar 2013 14:25:11 +0100 Subject: [PATCH 092/180] ol.Filter is now a (base) class, not an interface any more ol.Filter can now also be used to provide a simple filter that just evaluates a user provided filter function against a feature. --- src/ol/filter/extentfilter.js | 4 +++- src/ol/filter/filter.js | 15 +++++++++++---- src/ol/filter/geometryfilter.js | 4 +++- src/ol/filter/logicalfilter.js | 4 +++- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/ol/filter/extentfilter.js b/src/ol/filter/extentfilter.js index d919b03954..b8738bfa73 100644 --- a/src/ol/filter/extentfilter.js +++ b/src/ol/filter/extentfilter.js @@ -7,10 +7,11 @@ goog.require('ol.filter.Filter'); /** * @constructor - * @implements {ol.filter.Filter} + * @extends {ol.filter.Filter} * @param {ol.Extent} extent The extent. */ ol.filter.Extent = function(extent) { + goog.base(this); /** * @type {ol.Extent} @@ -19,6 +20,7 @@ ol.filter.Extent = function(extent) { this.extent_ = extent; }; +goog.inherits(ol.filter.Extent, ol.filter.Filter); /** diff --git a/src/ol/filter/filter.js b/src/ol/filter/filter.js index d41a6bed23..701a942ae7 100644 --- a/src/ol/filter/filter.js +++ b/src/ol/filter/filter.js @@ -5,13 +5,20 @@ goog.require('ol.Feature'); /** - * @interface + * @constructor + * @param {function(this:ol.filter.Filter, ol.Feature)=} opt_filterFunction + * Filter function. Should return true if the passed feature passes the + * filter, false otherwise. */ -ol.filter.Filter = function() {}; +ol.filter.Filter = function(opt_filterFunction) { + if (goog.isDef(opt_filterFunction)) { + this.applies = opt_filterFunction; + } +}; /** - * @param {ol.Feature} feature Feature to evaluate. + * @param {ol.Feature} feature Feature to evaluate the filter against. * @return {boolean} The provided feature passes this filter. */ -ol.filter.Filter.prototype.applies = function(feature) {}; +ol.filter.Filter.prototype.applies = goog.abstractMethod; diff --git a/src/ol/filter/geometryfilter.js b/src/ol/filter/geometryfilter.js index 11deacedd6..f48a67b14b 100644 --- a/src/ol/filter/geometryfilter.js +++ b/src/ol/filter/geometryfilter.js @@ -8,10 +8,11 @@ goog.require('ol.geom.GeometryType'); /** * @constructor - * @implements {ol.filter.Filter} + * @extends {ol.filter.Filter} * @param {ol.geom.GeometryType} type The geometry type. */ ol.filter.Geometry = function(type) { + goog.base(this); /** * @type {ol.geom.GeometryType} @@ -20,6 +21,7 @@ ol.filter.Geometry = function(type) { this.type_ = type; }; +goog.inherits(ol.filter.Geometry, ol.filter.Filter); /** diff --git a/src/ol/filter/logicalfilter.js b/src/ol/filter/logicalfilter.js index ccc8a1bda7..89f438fc33 100644 --- a/src/ol/filter/logicalfilter.js +++ b/src/ol/filter/logicalfilter.js @@ -7,11 +7,12 @@ goog.require('ol.filter.Filter'); /** * @constructor - * @implements {ol.filter.Filter} + * @extends {ol.filter.Filter} * @param {Array.} filters Filters to and-combine. * @param {!ol.filter.LogicalOperator} operator Operator. */ ol.filter.Logical = function(filters, operator) { + goog.base(this); /** * @type {Array.} @@ -25,6 +26,7 @@ ol.filter.Logical = function(filters, operator) { this.operator = operator; }; +goog.inherits(ol.filter.Logical, ol.filter.Filter); /** From db52ff926e3578e13505ca094d221c32c394edb9 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 3 Mar 2013 15:22:33 +0100 Subject: [PATCH 093/180] Return an empty array for features without geometry This ensures that the static applyDefaultStyle() method works exactly the same way as the instance's apply() method. --- src/ol/style/style.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/ol/style/style.js b/src/ol/style/style.js index 573c3c80f9..e1afc68665 100644 --- a/src/ol/style/style.js +++ b/src/ol/style/style.js @@ -56,18 +56,21 @@ ol.style.Style.prototype.apply = function(feature) { * the feature. */ ol.style.Style.applyDefaultStyle = function(feature) { - var type = feature.getGeometry().getType(), + var geometry = feature.getGeometry(), symbolizerLiterals = []; - if (type === ol.geom.GeometryType.POINT || - type === ol.geom.GeometryType.MULTIPOINT) { - symbolizerLiterals.push(ol.style.ShapeDefaults); - } else if (type === ol.geom.GeometryType.LINESTRING || - type === ol.geom.GeometryType.MULTILINESTRING) { - symbolizerLiterals.push(ol.style.LineDefaults); - } else if (type === ol.geom.GeometryType.LINEARRING || - type === ol.geom.GeometryType.POLYGON || - type === ol.geom.GeometryType.MULTIPOLYGON) { - symbolizerLiterals.push(ol.style.PolygonDefaults); + if (!goog.isNull(geometry)) { + var type = geometry.getType(); + if (type === ol.geom.GeometryType.POINT || + type === ol.geom.GeometryType.MULTIPOINT) { + symbolizerLiterals.push(ol.style.ShapeDefaults); + } else if (type === ol.geom.GeometryType.LINESTRING || + type === ol.geom.GeometryType.MULTILINESTRING) { + symbolizerLiterals.push(ol.style.LineDefaults); + } else if (type === ol.geom.GeometryType.LINEARRING || + type === ol.geom.GeometryType.POLYGON || + type === ol.geom.GeometryType.MULTIPOLYGON) { + symbolizerLiterals.push(ol.style.PolygonDefaults); + } } return symbolizerLiterals; }; From 63c048edb276c8fd5ca0ccba666a1eb4c8e861ab Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 3 Mar 2013 15:22:52 +0100 Subject: [PATCH 094/180] Use the actual value, not just the index in the array. --- src/ol/layer/vectorlayer.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 38b7367a22..6397544d57 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -42,7 +42,7 @@ ol.layer.Vector.prototype.groupFeaturesBySymbolizerLiteral = var uniqueLiterals = {}, featuresBySymbolizer = [], style = this.style_, - feature, literals, literal, key; + feature, literals, literal, uniqueLiteral, key; for (var i = 0, ii = features.length; i < ii; ++i) { feature = features[i]; literals = goog.isNull(style) ? @@ -51,8 +51,9 @@ ol.layer.Vector.prototype.groupFeaturesBySymbolizerLiteral = for (var j = 0, jj = literals.length; j < jj; ++j) { literal = literals[j]; for (var l in uniqueLiterals) { - if (literal.equals(uniqueLiterals[l])) { - literal = uniqueLiterals[l]; + uniqueLiteral = featuresBySymbolizer[uniqueLiterals[key]][1]; + if (literal.equals(uniqueLiteral)) { + literal = uniqueLiteral; break; } } From 5535a26d4a0bf03f3d0b5617e4a1d861f035a08a Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 3 Mar 2013 15:25:20 +0100 Subject: [PATCH 095/180] Adding unit tests for rule based styling --- test/spec/ol/layer/vectorlayer.test.js | 53 +++++++++++++++++++++ test/spec/ol/style/line.test.js | 25 ++++++++++ test/spec/ol/style/polygon.test.js | 25 ++++++++++ test/spec/ol/style/rule.test.js | 33 +++++++++++++ test/spec/ol/style/shape.test.js | 25 ++++++++++ test/spec/ol/style/style.test.js | 66 ++++++++++++++++++++++++++ 6 files changed, 227 insertions(+) create mode 100644 test/spec/ol/layer/vectorlayer.test.js create mode 100644 test/spec/ol/style/rule.test.js create mode 100644 test/spec/ol/style/style.test.js diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js new file mode 100644 index 0000000000..1c5ccd4f79 --- /dev/null +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -0,0 +1,53 @@ +goog.provide('ol.test.layer.Vector'); + +describe('ol.layer.Vector', function() { + + describe('#groupFeaturesBySymbolizerLiteral()', function() { + + it('groups equal symbolizers', function() { + var layer = new ol.layer.Vector({ + source: new ol.source.Vector({ + projection: ol.Projection.getFromCode('EPSG:4326') + }), + style: new ol.style.Style({ + rules: [ + new ol.style.Rule({ + symbolizers: [ + new ol.style.Line({ + strokeWidth: 2, + strokeStyle: new ol.Expression('colorProperty'), + opacity: 1 + }) + ] + }) + ] + }) + }); + var features = [ + new ol.Feature({ + g: new ol.geom.LineString([[-10, -10], [10, 10]]), + colorProperty: '#BADA55' + }), + new ol.Feature({ + g: new ol.geom.LineString([[-10, 10], [10, -10]]), + colorProperty: '#013' + }), + new ol.Feature({ + g: new ol.geom.LineString([[10, -10], [-10, -10]]), + colorProperty: '#013' + }) + ]; + + var groups = layer.groupFeaturesBySymbolizerLiteral(features); + expect(groups.length).toBe(2); + expect(groups[0][0].length).toBe(1); + expect(groups[0][1].strokeStyle).toBe('#BADA55'); + expect(groups[1][0].length).toBe(2); + expect(groups[1][1].strokeStyle).toBe('#013'); + + layer.dispose(); + }); + + }); + +}); diff --git a/test/spec/ol/style/line.test.js b/test/spec/ol/style/line.test.js index aac6cebe4a..e9957e036e 100644 --- a/test/spec/ol/style/line.test.js +++ b/test/spec/ol/style/line.test.js @@ -1,5 +1,30 @@ goog.provide('ol.test.style.Line'); +describe('ol.style.LineLiteral', function() { + + describe('#equals()', function() { + + it('identifies equal literals', function() { + var literal = new ol.style.LineLiteral({ + strokeWidth: 3, + strokeStyle: '#BADA55' + }); + var equalLiteral = new ol.style.LineLiteral({ + strokeStyle: '#BADA55', + strokeWidth: 3 + }); + var differentLiteral = new ol.style.LineLiteral({ + strokeStyle: '#013', + strokeWidth: 3 + }); + expect(literal.equals(equalLiteral)).toBe(true); + expect(literal.equals(differentLiteral)).toBe(false); + }); + + }); + +}); + describe('ol.style.Line', function() { describe('constructor', function() { diff --git a/test/spec/ol/style/polygon.test.js b/test/spec/ol/style/polygon.test.js index b4c4722a9f..3e7693bc85 100644 --- a/test/spec/ol/style/polygon.test.js +++ b/test/spec/ol/style/polygon.test.js @@ -1,5 +1,30 @@ goog.provide('ol.test.style.Polygon'); +describe('ol.style.PolygonLiteral', function() { + + describe('#equals()', function() { + + it('identifies equal literals', function() { + var literal = new ol.style.PolygonLiteral({ + strokeWidth: 3, + fillStyle: '#BADA55' + }); + var equalLiteral = new ol.style.PolygonLiteral({ + fillStyle: '#BADA55', + strokeWidth: 3 + }); + var differentLiteral = new ol.style.PolygonLiteral({ + fillStyle: '#013', + strokeWidth: 3 + }); + expect(literal.equals(equalLiteral)).toBe(true); + expect(literal.equals(differentLiteral)).toBe(false); + }); + + }); + +}); + describe('ol.style.Polygon', function() { describe('constructor', function() { diff --git a/test/spec/ol/style/rule.test.js b/test/spec/ol/style/rule.test.js new file mode 100644 index 0000000000..fe5fcbcca7 --- /dev/null +++ b/test/spec/ol/style/rule.test.js @@ -0,0 +1,33 @@ +goog.provide('ol.test.style.Rule'); + +describe('ol.style.Rule', function() { + + describe('#applies()', function() { + var feature = new ol.Feature(), + rule; + + it('returns true for a rule without filter', function() { + rule = new ol.style.Rule({}); + expect(rule.applies(feature)).toBe(true); + }); + + it('returns false when the rule does not apply', function() { + rule = new ol.style.Rule({ + filter: new ol.filter.Filter(function() { return false; }) + }); + expect(rule.applies(feature)).toBe(false); + }); + + it('returns true when the rule applies', function() { + rule = new ol.style.Rule({ + filter: new ol.filter.Filter(function() { return true; }) + }); + expect(rule.applies(feature)).toBe(true); + }); + }); + +}); + +goog.require('ol.Feature'); +goog.require('ol.filter.Filter'); +goog.require('ol.style.Rule'); diff --git a/test/spec/ol/style/shape.test.js b/test/spec/ol/style/shape.test.js index 26405624e6..fec06b87d0 100644 --- a/test/spec/ol/style/shape.test.js +++ b/test/spec/ol/style/shape.test.js @@ -1,5 +1,30 @@ goog.provide('ol.test.style.Shape'); +describe('ol.style.ShapeLiteral', function() { + + describe('#equals()', function() { + + it('identifies equal literals', function() { + var literal = new ol.style.ShapeLiteral({ + size: 4, + fillStyle: '#BADA55' + }); + var equalLiteral = new ol.style.ShapeLiteral({ + fillStyle: '#BADA55', + size: 4 + }); + var differentLiteral = new ol.style.ShapeLiteral({ + fillStyle: '#013', + size: 4 + }); + expect(literal.equals(equalLiteral)).toBe(true); + expect(literal.equals(differentLiteral)).toBe(false); + }); + + }); + +}); + describe('ol.style.Shape', function() { describe('constructor', function() { diff --git a/test/spec/ol/style/style.test.js b/test/spec/ol/style/style.test.js new file mode 100644 index 0000000000..3e93398b64 --- /dev/null +++ b/test/spec/ol/style/style.test.js @@ -0,0 +1,66 @@ +goog.provide('ol.test.style.Style'); + +describe('ol.style.Style', function() { + + describe('#apply()', function() { + + it('applies a style to a feature', function() { + var style = new ol.style.Style({ + rules: [ + new ol.style.Rule({ + filter: new ol.filter.Filter(function(feature) { + return feature.get('foo') == 'bar'; + }), + symbolizers: [ + new ol.style.Shape({ + size: 4, + fillStyle: '#BADA55' + }) + ] + }) + ] + }); + var feature = new ol.Feature(); + feature.set('foo', 'bar'); + expect(style.apply(feature).length).toBe(1); + expect(style.apply(feature)[0].fillStyle).toBe('#BADA55'); + feature.set('foo', 'baz'); + expect(style.apply(feature).length).toBe(0); + }); + + }); + + describe('ol.style.Style.applyDefaultStyle()', function() { + var feature = new ol.Feature(); + + it('returns an empty array for features without geometry', function() { + expect(ol.style.Style.applyDefaultStyle(feature).length).toBe(0); + }); + + it('returns an array with the Shape default for points', function() { + feature.setGeometry(new ol.geom.Point([0, 0])); + expect(ol.style.Style.applyDefaultStyle(feature)[0] + .equals(ol.style.ShapeDefaults)).toBe(true); + }); + + it('returns an array with the Line default for lines', function() { + feature.setGeometry(new ol.geom.LineString([[0, 0], [1, 1]])); + expect(ol.style.Style.applyDefaultStyle(feature)[0] + .equals(ol.style.LineDefaults)).toBe(true); + }); + + it('returns an array with the Polygon default for polygons', function() { + feature.setGeometry(new ol.geom.Polygon([[[0, 0], [1, 1], [0, 0]]])); + expect(ol.style.Style.applyDefaultStyle(feature)[0] + .equals(ol.style.PolygonDefaults)).toBe(true); + }); + + }); + +}); + +goog.require('ol.geom.LineString'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.filter.Filter'); +goog.require('ol.style.Style'); From 4af32560cec009d23247fdd9afdf53c2b4db5be3 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 3 Mar 2013 15:49:53 +0100 Subject: [PATCH 096/180] Removing abstract methods for now Later we'll discuss having more specific FeatureParser and readFeatures type methods. --- src/ol/parser/parser.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/ol/parser/parser.js b/src/ol/parser/parser.js index 1c96acc41a..8d6d214147 100644 --- a/src/ol/parser/parser.js +++ b/src/ol/parser/parser.js @@ -6,16 +6,3 @@ goog.provide('ol.parser.Parser'); * @constructor */ ol.parser.Parser = function() {}; - -/** - * @param {*} data Data to deserialize. - * @return {*} Parsed data. - */ -ol.parser.Parser.prototype.read = goog.abstractMethod; - - -/** - * @param {*} obj Object to serialize. - * @return {*} Serialized object. - */ -ol.parser.Parser.prototype.write = goog.abstractMethod; From b247031fe291f7f562fa8c08d45c041b0c7263b1 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 3 Mar 2013 15:51:24 +0100 Subject: [PATCH 097/180] Test reading with shared vertices --- src/ol/parser/geojson.js | 91 +++++++---- test/spec/ol/parser/geojson.test.js | 65 +++++++- test/spec/ol/parser/geojson/countries.json | 181 +++++++++++++++++++++ 3 files changed, 305 insertions(+), 32 deletions(-) create mode 100644 test/spec/ol/parser/geojson/countries.json diff --git a/src/ol/parser/geojson.js b/src/ol/parser/geojson.js index 9bbabaff23..cb5863e243 100644 --- a/src/ol/parser/geojson.js +++ b/src/ol/parser/geojson.js @@ -8,6 +8,7 @@ goog.require('ol.geom.MultiPoint'); goog.require('ol.geom.MultiPolygon'); goog.require('ol.geom.Point'); goog.require('ol.geom.Polygon'); +goog.require('ol.geom.SharedVertices'); goog.require('ol.parser.Parser'); @@ -23,62 +24,63 @@ goog.inherits(ol.parser.GeoJSON, ol.parser.Parser); /** * Parse a GeoJSON string. * @param {string} str GeoJSON string. + * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {ol.Feature|Array.| * ol.geom.Geometry|Array.} Parsed geometry or array * of geometries. */ -ol.parser.GeoJSON.prototype.read = function(str) { - // TODO: add options and accept projection +ol.parser.GeoJSON.prototype.read = function(str, opt_options) { var json = /** @type {GeoJSONObject} */ (JSON.parse(str)); - return ol.parser.GeoJSON.prototype.parse_(json); + return this.parse_(json, opt_options); }; /** * @param {GeoJSONObject} json GeoJSON object. + * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {ol.Feature|Array.| * ol.geom.Geometry|Array.} Parsed geometry or array * of geometries. * @private */ -ol.parser.GeoJSON.prototype.parse_ = function(json) { +ol.parser.GeoJSON.prototype.parse_ = function(json, opt_options) { var result; switch (json.type) { case 'FeatureCollection': result = this.parseFeatureCollection_( - /** @type {GeoJSONFeatureCollection} */ (json)); + /** @type {GeoJSONFeatureCollection} */ (json), opt_options); break; case 'Feature': result = this.parseFeature_( - /** @type {GeoJSONFeature} */ (json)); + /** @type {GeoJSONFeature} */ (json), opt_options); break; case 'GeometryCollection': result = this.parseGeometryCollection_( - /** @type {GeoJSONGeometryCollection} */ (json)); + /** @type {GeoJSONGeometryCollection} */ (json), opt_options); break; case 'Point': result = this.parsePoint_( - /** @type {GeoJSONGeometry} */ (json)); + /** @type {GeoJSONGeometry} */ (json), opt_options); break; case 'LineString': result = this.parseLineString_( - /** @type {GeoJSONGeometry} */ (json)); + /** @type {GeoJSONGeometry} */ (json), opt_options); break; case 'Polygon': result = this.parsePolygon_( - /** @type {GeoJSONGeometry} */ (json)); + /** @type {GeoJSONGeometry} */ (json), opt_options); break; case 'MultiPoint': result = this.parseMultiPoint_( - /** @type {GeoJSONGeometry} */ (json)); + /** @type {GeoJSONGeometry} */ (json), opt_options); break; case 'MultiLineString': result = this.parseMultiLineString_( - /** @type {GeoJSONGeometry} */ (json)); + /** @type {GeoJSONGeometry} */ (json), opt_options); break; case 'MultiPolygon': result = this.parseMultiPolygon_( - /** @type {GeoJSONGeometry} */ (json)); + /** @type {GeoJSONGeometry} */ (json), opt_options); break; default: throw new Error('GeoJSON parsing not implemented for type: ' + json.type); @@ -89,15 +91,16 @@ ol.parser.GeoJSON.prototype.parse_ = function(json) { /** * @param {GeoJSONFeature} json GeoJSON feature. + * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {ol.Feature} Parsed feature. * @private */ -ol.parser.GeoJSON.prototype.parseFeature_ = function(json) { +ol.parser.GeoJSON.prototype.parseFeature_ = function(json, opt_options) { var geomJson = json.geometry, geometry = null; if (geomJson) { geometry = /** @type {ol.geom.Geometry} */ (this.parse_( - /** @type {GeoJSONGeometry} */ (geomJson))); + /** @type {GeoJSONGeometry} */ (geomJson), opt_options)); } var feature = new ol.Feature(); feature.setGeometry(geometry); @@ -108,10 +111,12 @@ ol.parser.GeoJSON.prototype.parseFeature_ = function(json) { /** * @param {GeoJSONFeatureCollection} json GeoJSON feature collection. + * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {Array.} Parsed array of features. * @private */ -ol.parser.GeoJSON.prototype.parseFeatureCollection_ = function(json) { +ol.parser.GeoJSON.prototype.parseFeatureCollection_ = function( + json, opt_options) { var features = json.features, len = features.length, result = new Array(len), @@ -119,7 +124,7 @@ ol.parser.GeoJSON.prototype.parseFeatureCollection_ = function(json) { for (i = 0; i < len; ++i) { result[i] = this.parse_( - /** @type {GeoJSONFeature} */ (features[i])); + /** @type {GeoJSONFeature} */ (features[i]), opt_options); } return result; }; @@ -127,10 +132,12 @@ ol.parser.GeoJSON.prototype.parseFeatureCollection_ = function(json) { /** * @param {GeoJSONGeometryCollection} json GeoJSON geometry collection. + * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {Array.} Parsed array of geometries. * @private */ -ol.parser.GeoJSON.prototype.parseGeometryCollection_ = function(json) { +ol.parser.GeoJSON.prototype.parseGeometryCollection_ = function( + json, opt_options) { var geometries = json.geometries, len = geometries.length, result = new Array(len), @@ -138,7 +145,7 @@ ol.parser.GeoJSON.prototype.parseGeometryCollection_ = function(json) { for (i = 0; i < len; ++i) { result[i] = this.parse_( - /** @type {GeoJSONGeometry} */ (geometries[i])); + /** @type {GeoJSONGeometry} */ (geometries[i]), opt_options); } return result; }; @@ -146,59 +153,81 @@ ol.parser.GeoJSON.prototype.parseGeometryCollection_ = function(json) { /** * @param {GeoJSONGeometry} json GeoJSON linestring. + * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {ol.geom.LineString} Parsed linestring. * @private */ -ol.parser.GeoJSON.prototype.parseLineString_ = function(json) { - return new ol.geom.LineString(json.coordinates); +ol.parser.GeoJSON.prototype.parseLineString_ = function(json, opt_options) { + var vertices = opt_options && opt_options.lineVertices; + return new ol.geom.LineString(json.coordinates, vertices); }; /** * @param {GeoJSONGeometry} json GeoJSON multi-linestring. + * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {ol.geom.MultiLineString} Parsed multi-linestring. * @private */ -ol.parser.GeoJSON.prototype.parseMultiLineString_ = function(json) { - return new ol.geom.MultiLineString(json.coordinates); +ol.parser.GeoJSON.prototype.parseMultiLineString_ = function( + json, opt_options) { + var vertices = opt_options && opt_options.lineVertices; + return new ol.geom.MultiLineString(json.coordinates, vertices); }; /** * @param {GeoJSONGeometry} json GeoJSON multi-point. + * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {ol.geom.MultiPoint} Parsed multi-point. * @private */ -ol.parser.GeoJSON.prototype.parseMultiPoint_ = function(json) { - return new ol.geom.MultiPoint(json.coordinates); +ol.parser.GeoJSON.prototype.parseMultiPoint_ = function(json, opt_options) { + var vertices = opt_options && opt_options.pointVertices; + return new ol.geom.MultiPoint(json.coordinates, vertices); }; /** * @param {GeoJSONGeometry} json GeoJSON multi-polygon. + * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {ol.geom.MultiPolygon} Parsed multi-polygon. * @private */ -ol.parser.GeoJSON.prototype.parseMultiPolygon_ = function(json) { - return new ol.geom.MultiPolygon(json.coordinates); +ol.parser.GeoJSON.prototype.parseMultiPolygon_ = function(json, opt_options) { + var vertices = opt_options && opt_options.polygonVertices; + return new ol.geom.MultiPolygon(json.coordinates, vertices); }; /** * @param {GeoJSONGeometry} json GeoJSON point. + * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {ol.geom.Point} Parsed multi-point. * @private */ -ol.parser.GeoJSON.prototype.parsePoint_ = function(json) { - return new ol.geom.Point(json.coordinates); +ol.parser.GeoJSON.prototype.parsePoint_ = function(json, opt_options) { + var vertices = opt_options && opt_options.pointVertices; + return new ol.geom.Point(json.coordinates, vertices); }; /** * @param {GeoJSONGeometry} json GeoJSON polygon. + * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {ol.geom.Polygon} Parsed polygon. * @private */ -ol.parser.GeoJSON.prototype.parsePolygon_ = function(json) { - return new ol.geom.Polygon(json.coordinates); +ol.parser.GeoJSON.prototype.parsePolygon_ = function(json, opt_options) { + var vertices = opt_options && opt_options.polygonVertices; + return new ol.geom.Polygon(json.coordinates, vertices); }; + + +/** + * @typedef {{pointVertices: (ol.geom.SharedVertices|undefined), + * lineVertices: (ol.geom.SharedVertices|undefined), + * polygonVertices: (ol.geom.SharedVertices|undefined)}} + * TODO: add support for toProjection + */ +ol.parser.GeoJSON.ReadOptions; diff --git a/test/spec/ol/parser/geojson.test.js b/test/spec/ol/parser/geojson.test.js index 9914a06b44..16a807125e 100644 --- a/test/spec/ol/parser/geojson.test.js +++ b/test/spec/ol/parser/geojson.test.js @@ -68,7 +68,7 @@ describe('ol.parser.GeoJSON', function() { ] }; - describe('read()', function() { + describe('#read()', function() { it('parses point', function() { var str = JSON.stringify({ @@ -143,6 +143,68 @@ describe('ol.parser.GeoJSON', function() { expect(secondGeom).toBeA(ol.geom.LineString); }); + it('parses countries.json', function() { + afterLoadText('spec/ol/parser/geojson/countries.json', function(text) { + var result = parser.read(text); + expect(result.length).toBe(179); + + var first = result[0]; + expect(first).toBeA(ol.Feature); + expect(first.get('name')).toBe('Afghanistan'); + var firstGeom = first.getGeometry(); + expect(firstGeom).toBeA(ol.geom.Polygon); + expect(firstGeom.getBounds().equals( + new ol.Extent(60.52843, 29.318572, 75.158028, 38.486282))) + .toBe(true); + + var last = result[178]; + expect(last).toBeA(ol.Feature); + expect(last.get('name')).toBe('Zimbabwe'); + var lastGeom = last.getGeometry(); + expect(lastGeom).toBeA(ol.geom.Polygon); + expect(lastGeom.getBounds().equals( + new ol.Extent(25.264226, -22.271612, 32.849861, -15.507787))) + .toBe(true); + }); + }); + + it('parses countries.json with shared vertices', function() { + afterLoadText('spec/ol/parser/geojson/countries.json', function(text) { + var pointVertices = new ol.geom.SharedVertices(); + var lineVertices = new ol.geom.SharedVertices(); + var polygonVertices = new ol.geom.SharedVertices(); + + var result = parser.read(text, { + pointVertices: pointVertices, + lineVertices: lineVertices, + polygonVertices: polygonVertices + }); + expect(result.length).toBe(179); + + expect(pointVertices.coordinates.length).toBe(0); + expect(lineVertices.coordinates.length).toBe(0); + expect(polygonVertices.coordinates.length).toBe(21344); + + var first = result[0]; + expect(first).toBeA(ol.Feature); + expect(first.get('name')).toBe('Afghanistan'); + var firstGeom = first.getGeometry(); + expect(firstGeom).toBeA(ol.geom.Polygon); + expect(firstGeom.getBounds().equals( + new ol.Extent(60.52843, 29.318572, 75.158028, 38.486282))) + .toBe(true); + + var last = result[178]; + expect(last).toBeA(ol.Feature); + expect(last.get('name')).toBe('Zimbabwe'); + var lastGeom = last.getGeometry(); + expect(lastGeom).toBeA(ol.geom.Polygon); + expect(lastGeom.getBounds().equals( + new ol.Extent(25.264226, -22.271612, 32.849861, -15.507787))) + .toBe(true); + }); + }); + }); }); @@ -152,4 +214,5 @@ goog.require('ol.geom.LinearRing'); goog.require('ol.geom.LineString'); goog.require('ol.geom.Point'); goog.require('ol.geom.Polygon'); +goog.require('ol.geom.SharedVertices'); goog.require('ol.parser.GeoJSON'); diff --git a/test/spec/ol/parser/geojson/countries.json b/test/spec/ol/parser/geojson/countries.json new file mode 100644 index 0000000000..b27f2ecacb --- /dev/null +++ b/test/spec/ol/parser/geojson/countries.json @@ -0,0 +1,181 @@ +{"type":"FeatureCollection","features":[ +{"type":"Feature","id":"AFG","properties":{"name":"Afghanistan"},"geometry":{"type":"Polygon","coordinates":[[[61.210817,35.650072],[62.230651,35.270664],[62.984662,35.404041],[63.193538,35.857166],[63.982896,36.007957],[64.546479,36.312073],[64.746105,37.111818],[65.588948,37.305217],[65.745631,37.661164],[66.217385,37.39379],[66.518607,37.362784],[67.075782,37.356144],[67.83,37.144994],[68.135562,37.023115],[68.859446,37.344336],[69.196273,37.151144],[69.518785,37.608997],[70.116578,37.588223],[70.270574,37.735165],[70.376304,38.138396],[70.806821,38.486282],[71.348131,38.258905],[71.239404,37.953265],[71.541918,37.905774],[71.448693,37.065645],[71.844638,36.738171],[72.193041,36.948288],[72.63689,37.047558],[73.260056,37.495257],[73.948696,37.421566],[74.980002,37.41999],[75.158028,37.133031],[74.575893,37.020841],[74.067552,36.836176],[72.920025,36.720007],[71.846292,36.509942],[71.262348,36.074388],[71.498768,35.650563],[71.613076,35.153203],[71.115019,34.733126],[71.156773,34.348911],[70.881803,33.988856],[69.930543,34.02012],[70.323594,33.358533],[69.687147,33.105499],[69.262522,32.501944],[69.317764,31.901412],[68.926677,31.620189],[68.556932,31.71331],[67.792689,31.58293],[67.683394,31.303154],[66.938891,31.304911],[66.381458,30.738899],[66.346473,29.887943],[65.046862,29.472181],[64.350419,29.560031],[64.148002,29.340819],[63.550261,29.468331],[62.549857,29.318572],[60.874248,29.829239],[61.781222,30.73585],[61.699314,31.379506],[60.941945,31.548075],[60.863655,32.18292],[60.536078,32.981269],[60.9637,33.528832],[60.52843,33.676446],[60.803193,34.404102],[61.210817,35.650072]]]}}, +{"type":"Feature","id":"AGO","properties":{"name":"Angola"},"geometry":{"type":"MultiPolygon","coordinates":[[[[16.326528,-5.87747],[16.57318,-6.622645],[16.860191,-7.222298],[17.089996,-7.545689],[17.47297,-8.068551],[18.134222,-7.987678],[18.464176,-7.847014],[19.016752,-7.988246],[19.166613,-7.738184],[19.417502,-7.155429],[20.037723,-7.116361],[20.091622,-6.94309],[20.601823,-6.939318],[20.514748,-7.299606],[21.728111,-7.290872],[21.746456,-7.920085],[21.949131,-8.305901],[21.801801,-8.908707],[21.875182,-9.523708],[22.208753,-9.894796],[22.155268,-11.084801],[22.402798,-10.993075],[22.837345,-11.017622],[23.456791,-10.867863],[23.912215,-10.926826],[24.017894,-11.237298],[23.904154,-11.722282],[24.079905,-12.191297],[23.930922,-12.565848],[24.016137,-12.911046],[21.933886,-12.898437],[21.887843,-16.08031],[22.562478,-16.898451],[23.215048,-17.523116],[21.377176,-17.930636],[18.956187,-17.789095],[18.263309,-17.309951],[14.209707,-17.353101],[14.058501,-17.423381],[13.462362,-16.971212],[12.814081,-16.941343],[12.215461,-17.111668],[11.734199,-17.301889],[11.640096,-16.673142],[11.778537,-15.793816],[12.123581,-14.878316],[12.175619,-14.449144],[12.500095,-13.5477],[12.738479,-13.137906],[13.312914,-12.48363],[13.633721,-12.038645],[13.738728,-11.297863],[13.686379,-10.731076],[13.387328,-10.373578],[13.120988,-9.766897],[12.87537,-9.166934],[12.929061,-8.959091],[13.236433,-8.562629],[12.93304,-7.596539],[12.728298,-6.927122],[12.227347,-6.294448],[12.322432,-6.100092],[12.735171,-5.965682],[13.024869,-5.984389],[13.375597,-5.864241],[16.326528,-5.87747]]],[[[12.436688,-5.684304],[12.182337,-5.789931],[11.914963,-5.037987],[12.318608,-4.60623],[12.62076,-4.438023],[12.995517,-4.781103],[12.631612,-4.991271],[12.468004,-5.248362],[12.436688,-5.684304]]]]}}, +{"type":"Feature","id":"ALB","properties":{"name":"Albania"},"geometry":{"type":"Polygon","coordinates":[[[20.590247,41.855404],[20.463175,41.515089],[20.605182,41.086226],[21.02004,40.842727],[20.99999,40.580004],[20.674997,40.435],[20.615,40.110007],[20.150016,39.624998],[19.98,39.694993],[19.960002,39.915006],[19.406082,40.250773],[19.319059,40.72723],[19.40355,41.409566],[19.540027,41.719986],[19.371769,41.877548],[19.304486,42.195745],[19.738051,42.688247],[19.801613,42.500093],[20.0707,42.58863],[20.283755,42.32026],[20.52295,42.21787],[20.590247,41.855404]]]}}, +{"type":"Feature","id":"ARE","properties":{"name":"United Arab Emirates"},"geometry":{"type":"Polygon","coordinates":[[[51.579519,24.245497],[51.757441,24.294073],[51.794389,24.019826],[52.577081,24.177439],[53.404007,24.151317],[54.008001,24.121758],[54.693024,24.797892],[55.439025,25.439145],[56.070821,26.055464],[56.261042,25.714606],[56.396847,24.924732],[55.886233,24.920831],[55.804119,24.269604],[55.981214,24.130543],[55.528632,23.933604],[55.525841,23.524869],[55.234489,23.110993],[55.208341,22.70833],[55.006803,22.496948],[52.000733,23.001154],[51.617708,24.014219],[51.579519,24.245497]]]}}, +{"type":"Feature","id":"ARG","properties":{"name":"Argentina"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-65.5,-55.2],[-66.45,-55.25],[-66.95992,-54.89681],[-67.56244,-54.87001],[-68.63335,-54.8695],[-68.63401,-52.63637],[-68.25,-53.1],[-67.75,-53.85],[-66.45,-54.45],[-65.05,-54.7],[-65.5,-55.2]]],[[[-64.964892,-22.075862],[-64.377021,-22.798091],[-63.986838,-21.993644],[-62.846468,-22.034985],[-62.685057,-22.249029],[-60.846565,-23.880713],[-60.028966,-24.032796],[-58.807128,-24.771459],[-57.777217,-25.16234],[-57.63366,-25.603657],[-58.618174,-27.123719],[-57.60976,-27.395899],[-56.486702,-27.548499],[-55.695846,-27.387837],[-54.788795,-26.621786],[-54.625291,-25.739255],[-54.13005,-25.547639],[-53.628349,-26.124865],[-53.648735,-26.923473],[-54.490725,-27.474757],[-55.162286,-27.881915],[-56.2909,-28.852761],[-57.625133,-30.216295],[-57.874937,-31.016556],[-58.14244,-32.044504],[-58.132648,-33.040567],[-58.349611,-33.263189],[-58.427074,-33.909454],[-58.495442,-34.43149],[-57.22583,-35.288027],[-57.362359,-35.97739],[-56.737487,-36.413126],[-56.788285,-36.901572],[-57.749157,-38.183871],[-59.231857,-38.72022],[-61.237445,-38.928425],[-62.335957,-38.827707],[-62.125763,-39.424105],[-62.330531,-40.172586],[-62.145994,-40.676897],[-62.745803,-41.028761],[-63.770495,-41.166789],[-64.73209,-40.802677],[-65.118035,-41.064315],[-64.978561,-42.058001],[-64.303408,-42.359016],[-63.755948,-42.043687],[-63.458059,-42.563138],[-64.378804,-42.873558],[-65.181804,-43.495381],[-65.328823,-44.501366],[-65.565269,-45.036786],[-66.509966,-45.039628],[-67.293794,-45.551896],[-67.580546,-46.301773],[-66.597066,-47.033925],[-65.641027,-47.236135],[-65.985088,-48.133289],[-67.166179,-48.697337],[-67.816088,-49.869669],[-68.728745,-50.264218],[-69.138539,-50.73251],[-68.815561,-51.771104],[-68.149995,-52.349983],[-68.571545,-52.299444],[-69.498362,-52.142761],[-71.914804,-52.009022],[-72.329404,-51.425956],[-72.309974,-50.67701],[-72.975747,-50.74145],[-73.328051,-50.378785],[-73.415436,-49.318436],[-72.648247,-48.878618],[-72.331161,-48.244238],[-72.447355,-47.738533],[-71.917258,-46.884838],[-71.552009,-45.560733],[-71.659316,-44.973689],[-71.222779,-44.784243],[-71.329801,-44.407522],[-71.793623,-44.207172],[-71.464056,-43.787611],[-71.915424,-43.408565],[-72.148898,-42.254888],[-71.746804,-42.051386],[-71.915734,-40.832339],[-71.680761,-39.808164],[-71.413517,-38.916022],[-70.814664,-38.552995],[-71.118625,-37.576827],[-71.121881,-36.658124],[-70.364769,-36.005089],[-70.388049,-35.169688],[-69.817309,-34.193571],[-69.814777,-33.273886],[-70.074399,-33.09121],[-70.535069,-31.36501],[-69.919008,-30.336339],[-70.01355,-29.367923],[-69.65613,-28.459141],[-69.001235,-27.521214],[-68.295542,-26.89934],[-68.5948,-26.506909],[-68.386001,-26.185016],[-68.417653,-24.518555],[-67.328443,-24.025303],[-66.985234,-22.986349],[-67.106674,-22.735925],[-66.273339,-21.83231],[-64.964892,-22.075862]]]]}}, +{"type":"Feature","id":"ARM","properties":{"name":"Armenia"},"geometry":{"type":"Polygon","coordinates":[[[43.582746,41.092143],[44.97248,41.248129],[45.179496,40.985354],[45.560351,40.81229],[45.359175,40.561504],[45.891907,40.218476],[45.610012,39.899994],[46.034534,39.628021],[46.483499,39.464155],[46.50572,38.770605],[46.143623,38.741201],[45.735379,39.319719],[45.739978,39.473999],[45.298145,39.471751],[45.001987,39.740004],[44.79399,39.713003],[44.400009,40.005],[43.656436,40.253564],[43.752658,40.740201],[43.582746,41.092143]]]}}, +{"type":"Feature","id":"ATA","properties":{"name":"Antarctica"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-59.572095,-80.040179],[-59.865849,-80.549657],[-60.159656,-81.000327],[-62.255393,-80.863178],[-64.488125,-80.921934],[-65.741666,-80.588827],[-65.741666,-80.549657],[-66.290031,-80.255773],[-64.037688,-80.294944],[-61.883246,-80.39287],[-61.138976,-79.981371],[-60.610119,-79.628679],[-59.572095,-80.040179]]],[[[-159.208184,-79.497059],[-161.127601,-79.634209],[-162.439847,-79.281465],[-163.027408,-78.928774],[-163.066604,-78.869966],[-163.712896,-78.595667],[-163.712896,-78.595667],[-163.105801,-78.223338],[-161.245113,-78.380176],[-160.246208,-78.693645],[-159.482405,-79.046338],[-159.208184,-79.497059]]],[[[-45.154758,-78.04707],[-43.920828,-78.478103],[-43.48995,-79.08556],[-43.372438,-79.516645],[-43.333267,-80.026123],[-44.880537,-80.339644],[-46.506174,-80.594357],[-48.386421,-80.829485],[-50.482107,-81.025442],[-52.851988,-80.966685],[-54.164259,-80.633528],[-53.987991,-80.222028],[-51.853134,-79.94773],[-50.991326,-79.614623],[-50.364595,-79.183487],[-49.914131,-78.811209],[-49.306959,-78.458569],[-48.660616,-78.047018],[-48.660616,-78.047019],[-48.151396,-78.04707],[-46.662857,-77.831476],[-45.154758,-78.04707]]],[[[-121.211511,-73.50099],[-119.918851,-73.657725],[-118.724143,-73.481353],[-119.292119,-73.834097],[-120.232217,-74.08881],[-121.62283,-74.010468],[-122.621735,-73.657778],[-122.621735,-73.657777],[-122.406245,-73.324619],[-121.211511,-73.50099]]],[[[-125.559566,-73.481353],[-124.031882,-73.873268],[-124.619469,-73.834097],[-125.912181,-73.736118],[-127.28313,-73.461769],[-127.28313,-73.461768],[-126.558472,-73.246226],[-125.559566,-73.481353]]],[[[-98.98155,-71.933334],[-97.884743,-72.070535],[-96.787937,-71.952971],[-96.20035,-72.521205],[-96.983765,-72.442864],[-98.198083,-72.482035],[-99.432013,-72.442864],[-100.783455,-72.50162],[-101.801868,-72.305663],[-102.330725,-71.894164],[-102.330725,-71.894164],[-101.703967,-71.717792],[-100.430919,-71.854993],[-98.98155,-71.933334]]],[[[-68.451346,-70.955823],[-68.333834,-71.406493],[-68.510128,-71.798407],[-68.784297,-72.170736],[-69.959471,-72.307885],[-71.075889,-72.503842],[-72.388134,-72.484257],[-71.8985,-72.092343],[-73.073622,-72.229492],[-74.19004,-72.366693],[-74.953895,-72.072757],[-75.012625,-71.661258],[-73.915819,-71.269345],[-73.915819,-71.269344],[-73.230331,-71.15178],[-72.074717,-71.190951],[-71.780962,-70.681473],[-71.72218,-70.309196],[-71.741791,-69.505782],[-71.173815,-69.035475],[-70.253252,-68.87874],[-69.724447,-69.251017],[-69.489422,-69.623346],[-69.058518,-70.074016],[-68.725541,-70.505153],[-68.451346,-70.955823]]],[[[-58.614143,-64.152467],[-59.045073,-64.36801],[-59.789342,-64.211223],[-60.611928,-64.309202],[-61.297416,-64.54433],[-62.0221,-64.799094],[-62.51176,-65.09303],[-62.648858,-65.484942],[-62.590128,-65.857219],[-62.120079,-66.190326],[-62.805567,-66.425505],[-63.74569,-66.503847],[-64.294106,-66.837004],[-64.881693,-67.150474],[-65.508425,-67.58161],[-65.665082,-67.953887],[-65.312545,-68.365335],[-64.783715,-68.678908],[-63.961103,-68.913984],[-63.1973,-69.227556],[-62.785955,-69.619419],[-62.570516,-69.991747],[-62.276736,-70.383661],[-61.806661,-70.716768],[-61.512906,-71.089045],[-61.375809,-72.010074],[-61.081977,-72.382351],[-61.003661,-72.774265],[-60.690269,-73.166179],[-60.827367,-73.695242],[-61.375809,-74.106742],[-61.96337,-74.439848],[-63.295201,-74.576997],[-63.74569,-74.92974],[-64.352836,-75.262847],[-65.860987,-75.635124],[-67.192818,-75.79191],[-68.446282,-76.007452],[-69.797724,-76.222995],[-70.600724,-76.634494],[-72.206776,-76.673665],[-73.969536,-76.634494],[-75.555977,-76.712887],[-77.24037,-76.712887],[-76.926979,-77.104802],[-75.399294,-77.28107],[-74.282876,-77.55542],[-73.656119,-77.908112],[-74.772536,-78.221633],[-76.4961,-78.123654],[-77.925858,-78.378419],[-77.984666,-78.789918],[-78.023785,-79.181833],[-76.848637,-79.514939],[-76.633224,-79.887216],[-75.360097,-80.259545],[-73.244852,-80.416331],[-71.442946,-80.69063],[-70.013163,-81.004151],[-68.191646,-81.317672],[-65.704279,-81.474458],[-63.25603,-81.748757],[-61.552026,-82.042692],[-59.691416,-82.37585],[-58.712121,-82.846106],[-58.222487,-83.218434],[-57.008117,-82.865691],[-55.362894,-82.571755],[-53.619771,-82.258235],[-51.543644,-82.003521],[-49.76135,-81.729171],[-47.273931,-81.709586],[-44.825708,-81.846735],[-42.808363,-82.081915],[-42.16202,-81.65083],[-40.771433,-81.356894],[-38.244818,-81.337309],[-36.26667,-81.121715],[-34.386397,-80.906172],[-32.310296,-80.769023],[-30.097098,-80.592651],[-28.549802,-80.337938],[-29.254901,-79.985195],[-29.685805,-79.632503],[-29.685805,-79.260226],[-31.624808,-79.299397],[-33.681324,-79.456132],[-35.639912,-79.456132],[-35.914107,-79.083855],[-35.77701,-78.339248],[-35.326546,-78.123654],[-33.896763,-77.888526],[-32.212369,-77.65345],[-30.998051,-77.359515],[-29.783732,-77.065579],[-28.882779,-76.673665],[-27.511752,-76.497345],[-26.160336,-76.360144],[-25.474822,-76.281803],[-23.927552,-76.24258],[-22.458598,-76.105431],[-21.224694,-75.909474],[-20.010375,-75.674346],[-18.913543,-75.439218],[-17.522982,-75.125698],[-16.641589,-74.79254],[-15.701491,-74.498604],[-15.40771,-74.106742],[-16.46532,-73.871614],[-16.112784,-73.460114],[-15.446855,-73.146542],[-14.408805,-72.950585],[-13.311973,-72.715457],[-12.293508,-72.401936],[-11.510067,-72.010074],[-11.020433,-71.539767],[-10.295774,-71.265416],[-9.101015,-71.324224],[-8.611381,-71.65733],[-7.416622,-71.696501],[-7.377451,-71.324224],[-6.868232,-70.93231],[-5.790985,-71.030289],[-5.536375,-71.402617],[-4.341667,-71.461373],[-3.048981,-71.285053],[-1.795492,-71.167438],[-0.659489,-71.226246],[-0.228637,-71.637745],[0.868195,-71.304639],[1.886686,-71.128267],[3.022638,-70.991118],[4.139055,-70.853917],[5.157546,-70.618789],[6.273912,-70.462055],[7.13572,-70.246512],[7.742866,-69.893769],[8.48711,-70.148534],[9.525135,-70.011333],[10.249845,-70.48164],[10.817821,-70.834332],[11.953824,-70.638375],[12.404287,-70.246512],[13.422778,-69.972162],[14.734998,-70.030918],[15.126757,-70.403247],[15.949342,-70.030918],[17.026589,-69.913354],[18.201711,-69.874183],[19.259373,-69.893769],[20.375739,-70.011333],[21.452985,-70.07014],[21.923034,-70.403247],[22.569403,-70.697182],[23.666184,-70.520811],[24.841357,-70.48164],[25.977309,-70.48164],[27.093726,-70.462055],[28.09258,-70.324854],[29.150242,-70.20729],[30.031583,-69.93294],[30.971733,-69.75662],[31.990172,-69.658641],[32.754053,-69.384291],[33.302443,-68.835642],[33.870419,-68.502588],[34.908495,-68.659271],[35.300202,-69.012014],[36.16201,-69.247142],[37.200035,-69.168748],[37.905108,-69.52144],[38.649404,-69.776205],[39.667894,-69.541077],[40.020431,-69.109941],[40.921358,-68.933621],[41.959434,-68.600514],[42.938702,-68.463313],[44.113876,-68.267408],[44.897291,-68.051866],[45.719928,-67.816738],[46.503343,-67.601196],[47.44344,-67.718759],[48.344419,-67.366068],[48.990736,-67.091718],[49.930885,-67.111303],[50.753471,-66.876175],[50.949325,-66.523484],[51.791547,-66.249133],[52.614133,-66.053176],[53.613038,-65.89639],[54.53355,-65.818049],[55.414943,-65.876805],[56.355041,-65.974783],[57.158093,-66.249133],[57.255968,-66.680218],[58.137361,-67.013324],[58.744508,-67.287675],[59.939318,-67.405239],[60.605221,-67.679589],[61.427806,-67.953887],[62.387489,-68.012695],[63.19049,-67.816738],[64.052349,-67.405239],[64.992447,-67.620729],[65.971715,-67.738345],[66.911864,-67.855909],[67.891133,-67.934302],[68.890038,-67.934302],[69.712624,-68.972791],[69.673453,-69.227556],[69.555941,-69.678226],[68.596258,-69.93294],[67.81274,-70.305268],[67.949889,-70.697182],[69.066307,-70.677545],[68.929157,-71.069459],[68.419989,-71.441788],[67.949889,-71.853287],[68.71377,-72.166808],[69.869307,-72.264787],[71.024895,-72.088415],[71.573285,-71.696501],[71.906288,-71.324224],[72.454627,-71.010703],[73.08141,-70.716768],[73.33602,-70.364024],[73.864877,-69.874183],[74.491557,-69.776205],[75.62756,-69.737034],[76.626465,-69.619419],[77.644904,-69.462684],[78.134539,-69.07077],[78.428371,-68.698441],[79.113859,-68.326216],[80.093127,-68.071503],[80.93535,-67.875546],[81.483792,-67.542388],[82.051767,-67.366068],[82.776426,-67.209282],[83.775331,-67.30726],[84.676206,-67.209282],[85.655527,-67.091718],[86.752359,-67.150474],[87.477017,-66.876175],[87.986289,-66.209911],[88.358411,-66.484261],[88.828408,-66.954568],[89.67063,-67.150474],[90.630365,-67.228867],[91.5901,-67.111303],[92.608539,-67.189696],[93.548637,-67.209282],[94.17542,-67.111303],[95.017591,-67.170111],[95.781472,-67.385653],[96.682399,-67.248504],[97.759646,-67.248504],[98.68021,-67.111303],[99.718182,-67.248504],[100.384188,-66.915346],[100.893356,-66.58224],[101.578896,-66.30789],[102.832411,-65.563284],[103.478676,-65.700485],[104.242557,-65.974783],[104.90846,-66.327527],[106.181561,-66.934931],[107.160881,-66.954568],[108.081393,-66.954568],[109.15864,-66.837004],[110.235835,-66.699804],[111.058472,-66.425505],[111.74396,-66.13157],[112.860378,-66.092347],[113.604673,-65.876805],[114.388088,-66.072762],[114.897308,-66.386283],[115.602381,-66.699804],[116.699161,-66.660633],[117.384701,-66.915346],[118.57946,-67.170111],[119.832924,-67.268089],[120.871,-67.189696],[121.654415,-66.876175],[122.320369,-66.562654],[123.221296,-66.484261],[124.122274,-66.621462],[125.160247,-66.719389],[126.100396,-66.562654],[127.001427,-66.562654],[127.882768,-66.660633],[128.80328,-66.758611],[129.704259,-66.58224],[130.781454,-66.425505],[131.799945,-66.386283],[132.935896,-66.386283],[133.85646,-66.288304],[134.757387,-66.209963],[135.031582,-65.72007],[135.070753,-65.308571],[135.697485,-65.582869],[135.873805,-66.033591],[136.206705,-66.44509],[136.618049,-66.778197],[137.460271,-66.954568],[138.596223,-66.895761],[139.908442,-66.876175],[140.809421,-66.817367],[142.121692,-66.817367],[143.061842,-66.797782],[144.374061,-66.837004],[145.490427,-66.915346],[146.195552,-67.228867],[145.999699,-67.601196],[146.646067,-67.895131],[147.723263,-68.130259],[148.839629,-68.385024],[150.132314,-68.561292],[151.483705,-68.71813],[152.502247,-68.874813],[153.638199,-68.894502],[154.284567,-68.561292],[155.165857,-68.835642],[155.92979,-69.149215],[156.811132,-69.384291],[158.025528,-69.482269],[159.181013,-69.599833],[159.670699,-69.991747],[160.80665,-70.226875],[161.570479,-70.579618],[162.686897,-70.736353],[163.842434,-70.716768],[164.919681,-70.775524],[166.11444,-70.755938],[167.309095,-70.834332],[168.425616,-70.971481],[169.463589,-71.20666],[170.501665,-71.402617],[171.20679,-71.696501],[171.089227,-72.088415],[170.560422,-72.441159],[170.109958,-72.891829],[169.75737,-73.24452],[169.287321,-73.65602],[167.975101,-73.812806],[167.387489,-74.165498],[166.094803,-74.38104],[165.644391,-74.772954],[164.958851,-75.145283],[164.234193,-75.458804],[163.822797,-75.870303],[163.568239,-76.24258],[163.47026,-76.693302],[163.489897,-77.065579],[164.057873,-77.457442],[164.273363,-77.82977],[164.743464,-78.182514],[166.604126,-78.319611],[166.995781,-78.750748],[165.193876,-78.907483],[163.666217,-79.123025],[161.766385,-79.162248],[160.924162,-79.730482],[160.747894,-80.200737],[160.316964,-80.573066],[159.788211,-80.945395],[161.120016,-81.278501],[161.629287,-81.690001],[162.490992,-82.062278],[163.705336,-82.395435],[165.095949,-82.708956],[166.604126,-83.022477],[168.895665,-83.335998],[169.404782,-83.825891],[172.283934,-84.041433],[172.477049,-84.117914],[173.224083,-84.41371],[175.985672,-84.158997],[178.277212,-84.472518],[180,-84.71338],[-179.942499,-84.721443],[-179.058677,-84.139412],[-177.256772,-84.452933],[-177.140807,-84.417941],[-176.084673,-84.099259],[-175.947235,-84.110449],[-175.829882,-84.117914],[-174.382503,-84.534323],[-173.116559,-84.117914],[-172.889106,-84.061019],[-169.951223,-83.884647],[-168.999989,-84.117914],[-168.530199,-84.23739],[-167.022099,-84.570497],[-164.182144,-84.82521],[-161.929775,-85.138731],[-158.07138,-85.37391],[-155.192253,-85.09956],[-150.942099,-85.295517],[-148.533073,-85.609038],[-145.888918,-85.315102],[-143.107718,-85.040752],[-142.892279,-84.570497],[-146.829068,-84.531274],[-150.060732,-84.296146],[-150.902928,-83.904232],[-153.586201,-83.68869],[-153.409907,-83.23802],[-153.037759,-82.82652],[-152.665637,-82.454192],[-152.861517,-82.042692],[-154.526299,-81.768394],[-155.29018,-81.41565],[-156.83745,-81.102129],[-154.408787,-81.160937],[-152.097662,-81.004151],[-150.648293,-81.337309],[-148.865998,-81.043373],[-147.22075,-80.671045],[-146.417749,-80.337938],[-146.770286,-79.926439],[-148.062947,-79.652089],[-149.531901,-79.358205],[-151.588416,-79.299397],[-153.390322,-79.162248],[-155.329376,-79.064269],[-155.975668,-78.69194],[-157.268302,-78.378419],[-158.051768,-78.025676],[-158.365134,-76.889207],[-157.875474,-76.987238],[-156.974573,-77.300759],[-155.329376,-77.202728],[-153.742832,-77.065579],[-152.920247,-77.496664],[-151.33378,-77.398737],[-150.00195,-77.183143],[-148.748486,-76.908845],[-147.612483,-76.575738],[-146.104409,-76.47776],[-146.143528,-76.105431],[-146.496091,-75.733154],[-146.20231,-75.380411],[-144.909624,-75.204039],[-144.322037,-75.537197],[-142.794353,-75.34124],[-141.638764,-75.086475],[-140.209007,-75.06689],[-138.85759,-74.968911],[-137.5062,-74.733783],[-136.428901,-74.518241],[-135.214583,-74.302699],[-134.431194,-74.361455],[-133.745654,-74.439848],[-132.257168,-74.302699],[-130.925311,-74.479019],[-129.554284,-74.459433],[-128.242038,-74.322284],[-126.890622,-74.420263],[-125.402082,-74.518241],[-124.011496,-74.479019],[-122.562152,-74.498604],[-121.073613,-74.518241],[-119.70256,-74.479019],[-118.684145,-74.185083],[-117.469801,-74.028348],[-116.216312,-74.243891],[-115.021552,-74.067519],[-113.944331,-73.714828],[-113.297988,-74.028348],[-112.945452,-74.38104],[-112.299083,-74.714198],[-111.261059,-74.420263],[-110.066325,-74.79254],[-108.714909,-74.910103],[-107.559346,-75.184454],[-106.149148,-75.125698],[-104.876074,-74.949326],[-103.367949,-74.988497],[-102.016507,-75.125698],[-100.645531,-75.302018],[-100.1167,-74.870933],[-100.763043,-74.537826],[-101.252703,-74.185083],[-102.545337,-74.106742],[-103.113313,-73.734413],[-103.328752,-73.362084],[-103.681289,-72.61753],[-102.917485,-72.754679],[-101.60524,-72.813436],[-100.312528,-72.754679],[-99.13738,-72.911414],[-98.118889,-73.20535],[-97.688037,-73.558041],[-96.336595,-73.616849],[-95.043961,-73.4797],[-93.672907,-73.283743],[-92.439003,-73.166179],[-91.420564,-73.401307],[-90.088733,-73.322914],[-89.226951,-72.558722],[-88.423951,-73.009393],[-87.268337,-73.185764],[-86.014822,-73.087786],[-85.192236,-73.4797],[-83.879991,-73.518871],[-82.665646,-73.636434],[-81.470913,-73.851977],[-80.687447,-73.4797],[-80.295791,-73.126956],[-79.296886,-73.518871],[-77.925858,-73.420892],[-76.907367,-73.636434],[-76.221879,-73.969541],[-74.890049,-73.871614],[-73.852024,-73.65602],[-72.833533,-73.401307],[-71.619215,-73.264157],[-70.209042,-73.146542],[-68.935916,-73.009393],[-67.956622,-72.79385],[-67.369061,-72.480329],[-67.134036,-72.049244],[-67.251548,-71.637745],[-67.56494,-71.245831],[-67.917477,-70.853917],[-68.230843,-70.462055],[-68.485452,-70.109311],[-68.544209,-69.717397],[-68.446282,-69.325535],[-67.976233,-68.953206],[-67.5845,-68.541707],[-67.427843,-68.149844],[-67.62367,-67.718759],[-67.741183,-67.326845],[-67.251548,-66.876175],[-66.703184,-66.58224],[-66.056815,-66.209963],[-65.371327,-65.89639],[-64.568276,-65.602506],[-64.176542,-65.171423],[-63.628152,-64.897073],[-63.001394,-64.642308],[-62.041686,-64.583552],[-61.414928,-64.270031],[-60.709855,-64.074074],[-59.887269,-63.95651],[-59.162585,-63.701745],[-58.594557,-63.388224],[-57.811143,-63.27066],[-57.223582,-63.525425],[-57.59573,-63.858532],[-58.614143,-64.152467]]]]}}, +{"type":"Feature","id":"ATF","properties":{"name":"French Southern and Antarctic Lands"},"geometry":{"type":"Polygon","coordinates":[[[68.935,-48.625],[69.58,-48.94],[70.525,-49.065],[70.56,-49.255],[70.28,-49.71],[68.745,-49.775],[68.72,-49.2425],[68.8675,-48.83],[68.935,-48.625]]]}}, +{"type":"Feature","id":"AUS","properties":{"name":"Australia"},"geometry":{"type":"MultiPolygon","coordinates":[[[[145.397978,-40.792549],[146.364121,-41.137695],[146.908584,-41.000546],[147.689259,-40.808258],[148.289068,-40.875438],[148.359865,-42.062445],[148.017301,-42.407024],[147.914052,-43.211522],[147.564564,-42.937689],[146.870343,-43.634597],[146.663327,-43.580854],[146.048378,-43.549745],[145.43193,-42.693776],[145.29509,-42.03361],[144.718071,-41.162552],[144.743755,-40.703975],[145.397978,-40.792549]]],[[[143.561811,-13.763656],[143.922099,-14.548311],[144.563714,-14.171176],[144.894908,-14.594458],[145.374724,-14.984976],[145.271991,-15.428205],[145.48526,-16.285672],[145.637033,-16.784918],[145.888904,-16.906926],[146.160309,-17.761655],[146.063674,-18.280073],[146.387478,-18.958274],[147.471082,-19.480723],[148.177602,-19.955939],[148.848414,-20.39121],[148.717465,-20.633469],[149.28942,-21.260511],[149.678337,-22.342512],[150.077382,-22.122784],[150.482939,-22.556142],[150.727265,-22.402405],[150.899554,-23.462237],[151.609175,-24.076256],[152.07354,-24.457887],[152.855197,-25.267501],[153.136162,-26.071173],[153.161949,-26.641319],[153.092909,-27.2603],[153.569469,-28.110067],[153.512108,-28.995077],[153.339095,-29.458202],[153.069241,-30.35024],[153.089602,-30.923642],[152.891578,-31.640446],[152.450002,-32.550003],[151.709117,-33.041342],[151.343972,-33.816023],[151.010555,-34.31036],[150.714139,-35.17346],[150.32822,-35.671879],[150.075212,-36.420206],[149.946124,-37.109052],[149.997284,-37.425261],[149.423882,-37.772681],[148.304622,-37.809061],[147.381733,-38.219217],[146.922123,-38.606532],[146.317922,-39.035757],[145.489652,-38.593768],[144.876976,-38.417448],[145.032212,-37.896188],[144.485682,-38.085324],[143.609974,-38.809465],[142.745427,-38.538268],[142.17833,-38.380034],[141.606582,-38.308514],[140.638579,-38.019333],[139.992158,-37.402936],[139.806588,-36.643603],[139.574148,-36.138362],[139.082808,-35.732754],[138.120748,-35.612296],[138.449462,-35.127261],[138.207564,-34.384723],[137.71917,-35.076825],[136.829406,-35.260535],[137.352371,-34.707339],[137.503886,-34.130268],[137.890116,-33.640479],[137.810328,-32.900007],[136.996837,-33.752771],[136.372069,-34.094766],[135.989043,-34.890118],[135.208213,-34.47867],[135.239218,-33.947953],[134.613417,-33.222778],[134.085904,-32.848072],[134.273903,-32.617234],[132.990777,-32.011224],[132.288081,-31.982647],[131.326331,-31.495803],[129.535794,-31.590423],[128.240938,-31.948489],[127.102867,-32.282267],[126.148714,-32.215966],[125.088623,-32.728751],[124.221648,-32.959487],[124.028947,-33.483847],[123.659667,-33.890179],[122.811036,-33.914467],[122.183064,-34.003402],[121.299191,-33.821036],[120.580268,-33.930177],[119.893695,-33.976065],[119.298899,-34.509366],[119.007341,-34.464149],[118.505718,-34.746819],[118.024972,-35.064733],[117.295507,-35.025459],[116.625109,-35.025097],[115.564347,-34.386428],[115.026809,-34.196517],[115.048616,-33.623425],[115.545123,-33.487258],[115.714674,-33.259572],[115.679379,-32.900369],[115.801645,-32.205062],[115.689611,-31.612437],[115.160909,-30.601594],[114.997043,-30.030725],[115.040038,-29.461095],[114.641974,-28.810231],[114.616498,-28.516399],[114.173579,-28.118077],[114.048884,-27.334765],[113.477498,-26.543134],[113.338953,-26.116545],[113.778358,-26.549025],[113.440962,-25.621278],[113.936901,-25.911235],[114.232852,-26.298446],[114.216161,-25.786281],[113.721255,-24.998939],[113.625344,-24.683971],[113.393523,-24.384764],[113.502044,-23.80635],[113.706993,-23.560215],[113.843418,-23.059987],[113.736552,-22.475475],[114.149756,-21.755881],[114.225307,-22.517488],[114.647762,-21.82952],[115.460167,-21.495173],[115.947373,-21.068688],[116.711615,-20.701682],[117.166316,-20.623599],[117.441545,-20.746899],[118.229559,-20.374208],[118.836085,-20.263311],[118.987807,-20.044203],[119.252494,-19.952942],[119.805225,-19.976506],[120.85622,-19.683708],[121.399856,-19.239756],[121.655138,-18.705318],[122.241665,-18.197649],[122.286624,-17.798603],[122.312772,-17.254967],[123.012574,-16.4052],[123.433789,-17.268558],[123.859345,-17.069035],[123.503242,-16.596506],[123.817073,-16.111316],[124.258287,-16.327944],[124.379726,-15.56706],[124.926153,-15.0751],[125.167275,-14.680396],[125.670087,-14.51007],[125.685796,-14.230656],[126.125149,-14.347341],[126.142823,-14.095987],[126.582589,-13.952791],[127.065867,-13.817968],[127.804633,-14.276906],[128.35969,-14.86917],[128.985543,-14.875991],[129.621473,-14.969784],[129.4096,-14.42067],[129.888641,-13.618703],[130.339466,-13.357376],[130.183506,-13.10752],[130.617795,-12.536392],[131.223495,-12.183649],[131.735091,-12.302453],[132.575298,-12.114041],[132.557212,-11.603012],[131.824698,-11.273782],[132.357224,-11.128519],[133.019561,-11.376411],[133.550846,-11.786515],[134.393068,-12.042365],[134.678632,-11.941183],[135.298491,-12.248606],[135.882693,-11.962267],[136.258381,-12.049342],[136.492475,-11.857209],[136.95162,-12.351959],[136.685125,-12.887223],[136.305407,-13.29123],[135.961758,-13.324509],[136.077617,-13.724278],[135.783836,-14.223989],[135.428664,-14.715432],[135.500184,-14.997741],[136.295175,-15.550265],[137.06536,-15.870762],[137.580471,-16.215082],[138.303217,-16.807604],[138.585164,-16.806622],[139.108543,-17.062679],[139.260575,-17.371601],[140.215245,-17.710805],[140.875463,-17.369069],[141.07111,-16.832047],[141.274095,-16.38887],[141.398222,-15.840532],[141.702183,-15.044921],[141.56338,-14.561333],[141.63552,-14.270395],[141.519869,-13.698078],[141.65092,-12.944688],[141.842691,-12.741548],[141.68699,-12.407614],[141.928629,-11.877466],[142.118488,-11.328042],[142.143706,-11.042737],[142.51526,-10.668186],[142.79731,-11.157355],[142.866763,-11.784707],[143.115947,-11.90563],[143.158632,-12.325656],[143.522124,-12.834358],[143.597158,-13.400422],[143.561811,-13.763656]]]]}}, +{"type":"Feature","id":"AUT","properties":{"name":"Austria"},"geometry":{"type":"Polygon","coordinates":[[[16.979667,48.123497],[16.903754,47.714866],[16.340584,47.712902],[16.534268,47.496171],[16.202298,46.852386],[16.011664,46.683611],[15.137092,46.658703],[14.632472,46.431817],[13.806475,46.509306],[12.376485,46.767559],[12.153088,47.115393],[11.164828,46.941579],[11.048556,46.751359],[10.442701,46.893546],[9.932448,46.920728],[9.47997,47.10281],[9.632932,47.347601],[9.594226,47.525058],[9.896068,47.580197],[10.402084,47.302488],[10.544504,47.566399],[11.426414,47.523766],[12.141357,47.703083],[12.62076,47.672388],[12.932627,47.467646],[13.025851,47.637584],[12.884103,48.289146],[13.243357,48.416115],[13.595946,48.877172],[14.338898,48.555305],[14.901447,48.964402],[15.253416,49.039074],[16.029647,48.733899],[16.499283,48.785808],[16.960288,48.596982],[16.879983,48.470013],[16.979667,48.123497]]]}}, +{"type":"Feature","id":"AZE","properties":{"name":"Azerbaijan"},"geometry":{"type":"MultiPolygon","coordinates":[[[[45.001987,39.740004],[45.298145,39.471751],[45.739978,39.473999],[45.735379,39.319719],[46.143623,38.741201],[45.457722,38.874139],[44.952688,39.335765],[44.79399,39.713003],[45.001987,39.740004]]],[[[47.373315,41.219732],[47.815666,41.151416],[47.987283,41.405819],[48.584353,41.80887],[49.110264,41.282287],[49.618915,40.572924],[50.08483,40.526157],[50.392821,40.256561],[49.569202,40.176101],[49.395259,39.399482],[49.223228,39.049219],[48.856532,38.815486],[48.883249,38.320245],[48.634375,38.270378],[48.010744,38.794015],[48.355529,39.288765],[48.060095,39.582235],[47.685079,39.508364],[46.50572,38.770605],[46.483499,39.464155],[46.034534,39.628021],[45.610012,39.899994],[45.891907,40.218476],[45.359175,40.561504],[45.560351,40.81229],[45.179496,40.985354],[44.97248,41.248129],[45.217426,41.411452],[45.962601,41.123873],[46.501637,41.064445],[46.637908,41.181673],[46.145432,41.722802],[46.404951,41.860675],[46.686071,41.827137],[47.373315,41.219732]]]]}}, +{"type":"Feature","id":"BDI","properties":{"name":"Burundi"},"geometry":{"type":"Polygon","coordinates":[[[29.339998,-4.499983],[29.276384,-3.293907],[29.024926,-2.839258],[29.632176,-2.917858],[29.938359,-2.348487],[30.469696,-2.413858],[30.527677,-2.807632],[30.743013,-3.034285],[30.752263,-3.35933],[30.50556,-3.568567],[30.116333,-4.090138],[29.753512,-4.452389],[29.339998,-4.499983]]]}}, +{"type":"Feature","id":"BEL","properties":{"name":"Belgium"},"geometry":{"type":"Polygon","coordinates":[[[3.314971,51.345781],[4.047071,51.267259],[4.973991,51.475024],[5.606976,51.037298],[6.156658,50.803721],[6.043073,50.128052],[5.782417,50.090328],[5.674052,49.529484],[4.799222,49.985373],[4.286023,49.907497],[3.588184,50.378992],[3.123252,50.780363],[2.658422,50.796848],[2.513573,51.148506],[3.314971,51.345781]]]}}, +{"type":"Feature","id":"BEN","properties":{"name":"Benin"},"geometry":{"type":"Polygon","coordinates":[[[2.691702,6.258817],[1.865241,6.142158],[1.618951,6.832038],[1.664478,9.12859],[1.463043,9.334624],[1.425061,9.825395],[1.077795,10.175607],[0.772336,10.470808],[0.899563,10.997339],[1.24347,11.110511],[1.447178,11.547719],[1.935986,11.64115],[2.154474,11.94015],[2.490164,12.233052],[2.848643,12.235636],[3.61118,11.660167],[3.572216,11.327939],[3.797112,10.734746],[3.60007,10.332186],[3.705438,10.06321],[3.220352,9.444153],[2.912308,9.137608],[2.723793,8.506845],[2.749063,7.870734],[2.691702,6.258817]]]}}, +{"type":"Feature","id":"BFA","properties":{"name":"Burkina Faso"},"geometry":{"type":"Polygon","coordinates":[[[-2.827496,9.642461],[-3.511899,9.900326],[-3.980449,9.862344],[-4.330247,9.610835],[-4.779884,9.821985],[-4.954653,10.152714],[-5.404342,10.370737],[-5.470565,10.95127],[-5.197843,11.375146],[-5.220942,11.713859],[-4.427166,12.542646],[-4.280405,13.228444],[-4.006391,13.472485],[-3.522803,13.337662],[-3.103707,13.541267],[-2.967694,13.79815],[-2.191825,14.246418],[-2.001035,14.559008],[-1.066363,14.973815],[-0.515854,15.116158],[-0.266257,14.924309],[0.374892,14.928908],[0.295646,14.444235],[0.429928,13.988733],[0.993046,13.33575],[1.024103,12.851826],[2.177108,12.625018],[2.154474,11.94015],[1.935986,11.64115],[1.447178,11.547719],[1.24347,11.110511],[0.899563,10.997339],[0.023803,11.018682],[-0.438702,11.098341],[-0.761576,10.93693],[-1.203358,11.009819],[-2.940409,10.96269],[-2.963896,10.395335],[-2.827496,9.642461]]]}}, +{"type":"Feature","id":"BGD","properties":{"name":"Bangladesh"},"geometry":{"type":"Polygon","coordinates":[[[92.672721,22.041239],[92.652257,21.324048],[92.303234,21.475485],[92.368554,20.670883],[92.082886,21.192195],[92.025215,21.70157],[91.834891,22.182936],[91.417087,22.765019],[90.496006,22.805017],[90.586957,22.392794],[90.272971,21.836368],[89.847467,22.039146],[89.70205,21.857116],[89.418863,21.966179],[89.031961,22.055708],[88.876312,22.879146],[88.52977,23.631142],[88.69994,24.233715],[88.084422,24.501657],[88.306373,24.866079],[88.931554,25.238692],[88.209789,25.768066],[88.563049,26.446526],[89.355094,26.014407],[89.832481,25.965082],[89.920693,25.26975],[90.872211,25.132601],[91.799596,25.147432],[92.376202,24.976693],[91.915093,24.130414],[91.46773,24.072639],[91.158963,23.503527],[91.706475,22.985264],[91.869928,23.624346],[92.146035,23.627499],[92.672721,22.041239]]]}}, +{"type":"Feature","id":"BGR","properties":{"name":"Bulgaria"},"geometry":{"type":"Polygon","coordinates":[[[22.65715,44.234923],[22.944832,43.823785],[23.332302,43.897011],[24.100679,43.741051],[25.569272,43.688445],[26.065159,43.943494],[27.2424,44.175986],[27.970107,43.812468],[28.558081,43.707462],[28.039095,43.293172],[27.673898,42.577892],[27.99672,42.007359],[27.135739,42.141485],[26.117042,41.826905],[26.106138,41.328899],[25.197201,41.234486],[24.492645,41.583896],[23.692074,41.309081],[22.952377,41.337994],[22.881374,41.999297],[22.380526,42.32026],[22.545012,42.461362],[22.436595,42.580321],[22.604801,42.898519],[22.986019,43.211161],[22.500157,43.642814],[22.410446,44.008063],[22.65715,44.234923]]]}}, +{"type":"Feature","id":"BHS","properties":{"name":"The Bahamas"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-77.53466,23.75975],[-77.78,23.71],[-78.03405,24.28615],[-78.40848,24.57564],[-78.19087,25.2103],[-77.89,25.17],[-77.54,24.34],[-77.53466,23.75975]]],[[[-77.82,26.58],[-78.91,26.42],[-78.98,26.79],[-78.51,26.87],[-77.85,26.84],[-77.82,26.58]]],[[[-77,26.59],[-77.17255,25.87918],[-77.35641,26.00735],[-77.34,26.53],[-77.78802,26.92516],[-77.79,27.04],[-77,26.59]]]]}}, +{"type":"Feature","id":"BIH","properties":{"name":"Bosnia and Herzegovina"},"geometry":{"type":"Polygon","coordinates":[[[19.005486,44.860234],[19.36803,44.863],[19.11761,44.42307],[19.59976,44.03847],[19.454,43.5681],[19.21852,43.52384],[19.03165,43.43253],[18.70648,43.20011],[18.56,42.65],[17.674922,43.028563],[17.297373,43.446341],[16.916156,43.667722],[16.456443,44.04124],[16.23966,44.351143],[15.750026,44.818712],[15.959367,45.233777],[16.318157,45.004127],[16.534939,45.211608],[17.002146,45.233777],[17.861783,45.06774],[18.553214,45.08159],[19.005486,44.860234]]]}}, +{"type":"Feature","id":"BLR","properties":{"name":"Belarus"},"geometry":{"type":"Polygon","coordinates":[[[23.484128,53.912498],[24.450684,53.905702],[25.536354,54.282423],[25.768433,54.846963],[26.588279,55.167176],[26.494331,55.615107],[27.10246,55.783314],[28.176709,56.16913],[29.229513,55.918344],[29.371572,55.670091],[29.896294,55.789463],[30.873909,55.550976],[30.971836,55.081548],[30.757534,54.811771],[31.384472,54.157056],[31.791424,53.974639],[31.731273,53.794029],[32.405599,53.618045],[32.693643,53.351421],[32.304519,53.132726],[31.497644,53.167427],[31.305201,53.073996],[31.540018,52.742052],[31.785998,52.101678],[30.927549,52.042353],[30.619454,51.822806],[30.555117,51.319503],[30.157364,51.416138],[29.254938,51.368234],[28.992835,51.602044],[28.617613,51.427714],[28.241615,51.572227],[27.454066,51.592303],[26.337959,51.832289],[25.327788,51.910656],[24.553106,51.888461],[24.005078,51.617444],[23.527071,51.578454],[23.508002,52.023647],[23.199494,52.486977],[23.799199,52.691099],[23.804935,53.089731],[23.527536,53.470122],[23.484128,53.912498]]]}}, +{"type":"Feature","id":"BLZ","properties":{"name":"Belize"},"geometry":{"type":"Polygon","coordinates":[[[-89.14308,17.808319],[-89.150909,17.955468],[-89.029857,18.001511],[-88.848344,17.883198],[-88.490123,18.486831],[-88.300031,18.499982],[-88.296336,18.353273],[-88.106813,18.348674],[-88.123479,18.076675],[-88.285355,17.644143],[-88.197867,17.489475],[-88.302641,17.131694],[-88.239518,17.036066],[-88.355428,16.530774],[-88.551825,16.265467],[-88.732434,16.233635],[-88.930613,15.887273],[-89.229122,15.886938],[-89.150806,17.015577],[-89.14308,17.808319]]]}}, +{"type":"Feature","id":"BMU","properties":{"name":"Bermuda"},"geometry":{"type":"Polygon","coordinates":[[[-64.7799734332998,32.3072000581802],[-64.7873319183061,32.3039237143428],[-64.7946942710173,32.3032682700388],[-64.8094297981283,32.3098175728414],[-64.8167896352437,32.3058845718466],[-64.8101968029642,32.3022833180511],[-64.7962291465484,32.2934409732427],[-64.7815086336978,32.2868973114514],[-64.7997025513437,32.2796896417328],[-64.8066707691087,32.2747767569465],[-64.8225587873683,32.2669111289395],[-64.8287548840306,32.2669075473817],[-64.8306732143498,32.2583944840235],[-64.8399924854972,32.254782282336],[-64.8566090462354,32.2547740387514],[-64.8682296789446,32.2616393614322],[-64.8628241459563,32.2724481933959],[-64.8748651338951,32.2757120264753],[-64.8717752856644,32.2819371582026],[-64.8671422127295,32.2930760547989],[-64.8559068764437,32.2960321186471],[-64.8597429072279,32.3015842021933],[-64.8439233486717,32.3140553852543],[-64.8350242329311,32.3242161760006],[-64.8338690593672,32.3294587561557],[-64.8520298651164,32.3110911879954],[-64.8635922932573,32.3048469433363],[-64.8686668994079,32.30910745083],[-64.8721354593415,32.3041908606301],[-64.8779667328485,32.3038632800462],[-64.8780046844321,32.2907757831692],[-64.8849776658292,32.2819261366004],[-64.8783230004629,32.2613001418681],[-64.863194968877,32.2465799485801],[-64.8519819555722,32.2485519134663],[-64.842311980074,32.2492123317296],[-64.8388242605209,32.2475773472534],[-64.8334002575532,32.2462714714698],[-64.8256389530584,32.2472637398594],[-64.8205697556026,32.2531698880328],[-64.8105087275579,32.2561208974156],[-64.7900177727338,32.2659446936992],[-64.7745415970416,32.2718413023427],[-64.7644742436426,32.2855931353214],[-64.7551803442276,32.2908326702531],[-64.7423982971436,32.2996734994024],[-64.7206991797682,32.3137542201258],[-64.7117851247134,32.3176823360806],[-64.6962778813133,32.3275029115532],[-64.6768921127452,32.3324095397555],[-64.6567136927777,32.3451776458469],[-64.6532168823499,32.3494356627941],[-64.6605720384429,32.3589423487763],[-64.65125819471,32.3615600906466],[-64.6462011670816,32.36975169749],[-64.6613227512832,32.3763135008721],[-64.6690666074397,32.388444543924],[-64.6834270548595,32.3854968316788],[-64.6954617672714,32.3763221285869],[-64.70438689565,32.3704254760469],[-64.7117569982798,32.368132600249],[-64.7061764744404,32.3600110593559],[-64.700531552697,32.3590601356818],[-64.6940348033967,32.3640708659835],[-64.6895164826082,32.3633598579866],[-64.6864150099255,32.3547797587266],[-64.6824635995504,32.3540628176846],[-64.6835876652835,32.3626447677968],[-64.6801998697415,32.3631199096979],[-64.6672170444687,32.3597751617473],[-64.6598811264978,32.3497625771755],[-64.6737331235384,32.3390281851635],[-64.6887090648183,32.3342439408053],[-64.706732854446,32.3429010723036],[-64.7149301576112,32.3552188753513],[-64.7185967666669,32.3552239212394],[-64.7214189847314,32.3518830231342],[-64.7270616067222,32.3466461715475],[-64.734962460882,32.3442819830499],[-64.7383521549094,32.3407216514918],[-64.7411729976333,32.3311790864627],[-64.7423019216485,32.323311561213],[-64.7462482354281,32.318538611581],[-64.7566773739613,32.3130509130175],[-64.768738200563,32.3088369816572],[-64.7799734332998,32.3072000581802]]]}}, +{"type":"Feature","id":"BOL","properties":{"name":"Bolivia"},"geometry":{"type":"Polygon","coordinates":[[[-62.846468,-22.034985],[-63.986838,-21.993644],[-64.377021,-22.798091],[-64.964892,-22.075862],[-66.273339,-21.83231],[-67.106674,-22.735925],[-67.82818,-22.872919],[-68.219913,-21.494347],[-68.757167,-20.372658],[-68.442225,-19.405068],[-68.966818,-18.981683],[-69.100247,-18.260125],[-69.590424,-17.580012],[-68.959635,-16.500698],[-69.389764,-15.660129],[-69.160347,-15.323974],[-69.339535,-14.953195],[-68.948887,-14.453639],[-68.929224,-13.602684],[-68.88008,-12.899729],[-68.66508,-12.5613],[-69.529678,-10.951734],[-68.786158,-11.03638],[-68.271254,-11.014521],[-68.048192,-10.712059],[-67.173801,-10.306812],[-66.646908,-9.931331],[-65.338435,-9.761988],[-65.444837,-10.511451],[-65.321899,-10.895872],[-65.402281,-11.56627],[-64.316353,-12.461978],[-63.196499,-12.627033],[-62.80306,-13.000653],[-62.127081,-13.198781],[-61.713204,-13.489202],[-61.084121,-13.479384],[-60.503304,-13.775955],[-60.459198,-14.354007],[-60.264326,-14.645979],[-60.251149,-15.077219],[-60.542966,-15.09391],[-60.15839,-16.258284],[-58.24122,-16.299573],[-58.388058,-16.877109],[-58.280804,-17.27171],[-57.734558,-17.552468],[-57.498371,-18.174188],[-57.676009,-18.96184],[-57.949997,-19.400004],[-57.853802,-19.969995],[-58.166392,-20.176701],[-58.183471,-19.868399],[-59.115042,-19.356906],[-60.043565,-19.342747],[-61.786326,-19.633737],[-62.265961,-20.513735],[-62.291179,-21.051635],[-62.685057,-22.249029],[-62.846468,-22.034985]]]}}, +{"type":"Feature","id":"BRA","properties":{"name":"Brazil"},"geometry":{"type":"Polygon","coordinates":[[[-57.625133,-30.216295],[-56.2909,-28.852761],[-55.162286,-27.881915],[-54.490725,-27.474757],[-53.648735,-26.923473],[-53.628349,-26.124865],[-54.13005,-25.547639],[-54.625291,-25.739255],[-54.428946,-25.162185],[-54.293476,-24.5708],[-54.29296,-24.021014],[-54.652834,-23.839578],[-55.027902,-24.001274],[-55.400747,-23.956935],[-55.517639,-23.571998],[-55.610683,-22.655619],[-55.797958,-22.35693],[-56.473317,-22.0863],[-56.88151,-22.282154],[-57.937156,-22.090176],[-57.870674,-20.732688],[-58.166392,-20.176701],[-57.853802,-19.969995],[-57.949997,-19.400004],[-57.676009,-18.96184],[-57.498371,-18.174188],[-57.734558,-17.552468],[-58.280804,-17.27171],[-58.388058,-16.877109],[-58.24122,-16.299573],[-60.15839,-16.258284],[-60.542966,-15.09391],[-60.251149,-15.077219],[-60.264326,-14.645979],[-60.459198,-14.354007],[-60.503304,-13.775955],[-61.084121,-13.479384],[-61.713204,-13.489202],[-62.127081,-13.198781],[-62.80306,-13.000653],[-63.196499,-12.627033],[-64.316353,-12.461978],[-65.402281,-11.56627],[-65.321899,-10.895872],[-65.444837,-10.511451],[-65.338435,-9.761988],[-66.646908,-9.931331],[-67.173801,-10.306812],[-68.048192,-10.712059],[-68.271254,-11.014521],[-68.786158,-11.03638],[-69.529678,-10.951734],[-70.093752,-11.123972],[-70.548686,-11.009147],[-70.481894,-9.490118],[-71.302412,-10.079436],[-72.184891,-10.053598],[-72.563033,-9.520194],[-73.226713,-9.462213],[-73.015383,-9.032833],[-73.571059,-8.424447],[-73.987235,-7.52383],[-73.723401,-7.340999],[-73.724487,-6.918595],[-73.120027,-6.629931],[-73.219711,-6.089189],[-72.964507,-5.741251],[-72.891928,-5.274561],[-71.748406,-4.593983],[-70.928843,-4.401591],[-70.794769,-4.251265],[-69.893635,-4.298187],[-69.444102,-1.556287],[-69.420486,-1.122619],[-69.577065,-0.549992],[-70.020656,-0.185156],[-70.015566,0.541414],[-69.452396,0.706159],[-69.252434,0.602651],[-69.218638,0.985677],[-69.804597,1.089081],[-69.816973,1.714805],[-67.868565,1.692455],[-67.53781,2.037163],[-67.259998,1.719999],[-67.065048,1.130112],[-66.876326,1.253361],[-66.325765,0.724452],[-65.548267,0.789254],[-65.354713,1.095282],[-64.611012,1.328731],[-64.199306,1.492855],[-64.083085,1.916369],[-63.368788,2.2009],[-63.422867,2.411068],[-64.269999,2.497006],[-64.408828,3.126786],[-64.368494,3.79721],[-64.816064,4.056445],[-64.628659,4.148481],[-63.888343,4.02053],[-63.093198,3.770571],[-62.804533,4.006965],[-62.08543,4.162124],[-60.966893,4.536468],[-60.601179,4.918098],[-60.733574,5.200277],[-60.213683,5.244486],[-59.980959,5.014061],[-60.111002,4.574967],[-59.767406,4.423503],[-59.53804,3.958803],[-59.815413,3.606499],[-59.974525,2.755233],[-59.718546,2.24963],[-59.646044,1.786894],[-59.030862,1.317698],[-58.540013,1.268088],[-58.429477,1.463942],[-58.11345,1.507195],[-57.660971,1.682585],[-57.335823,1.948538],[-56.782704,1.863711],[-56.539386,1.899523],[-55.995698,1.817667],[-55.9056,2.021996],[-56.073342,2.220795],[-55.973322,2.510364],[-55.569755,2.421506],[-55.097587,2.523748],[-54.524754,2.311849],[-54.088063,2.105557],[-53.778521,2.376703],[-53.554839,2.334897],[-53.418465,2.053389],[-52.939657,2.124858],[-52.556425,2.504705],[-52.249338,3.241094],[-51.657797,4.156232],[-51.317146,4.203491],[-51.069771,3.650398],[-50.508875,1.901564],[-49.974076,1.736483],[-49.947101,1.04619],[-50.699251,0.222984],[-50.388211,-0.078445],[-48.620567,-0.235489],[-48.584497,-1.237805],[-47.824956,-0.581618],[-46.566584,-0.941028],[-44.905703,-1.55174],[-44.417619,-2.13775],[-44.581589,-2.691308],[-43.418791,-2.38311],[-41.472657,-2.912018],[-39.978665,-2.873054],[-38.500383,-3.700652],[-37.223252,-4.820946],[-36.452937,-5.109404],[-35.597796,-5.149504],[-35.235389,-5.464937],[-34.89603,-6.738193],[-34.729993,-7.343221],[-35.128212,-8.996401],[-35.636967,-9.649282],[-37.046519,-11.040721],[-37.683612,-12.171195],[-38.423877,-13.038119],[-38.673887,-13.057652],[-38.953276,-13.79337],[-38.882298,-15.667054],[-39.161092,-17.208407],[-39.267339,-17.867746],[-39.583521,-18.262296],[-39.760823,-19.599113],[-40.774741,-20.904512],[-40.944756,-21.937317],[-41.754164,-22.370676],[-41.988284,-22.97007],[-43.074704,-22.967693],[-44.647812,-23.351959],[-45.352136,-23.796842],[-46.472093,-24.088969],[-47.648972,-24.885199],[-48.495458,-25.877025],[-48.641005,-26.623698],[-48.474736,-27.175912],[-48.66152,-28.186135],[-48.888457,-28.674115],[-49.587329,-29.224469],[-50.696874,-30.984465],[-51.576226,-31.777698],[-52.256081,-32.24537],[-52.7121,-33.196578],[-53.373662,-33.768378],[-53.650544,-33.202004],[-53.209589,-32.727666],[-53.787952,-32.047243],[-54.572452,-31.494511],[-55.60151,-30.853879],[-55.973245,-30.883076],[-56.976026,-30.109686],[-57.625133,-30.216295]]]}}, +{"type":"Feature","id":"BRN","properties":{"name":"Brunei"},"geometry":{"type":"Polygon","coordinates":[[[114.204017,4.525874],[114.599961,4.900011],[115.45071,5.44773],[115.4057,4.955228],[115.347461,4.316636],[114.869557,4.348314],[114.659596,4.007637],[114.204017,4.525874]]]}}, +{"type":"Feature","id":"BTN","properties":{"name":"Bhutan"},"geometry":{"type":"Polygon","coordinates":[[[91.696657,27.771742],[92.103712,27.452614],[92.033484,26.83831],[91.217513,26.808648],[90.373275,26.875724],[89.744528,26.719403],[88.835643,27.098966],[88.814248,27.299316],[89.47581,28.042759],[90.015829,28.296439],[90.730514,28.064954],[91.258854,28.040614],[91.696657,27.771742]]]}}, +{"type":"Feature","id":"BWA","properties":{"name":"Botswana"},"geometry":{"type":"Polygon","coordinates":[[[25.649163,-18.536026],[25.850391,-18.714413],[26.164791,-19.293086],[27.296505,-20.39152],[27.724747,-20.499059],[27.727228,-20.851802],[28.02137,-21.485975],[28.794656,-21.639454],[29.432188,-22.091313],[28.017236,-22.827754],[27.11941,-23.574323],[26.786407,-24.240691],[26.485753,-24.616327],[25.941652,-24.696373],[25.765849,-25.174845],[25.664666,-25.486816],[25.025171,-25.71967],[24.211267,-25.670216],[23.73357,-25.390129],[23.312097,-25.26869],[22.824271,-25.500459],[22.579532,-25.979448],[22.105969,-26.280256],[21.605896,-26.726534],[20.889609,-26.828543],[20.66647,-26.477453],[20.758609,-25.868136],[20.165726,-24.917962],[19.895768,-24.76779],[19.895458,-21.849157],[20.881134,-21.814327],[20.910641,-18.252219],[21.65504,-18.219146],[23.196858,-17.869038],[23.579006,-18.281261],[24.217365,-17.889347],[24.520705,-17.887125],[25.084443,-17.661816],[25.264226,-17.73654],[25.649163,-18.536026]]]}}, +{"type":"Feature","id":"CAF","properties":{"name":"Central African Republic"},"geometry":{"type":"Polygon","coordinates":[[[15.27946,7.421925],[16.106232,7.497088],[16.290562,7.754307],[16.456185,7.734774],[16.705988,7.508328],[17.96493,7.890914],[18.389555,8.281304],[18.911022,8.630895],[18.81201,8.982915],[19.094008,9.074847],[20.059685,9.012706],[21.000868,9.475985],[21.723822,10.567056],[22.231129,10.971889],[22.864165,11.142395],[22.977544,10.714463],[23.554304,10.089255],[23.55725,9.681218],[23.394779,9.265068],[23.459013,8.954286],[23.805813,8.666319],[24.567369,8.229188],[25.114932,7.825104],[25.124131,7.500085],[25.796648,6.979316],[26.213418,6.546603],[26.465909,5.946717],[27.213409,5.550953],[27.374226,5.233944],[27.044065,5.127853],[26.402761,5.150875],[25.650455,5.256088],[25.278798,5.170408],[25.128833,4.927245],[24.805029,4.897247],[24.410531,5.108784],[23.297214,4.609693],[22.84148,4.710126],[22.704124,4.633051],[22.405124,4.02916],[21.659123,4.224342],[20.927591,4.322786],[20.290679,4.691678],[19.467784,5.031528],[18.932312,4.709506],[18.542982,4.201785],[18.453065,3.504386],[17.8099,3.560196],[17.133042,3.728197],[16.537058,3.198255],[16.012852,2.26764],[15.907381,2.557389],[15.862732,3.013537],[15.405396,3.335301],[15.03622,3.851367],[14.950953,4.210389],[14.478372,4.732605],[14.558936,5.030598],[14.459407,5.451761],[14.53656,6.226959],[14.776545,6.408498],[15.27946,7.421925]]]}}, +{"type":"Feature","id":"CAN","properties":{"name":"Canada"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-63.6645,46.55001],[-62.9393,46.41587],[-62.01208,46.44314],[-62.50391,46.03339],[-62.87433,45.96818],[-64.1428,46.39265],[-64.39261,46.72747],[-64.01486,47.03601],[-63.6645,46.55001]]],[[[-61.806305,49.10506],[-62.29318,49.08717],[-63.58926,49.40069],[-64.51912,49.87304],[-64.17322,49.95718],[-62.85829,49.70641],[-61.835585,49.28855],[-61.806305,49.10506]]],[[[-123.510002,48.510011],[-124.012891,48.370846],[-125.655013,48.825005],[-125.954994,49.179996],[-126.850004,49.53],[-127.029993,49.814996],[-128.059336,49.994959],[-128.444584,50.539138],[-128.358414,50.770648],[-127.308581,50.552574],[-126.695001,50.400903],[-125.755007,50.295018],[-125.415002,49.950001],[-124.920768,49.475275],[-123.922509,49.062484],[-123.510002,48.510011]]],[[[-56.134036,50.68701],[-56.795882,49.812309],[-56.143105,50.150117],[-55.471492,49.935815],[-55.822401,49.587129],[-54.935143,49.313011],[-54.473775,49.556691],[-53.476549,49.249139],[-53.786014,48.516781],[-53.086134,48.687804],[-52.958648,48.157164],[-52.648099,47.535548],[-53.069158,46.655499],[-53.521456,46.618292],[-54.178936,46.807066],[-53.961869,47.625207],[-54.240482,47.752279],[-55.400773,46.884994],[-55.997481,46.91972],[-55.291219,47.389562],[-56.250799,47.632545],[-57.325229,47.572807],[-59.266015,47.603348],[-59.419494,47.899454],[-58.796586,48.251525],[-59.231625,48.523188],[-58.391805,49.125581],[-57.35869,50.718274],[-56.73865,51.287438],[-55.870977,51.632094],[-55.406974,51.588273],[-55.600218,51.317075],[-56.134036,50.68701]]],[[[-132.710008,54.040009],[-132.710009,54.040009],[-132.710008,54.040009],[-132.710008,54.040009],[-131.74999,54.120004],[-132.04948,52.984621],[-131.179043,52.180433],[-131.57783,52.182371],[-132.180428,52.639707],[-132.549992,53.100015],[-133.054611,53.411469],[-133.239664,53.85108],[-133.180004,54.169975],[-132.710008,54.040009]]],[[[-79.26582,62.158675],[-79.65752,61.63308],[-80.09956,61.7181],[-80.36215,62.01649],[-80.315395,62.085565],[-79.92939,62.3856],[-79.52002,62.36371],[-79.26582,62.158675]]],[[[-81.89825,62.7108],[-83.06857,62.15922],[-83.77462,62.18231],[-83.99367,62.4528],[-83.25048,62.91409],[-81.87699,62.90458],[-81.89825,62.7108]]],[[[-85.161308,65.657285],[-84.975764,65.217518],[-84.464012,65.371772],[-83.882626,65.109618],[-82.787577,64.766693],[-81.642014,64.455136],[-81.55344,63.979609],[-80.817361,64.057486],[-80.103451,63.725981],[-80.99102,63.411246],[-82.547178,63.651722],[-83.108798,64.101876],[-84.100417,63.569712],[-85.523405,63.052379],[-85.866769,63.637253],[-87.221983,63.541238],[-86.35276,64.035833],[-86.224886,64.822917],[-85.883848,65.738778],[-85.161308,65.657285]]],[[[-75.86588,67.14886],[-76.98687,67.09873],[-77.2364,67.58809],[-76.81166,68.14856],[-75.89521,68.28721],[-75.1145,68.01036],[-75.10333,67.58202],[-75.21597,67.44425],[-75.86588,67.14886]]],[[[-95.647681,69.10769],[-96.269521,68.75704],[-97.617401,69.06003],[-98.431801,68.9507],[-99.797401,69.40003],[-98.917401,69.71003],[-98.218261,70.14354],[-97.157401,69.86003],[-96.557401,69.68003],[-96.257401,69.49003],[-95.647681,69.10769]]],[[[-90.5471,69.49766],[-90.55151,68.47499],[-89.21515,69.25873],[-88.01966,68.61508],[-88.31749,67.87338],[-87.35017,67.19872],[-86.30607,67.92146],[-85.57664,68.78456],[-85.52197,69.88211],[-84.10081,69.80539],[-82.62258,69.65826],[-81.28043,69.16202],[-81.2202,68.66567],[-81.96436,68.13253],[-81.25928,67.59716],[-81.38653,67.11078],[-83.34456,66.41154],[-84.73542,66.2573],[-85.76943,66.55833],[-86.0676,66.05625],[-87.03143,65.21297],[-87.32324,64.77563],[-88.48296,64.09897],[-89.91444,64.03273],[-90.70398,63.61017],[-90.77004,62.96021],[-91.93342,62.83508],[-93.15698,62.02469],[-94.24153,60.89865],[-94.62931,60.11021],[-94.6846,58.94882],[-93.21502,58.78212],[-92.76462,57.84571],[-92.29703,57.08709],[-90.89769,57.28468],[-89.03953,56.85172],[-88.03978,56.47162],[-87.32421,55.99914],[-86.07121,55.72383],[-85.01181,55.3026],[-83.36055,55.24489],[-82.27285,55.14832],[-82.4362,54.28227],[-82.12502,53.27703],[-81.40075,52.15788],[-79.91289,51.20842],[-79.14301,51.53393],[-78.60191,52.56208],[-79.12421,54.14145],[-79.82958,54.66772],[-78.22874,55.13645],[-77.0956,55.83741],[-76.54137,56.53423],[-76.62319,57.20263],[-77.30226,58.05209],[-78.51688,58.80458],[-77.33676,59.85261],[-77.77272,60.75788],[-78.10687,62.31964],[-77.41067,62.55053],[-75.69621,62.2784],[-74.6682,62.18111],[-73.83988,62.4438],[-72.90853,62.10507],[-71.67708,61.52535],[-71.37369,61.13717],[-69.59042,61.06141],[-69.62033,60.22125],[-69.2879,58.95736],[-68.37455,58.80106],[-67.64976,58.21206],[-66.20178,58.76731],[-65.24517,59.87071],[-64.58352,60.33558],[-63.80475,59.4426],[-62.50236,58.16708],[-61.39655,56.96745],[-61.79866,56.33945],[-60.46853,55.77548],[-59.56962,55.20407],[-57.97508,54.94549],[-57.3332,54.6265],[-56.93689,53.78032],[-56.15811,53.64749],[-55.75632,53.27036],[-55.68338,52.14664],[-56.40916,51.7707],[-57.12691,51.41972],[-58.77482,51.0643],[-60.03309,50.24277],[-61.72366,50.08046],[-63.86251,50.29099],[-65.36331,50.2982],[-66.39905,50.22897],[-67.23631,49.51156],[-68.51114,49.06836],[-69.95362,47.74488],[-71.10458,46.82171],[-70.25522,46.98606],[-68.65,48.3],[-66.55243,49.1331],[-65.05626,49.23278],[-64.17099,48.74248],[-65.11545,48.07085],[-64.79854,46.99297],[-64.47219,46.23849],[-63.17329,45.73902],[-61.52072,45.88377],[-60.51815,47.00793],[-60.4486,46.28264],[-59.80287,45.9204],[-61.03988,45.26525],[-63.25471,44.67014],[-64.24656,44.26553],[-65.36406,43.54523],[-66.1234,43.61867],[-66.16173,44.46512],[-64.42549,45.29204],[-66.02605,45.25931],[-67.13741,45.13753],[-67.79134,45.70281],[-67.79046,47.06636],[-68.23444,47.35486],[-68.905,47.185],[-69.237216,47.447781],[-69.99997,46.69307],[-70.305,45.915],[-70.66,45.46],[-71.08482,45.30524],[-71.405,45.255],[-71.50506,45.0082],[-73.34783,45.00738],[-74.867,45.00048],[-75.31821,44.81645],[-76.375,44.09631],[-76.5,44.018459],[-76.820034,43.628784],[-77.737885,43.629056],[-78.72028,43.625089],[-79.171674,43.466339],[-79.01,43.27],[-78.92,42.965],[-78.939362,42.863611],[-80.247448,42.3662],[-81.277747,42.209026],[-82.439278,41.675105],[-82.690089,41.675105],[-83.02981,41.832796],[-83.142,41.975681],[-83.12,42.08],[-82.9,42.43],[-82.43,42.98],[-82.137642,43.571088],[-82.337763,44.44],[-82.550925,45.347517],[-83.592851,45.816894],[-83.469551,45.994686],[-83.616131,46.116927],[-83.890765,46.116927],[-84.091851,46.275419],[-84.14212,46.512226],[-84.3367,46.40877],[-84.6049,46.4396],[-84.543749,46.538684],[-84.779238,46.637102],[-84.87608,46.900083],[-85.652363,47.220219],[-86.461991,47.553338],[-87.439793,47.94],[-88.378114,48.302918],[-89.272917,48.019808],[-89.6,48.01],[-90.83,48.27],[-91.64,48.14],[-92.61,48.45],[-93.63087,48.60926],[-94.32914,48.67074],[-94.64,48.84],[-94.81758,49.38905],[-95.15609,49.38425],[-95.15907,49],[-97.22872,49.0007],[-100.65,49],[-104.04826,48.99986],[-107.05,49],[-110.05,49],[-113,49],[-116.04818,49],[-117.03121,49],[-120,49],[-122.84,49],[-122.97421,49.002538],[-124.91024,49.98456],[-125.62461,50.41656],[-127.43561,50.83061],[-127.99276,51.71583],[-127.85032,52.32961],[-129.12979,52.75538],[-129.30523,53.56159],[-130.51497,54.28757],[-130.53611,54.80278],[-129.98,55.285],[-130.00778,55.91583],[-131.70781,56.55212],[-132.73042,57.69289],[-133.35556,58.41028],[-134.27111,58.86111],[-134.945,59.27056],[-135.47583,59.78778],[-136.47972,59.46389],[-137.4525,58.905],[-138.34089,59.56211],[-139.039,60],[-140.013,60.27682],[-140.99778,60.30639],[-140.9925,66.00003],[-140.986,69.712],[-139.12052,69.47102],[-137.54636,68.99002],[-136.50358,68.89804],[-135.62576,69.31512],[-134.41464,69.62743],[-132.92925,69.50534],[-131.43136,69.94451],[-129.79471,70.19369],[-129.10773,69.77927],[-128.36156,70.01286],[-128.13817,70.48384],[-127.44712,70.37721],[-125.75632,69.48058],[-124.42483,70.1584],[-124.28968,69.39969],[-123.06108,69.56372],[-122.6835,69.85553],[-121.47226,69.79778],[-119.94288,69.37786],[-117.60268,69.01128],[-116.22643,68.84151],[-115.2469,68.90591],[-113.89794,68.3989],[-115.30489,67.90261],[-113.49727,67.68815],[-110.798,67.80612],[-109.94619,67.98104],[-108.8802,67.38144],[-107.79239,67.88736],[-108.81299,68.31164],[-108.16721,68.65392],[-106.95,68.7],[-106.15,68.8],[-105.34282,68.56122],[-104.33791,68.018],[-103.22115,68.09775],[-101.45433,67.64689],[-99.90195,67.80566],[-98.4432,67.78165],[-98.5586,68.40394],[-97.66948,68.57864],[-96.11991,68.23939],[-96.12588,67.29338],[-95.48943,68.0907],[-94.685,68.06383],[-94.23282,69.06903],[-95.30408,69.68571],[-96.47131,70.08976],[-96.39115,71.19482],[-95.2088,71.92053],[-93.88997,71.76015],[-92.87818,71.31869],[-91.51964,70.19129],[-92.40692,69.69997],[-90.5471,69.49766]]],[[[-114.16717,73.12145],[-114.66634,72.65277],[-112.44102,72.9554],[-111.05039,72.4504],[-109.92035,72.96113],[-109.00654,72.63335],[-108.18835,71.65089],[-107.68599,72.06548],[-108.39639,73.08953],[-107.51645,73.23598],[-106.52259,73.07601],[-105.40246,72.67259],[-104.77484,71.6984],[-104.46476,70.99297],[-102.78537,70.49776],[-100.98078,70.02432],[-101.08929,69.58447],[-102.73116,69.50402],[-102.09329,69.11962],[-102.43024,68.75282],[-104.24,68.91],[-105.96,69.18],[-107.12254,69.11922],[-109,68.78],[-111.534149,68.630059],[-113.3132,68.53554],[-113.85496,69.00744],[-115.22,69.28],[-116.10794,69.16821],[-117.34,69.96],[-116.67473,70.06655],[-115.13112,70.2373],[-113.72141,70.19237],[-112.4161,70.36638],[-114.35,70.6],[-116.48684,70.52045],[-117.9048,70.54056],[-118.43238,70.9092],[-116.11311,71.30918],[-117.65568,71.2952],[-119.40199,71.55859],[-118.56267,72.30785],[-117.86642,72.70594],[-115.18909,73.31459],[-114.16717,73.12145]]],[[[-104.5,73.42],[-105.38,72.76],[-106.94,73.46],[-106.6,73.6],[-105.26,73.64],[-104.5,73.42]]],[[[-76.34,73.102685],[-76.251404,72.826385],[-77.314438,72.855545],[-78.39167,72.876656],[-79.486252,72.742203],[-79.775833,72.802902],[-80.876099,73.333183],[-80.833885,73.693184],[-80.353058,73.75972],[-78.064438,73.651932],[-76.34,73.102685]]],[[[-86.562179,73.157447],[-85.774371,72.534126],[-84.850112,73.340278],[-82.31559,73.750951],[-80.600088,72.716544],[-80.748942,72.061907],[-78.770639,72.352173],[-77.824624,72.749617],[-75.605845,72.243678],[-74.228616,71.767144],[-74.099141,71.33084],[-72.242226,71.556925],[-71.200015,70.920013],[-68.786054,70.525024],[-67.91497,70.121948],[-66.969033,69.186087],[-68.805123,68.720198],[-66.449866,68.067163],[-64.862314,67.847539],[-63.424934,66.928473],[-61.851981,66.862121],[-62.163177,66.160251],[-63.918444,64.998669],[-65.14886,65.426033],[-66.721219,66.388041],[-68.015016,66.262726],[-68.141287,65.689789],[-67.089646,65.108455],[-65.73208,64.648406],[-65.320168,64.382737],[-64.669406,63.392927],[-65.013804,62.674185],[-66.275045,62.945099],[-68.783186,63.74567],[-67.369681,62.883966],[-66.328297,62.280075],[-66.165568,61.930897],[-68.877367,62.330149],[-71.023437,62.910708],[-72.235379,63.397836],[-71.886278,63.679989],[-73.378306,64.193963],[-74.834419,64.679076],[-74.818503,64.389093],[-77.70998,64.229542],[-78.555949,64.572906],[-77.897281,65.309192],[-76.018274,65.326969],[-73.959795,65.454765],[-74.293883,65.811771],[-73.944912,66.310578],[-72.651167,67.284576],[-72.92606,67.726926],[-73.311618,68.069437],[-74.843307,68.554627],[-76.869101,68.894736],[-76.228649,69.147769],[-77.28737,69.76954],[-78.168634,69.826488],[-78.957242,70.16688],[-79.492455,69.871808],[-81.305471,69.743185],[-84.944706,69.966634],[-87.060003,70.260001],[-88.681713,70.410741],[-89.51342,70.762038],[-88.467721,71.218186],[-89.888151,71.222552],[-90.20516,72.235074],[-89.436577,73.129464],[-88.408242,73.537889],[-85.826151,73.803816],[-86.562179,73.157447]]],[[[-100.35642,73.84389],[-99.16387,73.63339],[-97.38,73.76],[-97.12,73.47],[-98.05359,72.99052],[-96.54,72.56],[-96.72,71.66],[-98.35966,71.27285],[-99.32286,71.35639],[-100.01482,71.73827],[-102.5,72.51],[-102.48,72.83],[-100.43836,72.70588],[-101.54,73.36],[-100.35642,73.84389]]],[[[-93.196296,72.771992],[-94.269047,72.024596],[-95.409856,72.061881],[-96.033745,72.940277],[-96.018268,73.43743],[-95.495793,73.862417],[-94.503658,74.134907],[-92.420012,74.100025],[-90.509793,73.856732],[-92.003965,72.966244],[-93.196296,72.771992]]],[[[-120.46,71.383602],[-123.09219,70.90164],[-123.62,71.34],[-125.928949,71.868688],[-125.5,72.292261],[-124.80729,73.02256],[-123.94,73.68],[-124.91775,74.29275],[-121.53788,74.44893],[-120.10978,74.24135],[-117.55564,74.18577],[-116.58442,73.89607],[-115.51081,73.47519],[-116.76794,73.22292],[-119.22,72.52],[-120.46,71.82],[-120.46,71.383602]]],[[[-93.612756,74.979997],[-94.156909,74.592347],[-95.608681,74.666864],[-96.820932,74.927623],[-96.288587,75.377828],[-94.85082,75.647218],[-93.977747,75.29649],[-93.612756,74.979997]]],[[[-98.5,76.72],[-97.735585,76.25656],[-97.704415,75.74344],[-98.16,75],[-99.80874,74.89744],[-100.88366,75.05736],[-100.86292,75.64075],[-102.50209,75.5638],[-102.56552,76.3366],[-101.48973,76.30537],[-99.98349,76.64634],[-98.57699,76.58859],[-98.5,76.72]]],[[[-108.21141,76.20168],[-107.81943,75.84552],[-106.92893,76.01282],[-105.881,75.9694],[-105.70498,75.47951],[-106.31347,75.00527],[-109.7,74.85],[-112.22307,74.41696],[-113.74381,74.39427],[-113.87135,74.72029],[-111.79421,75.1625],[-116.31221,75.04343],[-117.7104,75.2222],[-116.34602,76.19903],[-115.40487,76.47887],[-112.59056,76.14134],[-110.81422,75.54919],[-109.0671,75.47321],[-110.49726,76.42982],[-109.5811,76.79417],[-108.54859,76.67832],[-108.21141,76.20168]]],[[[-94.684086,77.097878],[-93.573921,76.776296],[-91.605023,76.778518],[-90.741846,76.449597],[-90.969661,76.074013],[-89.822238,75.847774],[-89.187083,75.610166],[-87.838276,75.566189],[-86.379192,75.482421],[-84.789625,75.699204],[-82.753445,75.784315],[-81.128531,75.713983],[-80.057511,75.336849],[-79.833933,74.923127],[-80.457771,74.657304],[-81.948843,74.442459],[-83.228894,74.564028],[-86.097452,74.410032],[-88.15035,74.392307],[-89.764722,74.515555],[-92.422441,74.837758],[-92.768285,75.38682],[-92.889906,75.882655],[-93.893824,76.319244],[-95.962457,76.441381],[-97.121379,76.751078],[-96.745123,77.161389],[-94.684086,77.097878]]],[[[-116.198587,77.645287],[-116.335813,76.876962],[-117.106051,76.530032],[-118.040412,76.481172],[-119.899318,76.053213],[-121.499995,75.900019],[-122.854924,76.116543],[-122.854925,76.116543],[-121.157535,76.864508],[-119.103939,77.51222],[-117.570131,77.498319],[-116.198587,77.645287]]],[[[-93.840003,77.519997],[-94.295608,77.491343],[-96.169654,77.555111],[-96.436304,77.834629],[-94.422577,77.820005],[-93.720656,77.634331],[-93.840003,77.519997]]],[[[-110.186938,77.697015],[-112.051191,77.409229],[-113.534279,77.732207],[-112.724587,78.05105],[-111.264443,78.152956],[-109.854452,77.996325],[-110.186938,77.697015]]],[[[-109.663146,78.601973],[-110.881314,78.40692],[-112.542091,78.407902],[-112.525891,78.550555],[-111.50001,78.849994],[-110.963661,78.804441],[-109.663146,78.601973]]],[[[-95.830295,78.056941],[-97.309843,77.850597],[-98.124289,78.082857],[-98.552868,78.458105],[-98.631984,78.87193],[-97.337231,78.831984],[-96.754399,78.765813],[-95.559278,78.418315],[-95.830295,78.056941]]],[[[-100.060192,78.324754],[-99.670939,77.907545],[-101.30394,78.018985],[-102.949809,78.343229],[-105.176133,78.380332],[-104.210429,78.67742],[-105.41958,78.918336],[-105.492289,79.301594],[-103.529282,79.165349],[-100.825158,78.800462],[-100.060192,78.324754]]],[[[-87.02,79.66],[-85.81435,79.3369],[-87.18756,79.0393],[-89.03535,78.28723],[-90.80436,78.21533],[-92.87669,78.34333],[-93.95116,78.75099],[-93.93574,79.11373],[-93.14524,79.3801],[-94.974,79.37248],[-96.07614,79.70502],[-96.70972,80.15777],[-96.01644,80.60233],[-95.32345,80.90729],[-94.29843,80.97727],[-94.73542,81.20646],[-92.40984,81.25739],[-91.13289,80.72345],[-89.45,80.509322],[-87.81,80.32],[-87.02,79.66]]],[[[-68.5,83.106322],[-65.82735,83.02801],[-63.68,82.9],[-61.85,82.6286],[-61.89388,82.36165],[-64.334,81.92775],[-66.75342,81.72527],[-67.65755,81.50141],[-65.48031,81.50657],[-67.84,80.9],[-69.4697,80.61683],[-71.18,79.8],[-73.2428,79.63415],[-73.88,79.430162],[-76.90773,79.32309],[-75.52924,79.19766],[-76.22046,79.01907],[-75.39345,78.52581],[-76.34354,78.18296],[-77.88851,77.89991],[-78.36269,77.50859],[-79.75951,77.20968],[-79.61965,76.98336],[-77.91089,77.022045],[-77.88911,76.777955],[-80.56125,76.17812],[-83.17439,76.45403],[-86.11184,76.29901],[-87.6,76.42],[-89.49068,76.47239],[-89.6161,76.95213],[-87.76739,77.17833],[-88.26,77.9],[-87.65,77.970222],[-84.97634,77.53873],[-86.34,78.18],[-87.96192,78.37181],[-87.15198,78.75867],[-85.37868,78.9969],[-85.09495,79.34543],[-86.50734,79.73624],[-86.93179,80.25145],[-84.19844,80.20836],[-83.408696,80.1],[-81.84823,80.46442],[-84.1,80.58],[-87.59895,80.51627],[-89.36663,80.85569],[-90.2,81.26],[-91.36786,81.5531],[-91.58702,81.89429],[-90.1,82.085],[-88.93227,82.11751],[-86.97024,82.27961],[-85.5,82.652273],[-84.260005,82.6],[-83.18,82.32],[-82.42,82.86],[-81.1,83.02],[-79.30664,83.13056],[-76.25,83.172059],[-75.71878,83.06404],[-72.83153,83.23324],[-70.665765,83.169781],[-68.5,83.106322]]]]}}, +{"type":"Feature","id":"CHE","properties":{"name":"Switzerland"},"geometry":{"type":"Polygon","coordinates":[[[9.594226,47.525058],[9.632932,47.347601],[9.47997,47.10281],[9.932448,46.920728],[10.442701,46.893546],[10.363378,46.483571],[9.922837,46.314899],[9.182882,46.440215],[8.966306,46.036932],[8.489952,46.005151],[8.31663,46.163642],[7.755992,45.82449],[7.273851,45.776948],[6.843593,45.991147],[6.5001,46.429673],[6.022609,46.27299],[6.037389,46.725779],[6.768714,47.287708],[6.736571,47.541801],[7.192202,47.449766],[7.466759,47.620582],[8.317301,47.61358],[8.522612,47.830828],[9.594226,47.525058]]]}}, +{"type":"Feature","id":"CHL","properties":{"name":"Chile"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-68.63401,-52.63637],[-68.63335,-54.8695],[-67.56244,-54.87001],[-66.95992,-54.89681],[-67.29103,-55.30124],[-68.14863,-55.61183],[-68.639991,-55.580018],[-69.2321,-55.49906],[-69.95809,-55.19843],[-71.00568,-55.05383],[-72.2639,-54.49514],[-73.2852,-53.95752],[-74.66253,-52.83749],[-73.8381,-53.04743],[-72.43418,-53.7154],[-71.10773,-54.07433],[-70.59178,-53.61583],[-70.26748,-52.93123],[-69.34565,-52.5183],[-68.63401,-52.63637]]],[[[-68.219913,-21.494347],[-67.82818,-22.872919],[-67.106674,-22.735925],[-66.985234,-22.986349],[-67.328443,-24.025303],[-68.417653,-24.518555],[-68.386001,-26.185016],[-68.5948,-26.506909],[-68.295542,-26.89934],[-69.001235,-27.521214],[-69.65613,-28.459141],[-70.01355,-29.367923],[-69.919008,-30.336339],[-70.535069,-31.36501],[-70.074399,-33.09121],[-69.814777,-33.273886],[-69.817309,-34.193571],[-70.388049,-35.169688],[-70.364769,-36.005089],[-71.121881,-36.658124],[-71.118625,-37.576827],[-70.814664,-38.552995],[-71.413517,-38.916022],[-71.680761,-39.808164],[-71.915734,-40.832339],[-71.746804,-42.051386],[-72.148898,-42.254888],[-71.915424,-43.408565],[-71.464056,-43.787611],[-71.793623,-44.207172],[-71.329801,-44.407522],[-71.222779,-44.784243],[-71.659316,-44.973689],[-71.552009,-45.560733],[-71.917258,-46.884838],[-72.447355,-47.738533],[-72.331161,-48.244238],[-72.648247,-48.878618],[-73.415436,-49.318436],[-73.328051,-50.378785],[-72.975747,-50.74145],[-72.309974,-50.67701],[-72.329404,-51.425956],[-71.914804,-52.009022],[-69.498362,-52.142761],[-68.571545,-52.299444],[-69.461284,-52.291951],[-69.94278,-52.537931],[-70.845102,-52.899201],[-71.006332,-53.833252],[-71.429795,-53.856455],[-72.557943,-53.53141],[-73.702757,-52.835069],[-73.702757,-52.83507],[-74.946763,-52.262754],[-75.260026,-51.629355],[-74.976632,-51.043396],[-75.479754,-50.378372],[-75.608015,-48.673773],[-75.18277,-47.711919],[-74.126581,-46.939253],[-75.644395,-46.647643],[-74.692154,-45.763976],[-74.351709,-44.103044],[-73.240356,-44.454961],[-72.717804,-42.383356],[-73.3889,-42.117532],[-73.701336,-43.365776],[-74.331943,-43.224958],[-74.017957,-41.794813],[-73.677099,-39.942213],[-73.217593,-39.258689],[-73.505559,-38.282883],[-73.588061,-37.156285],[-73.166717,-37.12378],[-72.553137,-35.50884],[-71.861732,-33.909093],[-71.43845,-32.418899],[-71.668721,-30.920645],[-71.370083,-30.095682],[-71.489894,-28.861442],[-70.905124,-27.64038],[-70.724954,-25.705924],[-70.403966,-23.628997],[-70.091246,-21.393319],[-70.16442,-19.756468],[-70.372572,-18.347975],[-69.858444,-18.092694],[-69.590424,-17.580012],[-69.100247,-18.260125],[-68.966818,-18.981683],[-68.442225,-19.405068],[-68.757167,-20.372658],[-68.219913,-21.494347]]]]}}, +{"type":"Feature","id":"CHN","properties":{"name":"China"},"geometry":{"type":"MultiPolygon","coordinates":[[[[110.339188,18.678395],[109.47521,18.197701],[108.655208,18.507682],[108.626217,19.367888],[109.119056,19.821039],[110.211599,20.101254],[110.786551,20.077534],[111.010051,19.69593],[110.570647,19.255879],[110.339188,18.678395]]],[[[127.657407,49.76027],[129.397818,49.4406],[130.582293,48.729687],[130.987282,47.790132],[132.506672,47.78897],[133.373596,48.183442],[135.026311,48.47823],[134.500814,47.57844],[134.112362,47.212467],[133.769644,46.116927],[133.097127,45.144066],[131.883454,45.321162],[131.025212,44.967953],[131.288555,44.11152],[131.144688,42.92999],[130.633866,42.903015],[130.640016,42.395009],[129.994267,42.985387],[129.596669,42.424982],[128.052215,41.994285],[128.208433,41.466772],[127.343783,41.503152],[126.869083,41.816569],[126.182045,41.107336],[125.079942,40.569824],[124.265625,39.928493],[122.86757,39.637788],[122.131388,39.170452],[121.054554,38.897471],[121.585995,39.360854],[121.376757,39.750261],[122.168595,40.422443],[121.640359,40.94639],[120.768629,40.593388],[119.639602,39.898056],[119.023464,39.252333],[118.042749,39.204274],[117.532702,38.737636],[118.059699,38.061476],[118.87815,37.897325],[118.911636,37.448464],[119.702802,37.156389],[120.823457,37.870428],[121.711259,37.481123],[122.357937,37.454484],[122.519995,36.930614],[121.104164,36.651329],[120.637009,36.11144],[119.664562,35.609791],[119.151208,34.909859],[120.227525,34.360332],[120.620369,33.376723],[121.229014,32.460319],[121.908146,31.692174],[121.891919,30.949352],[121.264257,30.676267],[121.503519,30.142915],[122.092114,29.83252],[121.938428,29.018022],[121.684439,28.225513],[121.125661,28.135673],[120.395473,27.053207],[119.585497,25.740781],[118.656871,24.547391],[117.281606,23.624501],[115.890735,22.782873],[114.763827,22.668074],[114.152547,22.22376],[113.80678,22.54834],[113.241078,22.051367],[111.843592,21.550494],[110.785466,21.397144],[110.444039,20.341033],[109.889861,20.282457],[109.627655,21.008227],[109.864488,21.395051],[108.522813,21.715212],[108.05018,21.55238],[107.04342,21.811899],[106.567273,22.218205],[106.725403,22.794268],[105.811247,22.976892],[105.329209,23.352063],[104.476858,22.81915],[103.504515,22.703757],[102.706992,22.708795],[102.170436,22.464753],[101.652018,22.318199],[101.80312,21.174367],[101.270026,21.201652],[101.180005,21.436573],[101.150033,21.849984],[100.416538,21.558839],[99.983489,21.742937],[99.240899,22.118314],[99.531992,22.949039],[98.898749,23.142722],[98.660262,24.063286],[97.60472,23.897405],[97.724609,25.083637],[98.671838,25.918703],[98.712094,26.743536],[98.68269,27.508812],[98.246231,27.747221],[97.911988,28.335945],[97.327114,28.261583],[96.248833,28.411031],[96.586591,28.83098],[96.117679,29.452802],[95.404802,29.031717],[94.56599,29.277438],[93.413348,28.640629],[92.503119,27.896876],[91.696657,27.771742],[91.258854,28.040614],[90.730514,28.064954],[90.015829,28.296439],[89.47581,28.042759],[88.814248,27.299316],[88.730326,28.086865],[88.120441,27.876542],[86.954517,27.974262],[85.82332,28.203576],[85.011638,28.642774],[84.23458,28.839894],[83.898993,29.320226],[83.337115,29.463732],[82.327513,30.115268],[81.525804,30.422717],[81.111256,30.183481],[79.721367,30.882715],[78.738894,31.515906],[78.458446,32.618164],[79.176129,32.48378],[79.208892,32.994395],[78.811086,33.506198],[78.912269,34.321936],[77.837451,35.49401],[76.192848,35.898403],[75.896897,36.666806],[75.158028,37.133031],[74.980002,37.41999],[74.829986,37.990007],[74.864816,38.378846],[74.257514,38.606507],[73.928852,38.505815],[73.675379,39.431237],[73.960013,39.660008],[73.822244,39.893973],[74.776862,40.366425],[75.467828,40.562072],[76.526368,40.427946],[76.904484,41.066486],[78.187197,41.185316],[78.543661,41.582243],[80.11943,42.123941],[80.25999,42.349999],[80.18015,42.920068],[80.866206,43.180362],[79.966106,44.917517],[81.947071,45.317027],[82.458926,45.53965],[83.180484,47.330031],[85.16429,47.000956],[85.720484,47.452969],[85.768233,48.455751],[86.598776,48.549182],[87.35997,49.214981],[87.751264,49.297198],[88.013832,48.599463],[88.854298,48.069082],[90.280826,47.693549],[90.970809,46.888146],[90.585768,45.719716],[90.94554,45.286073],[92.133891,45.115076],[93.480734,44.975472],[94.688929,44.352332],[95.306875,44.241331],[95.762455,43.319449],[96.349396,42.725635],[97.451757,42.74889],[99.515817,42.524691],[100.845866,42.663804],[101.83304,42.514873],[103.312278,41.907468],[104.522282,41.908347],[104.964994,41.59741],[106.129316,42.134328],[107.744773,42.481516],[109.243596,42.519446],[110.412103,42.871234],[111.129682,43.406834],[111.829588,43.743118],[111.667737,44.073176],[111.348377,44.457442],[111.873306,45.102079],[112.436062,45.011646],[113.463907,44.808893],[114.460332,45.339817],[115.985096,45.727235],[116.717868,46.388202],[117.421701,46.672733],[118.874326,46.805412],[119.66327,46.69268],[119.772824,47.048059],[118.866574,47.74706],[118.064143,48.06673],[117.295507,47.697709],[116.308953,47.85341],[115.742837,47.726545],[115.485282,48.135383],[116.191802,49.134598],[116.678801,49.888531],[117.879244,49.510983],[119.288461,50.142883],[119.279366,50.582908],[120.18205,51.643566],[120.738191,51.964115],[120.725789,52.516226],[120.177089,52.753886],[121.003085,53.251401],[122.245748,53.431726],[123.571507,53.458804],[125.068211,53.161045],[125.946349,52.792799],[126.564399,51.784255],[126.939157,51.353894],[127.287456,50.739797],[127.657407,49.76027]]]]}}, +{"type":"Feature","id":"CIV","properties":{"name":"Ivory Coast"},"geometry":{"type":"Polygon","coordinates":[[[-2.856125,4.994476],[-3.311084,4.984296],[-4.00882,5.179813],[-4.649917,5.168264],[-5.834496,4.993701],[-6.528769,4.705088],[-7.518941,4.338288],[-7.712159,4.364566],[-7.635368,5.188159],[-7.539715,5.313345],[-7.570153,5.707352],[-7.993693,6.12619],[-8.311348,6.193033],[-8.60288,6.467564],[-8.385452,6.911801],[-8.485446,7.395208],[-8.439298,7.686043],[-8.280703,7.68718],[-8.221792,8.123329],[-8.299049,8.316444],[-8.203499,8.455453],[-7.8321,8.575704],[-8.079114,9.376224],[-8.309616,9.789532],[-8.229337,10.12902],[-8.029944,10.206535],[-7.89959,10.297382],[-7.622759,10.147236],[-6.850507,10.138994],[-6.666461,10.430811],[-6.493965,10.411303],[-6.205223,10.524061],[-6.050452,10.096361],[-5.816926,10.222555],[-5.404342,10.370737],[-4.954653,10.152714],[-4.779884,9.821985],[-4.330247,9.610835],[-3.980449,9.862344],[-3.511899,9.900326],[-2.827496,9.642461],[-2.56219,8.219628],[-2.983585,7.379705],[-3.24437,6.250472],[-2.810701,5.389051],[-2.856125,4.994476]]]}}, +{"type":"Feature","id":"CMR","properties":{"name":"Cameroon"},"geometry":{"type":"Polygon","coordinates":[[[13.075822,2.267097],[12.951334,2.321616],[12.35938,2.192812],[11.751665,2.326758],[11.276449,2.261051],[9.649158,2.283866],[9.795196,3.073404],[9.404367,3.734527],[8.948116,3.904129],[8.744924,4.352215],[8.488816,4.495617],[8.500288,4.771983],[8.757533,5.479666],[9.233163,6.444491],[9.522706,6.453482],[10.118277,7.03877],[10.497375,7.055358],[11.058788,6.644427],[11.745774,6.981383],[11.839309,7.397042],[12.063946,7.799808],[12.218872,8.305824],[12.753672,8.717763],[12.955468,9.417772],[13.1676,9.640626],[13.308676,10.160362],[13.57295,10.798566],[14.415379,11.572369],[14.468192,11.904752],[14.577178,12.085361],[14.181336,12.483657],[14.213531,12.802035],[14.495787,12.859396],[14.893386,12.219048],[14.960152,11.555574],[14.923565,10.891325],[15.467873,9.982337],[14.909354,9.992129],[14.627201,9.920919],[14.171466,10.021378],[13.954218,9.549495],[14.544467,8.965861],[14.979996,8.796104],[15.120866,8.38215],[15.436092,7.692812],[15.27946,7.421925],[14.776545,6.408498],[14.53656,6.226959],[14.459407,5.451761],[14.558936,5.030598],[14.478372,4.732605],[14.950953,4.210389],[15.03622,3.851367],[15.405396,3.335301],[15.862732,3.013537],[15.907381,2.557389],[16.012852,2.26764],[15.940919,1.727673],[15.146342,1.964015],[14.337813,2.227875],[13.075822,2.267097]]]}}, +{"type":"Feature","id":"COD","properties":{"name":"Democratic Republic of the Congo"},"geometry":{"type":"Polygon","coordinates":[[[30.83386,3.509166],[30.773347,2.339883],[31.174149,2.204465],[30.85267,1.849396],[30.468508,1.583805],[30.086154,1.062313],[29.875779,0.59738],[29.819503,-0.20531],[29.587838,-0.587406],[29.579466,-1.341313],[29.291887,-1.620056],[29.254835,-2.21511],[29.117479,-2.292211],[29.024926,-2.839258],[29.276384,-3.293907],[29.339998,-4.499983],[29.519987,-5.419979],[29.419993,-5.939999],[29.620032,-6.520015],[30.199997,-7.079981],[30.740015,-8.340007],[30.346086,-8.238257],[29.002912,-8.407032],[28.734867,-8.526559],[28.449871,-9.164918],[28.673682,-9.605925],[28.49607,-10.789884],[28.372253,-11.793647],[28.642417,-11.971569],[29.341548,-12.360744],[29.616001,-12.178895],[29.699614,-13.257227],[28.934286,-13.248958],[28.523562,-12.698604],[28.155109,-12.272481],[27.388799,-12.132747],[27.16442,-11.608748],[26.553088,-11.92444],[25.75231,-11.784965],[25.418118,-11.330936],[24.78317,-11.238694],[24.314516,-11.262826],[24.257155,-10.951993],[23.912215,-10.926826],[23.456791,-10.867863],[22.837345,-11.017622],[22.402798,-10.993075],[22.155268,-11.084801],[22.208753,-9.894796],[21.875182,-9.523708],[21.801801,-8.908707],[21.949131,-8.305901],[21.746456,-7.920085],[21.728111,-7.290872],[20.514748,-7.299606],[20.601823,-6.939318],[20.091622,-6.94309],[20.037723,-7.116361],[19.417502,-7.155429],[19.166613,-7.738184],[19.016752,-7.988246],[18.464176,-7.847014],[18.134222,-7.987678],[17.47297,-8.068551],[17.089996,-7.545689],[16.860191,-7.222298],[16.57318,-6.622645],[16.326528,-5.87747],[13.375597,-5.864241],[13.024869,-5.984389],[12.735171,-5.965682],[12.322432,-6.100092],[12.182337,-5.789931],[12.436688,-5.684304],[12.468004,-5.248362],[12.631612,-4.991271],[12.995517,-4.781103],[13.25824,-4.882957],[13.600235,-4.500138],[14.144956,-4.510009],[14.209035,-4.793092],[14.582604,-4.970239],[15.170992,-4.343507],[15.75354,-3.855165],[16.00629,-3.535133],[15.972803,-2.712392],[16.407092,-1.740927],[16.865307,-1.225816],[17.523716,-0.74383],[17.638645,-0.424832],[17.663553,-0.058084],[17.82654,0.288923],[17.774192,0.855659],[17.898835,1.741832],[18.094276,2.365722],[18.393792,2.900443],[18.453065,3.504386],[18.542982,4.201785],[18.932312,4.709506],[19.467784,5.031528],[20.290679,4.691678],[20.927591,4.322786],[21.659123,4.224342],[22.405124,4.02916],[22.704124,4.633051],[22.84148,4.710126],[23.297214,4.609693],[24.410531,5.108784],[24.805029,4.897247],[25.128833,4.927245],[25.278798,5.170408],[25.650455,5.256088],[26.402761,5.150875],[27.044065,5.127853],[27.374226,5.233944],[27.979977,4.408413],[28.428994,4.287155],[28.696678,4.455077],[29.159078,4.389267],[29.715995,4.600805],[29.9535,4.173699],[30.83386,3.509166]]]}}, +{"type":"Feature","id":"COG","properties":{"name":"Republic of the Congo"},"geometry":{"type":"Polygon","coordinates":[[[12.995517,-4.781103],[12.62076,-4.438023],[12.318608,-4.60623],[11.914963,-5.037987],[11.093773,-3.978827],[11.855122,-3.426871],[11.478039,-2.765619],[11.820964,-2.514161],[12.495703,-2.391688],[12.575284,-1.948511],[13.109619,-2.42874],[13.992407,-2.470805],[14.29921,-1.998276],[14.425456,-1.333407],[14.316418,-0.552627],[13.843321,0.038758],[14.276266,1.19693],[14.026669,1.395677],[13.282631,1.314184],[13.003114,1.830896],[13.075822,2.267097],[14.337813,2.227875],[15.146342,1.964015],[15.940919,1.727673],[16.012852,2.26764],[16.537058,3.198255],[17.133042,3.728197],[17.8099,3.560196],[18.453065,3.504386],[18.393792,2.900443],[18.094276,2.365722],[17.898835,1.741832],[17.774192,0.855659],[17.82654,0.288923],[17.663553,-0.058084],[17.638645,-0.424832],[17.523716,-0.74383],[16.865307,-1.225816],[16.407092,-1.740927],[15.972803,-2.712392],[16.00629,-3.535133],[15.75354,-3.855165],[15.170992,-4.343507],[14.582604,-4.970239],[14.209035,-4.793092],[14.144956,-4.510009],[13.600235,-4.500138],[13.25824,-4.882957],[12.995517,-4.781103]]]}}, +{"type":"Feature","id":"COL","properties":{"name":"Colombia"},"geometry":{"type":"Polygon","coordinates":[[[-75.373223,-0.152032],[-75.801466,0.084801],[-76.292314,0.416047],[-76.57638,0.256936],[-77.424984,0.395687],[-77.668613,0.825893],[-77.855061,0.809925],[-78.855259,1.380924],[-78.990935,1.69137],[-78.617831,1.766404],[-78.662118,2.267355],[-78.42761,2.629556],[-77.931543,2.696606],[-77.510431,3.325017],[-77.12769,3.849636],[-77.496272,4.087606],[-77.307601,4.667984],[-77.533221,5.582812],[-77.318815,5.845354],[-77.476661,6.691116],[-77.881571,7.223771],[-77.753414,7.70984],[-77.431108,7.638061],[-77.242566,7.935278],[-77.474723,8.524286],[-77.353361,8.670505],[-76.836674,8.638749],[-76.086384,9.336821],[-75.6746,9.443248],[-75.664704,9.774003],[-75.480426,10.61899],[-74.906895,11.083045],[-74.276753,11.102036],[-74.197223,11.310473],[-73.414764,11.227015],[-72.627835,11.731972],[-72.238195,11.95555],[-71.75409,12.437303],[-71.399822,12.376041],[-71.137461,12.112982],[-71.331584,11.776284],[-71.973922,11.608672],[-72.227575,11.108702],[-72.614658,10.821975],[-72.905286,10.450344],[-73.027604,9.73677],[-73.304952,9.152],[-72.78873,9.085027],[-72.660495,8.625288],[-72.439862,8.405275],[-72.360901,8.002638],[-72.479679,7.632506],[-72.444487,7.423785],[-72.198352,7.340431],[-71.960176,6.991615],[-70.674234,7.087785],[-70.093313,6.960376],[-69.38948,6.099861],[-68.985319,6.206805],[-68.265052,6.153268],[-67.695087,6.267318],[-67.34144,6.095468],[-67.521532,5.55687],[-67.744697,5.221129],[-67.823012,4.503937],[-67.621836,3.839482],[-67.337564,3.542342],[-67.303173,3.318454],[-67.809938,2.820655],[-67.447092,2.600281],[-67.181294,2.250638],[-66.876326,1.253361],[-67.065048,1.130112],[-67.259998,1.719999],[-67.53781,2.037163],[-67.868565,1.692455],[-69.816973,1.714805],[-69.804597,1.089081],[-69.218638,0.985677],[-69.252434,0.602651],[-69.452396,0.706159],[-70.015566,0.541414],[-70.020656,-0.185156],[-69.577065,-0.549992],[-69.420486,-1.122619],[-69.444102,-1.556287],[-69.893635,-4.298187],[-70.394044,-3.766591],[-70.692682,-3.742872],[-70.047709,-2.725156],[-70.813476,-2.256865],[-71.413646,-2.342802],[-71.774761,-2.16979],[-72.325787,-2.434218],[-73.070392,-2.308954],[-73.659504,-1.260491],[-74.122395,-1.002833],[-74.441601,-0.53082],[-75.106625,-0.057205],[-75.373223,-0.152032]]]}}, +{"type":"Feature","id":"CRI","properties":{"name":"Costa Rica"},"geometry":{"type":"Polygon","coordinates":[[[-82.965783,8.225028],[-83.508437,8.446927],[-83.711474,8.656836],[-83.596313,8.830443],[-83.632642,9.051386],[-83.909886,9.290803],[-84.303402,9.487354],[-84.647644,9.615537],[-84.713351,9.908052],[-84.97566,10.086723],[-84.911375,9.795992],[-85.110923,9.55704],[-85.339488,9.834542],[-85.660787,9.933347],[-85.797445,10.134886],[-85.791709,10.439337],[-85.659314,10.754331],[-85.941725,10.895278],[-85.71254,11.088445],[-85.561852,11.217119],[-84.903003,10.952303],[-84.673069,11.082657],[-84.355931,10.999226],[-84.190179,10.79345],[-83.895054,10.726839],[-83.655612,10.938764],[-83.40232,10.395438],[-83.015677,9.992982],[-82.546196,9.566135],[-82.932891,9.476812],[-82.927155,9.07433],[-82.719183,8.925709],[-82.868657,8.807266],[-82.829771,8.626295],[-82.913176,8.423517],[-82.965783,8.225028]]]}}, +{"type":"Feature","id":"CUB","properties":{"name":"Cuba"},"geometry":{"type":"Polygon","coordinates":[[[-82.268151,23.188611],[-81.404457,23.117271],[-80.618769,23.10598],[-79.679524,22.765303],[-79.281486,22.399202],[-78.347434,22.512166],[-77.993296,22.277194],[-77.146422,21.657851],[-76.523825,21.20682],[-76.19462,21.220565],[-75.598222,21.016624],[-75.67106,20.735091],[-74.933896,20.693905],[-74.178025,20.284628],[-74.296648,20.050379],[-74.961595,19.923435],[-75.63468,19.873774],[-76.323656,19.952891],[-77.755481,19.855481],[-77.085108,20.413354],[-77.492655,20.673105],[-78.137292,20.739949],[-78.482827,21.028613],[-78.719867,21.598114],[-79.285,21.559175],[-80.217475,21.827324],[-80.517535,22.037079],[-81.820943,22.192057],[-82.169992,22.387109],[-81.795002,22.636965],[-82.775898,22.68815],[-83.494459,22.168518],[-83.9088,22.154565],[-84.052151,21.910575],[-84.54703,21.801228],[-84.974911,21.896028],[-84.447062,22.20495],[-84.230357,22.565755],[-83.77824,22.788118],[-83.267548,22.983042],[-82.510436,23.078747],[-82.268151,23.188611]]]}}, +{"type":"Feature","id":"-99","properties":{"name":"Northern Cyprus"},"geometry":{"type":"Polygon","coordinates":[[[32.73178,35.140026],[32.802474,35.145504],[32.946961,35.386703],[33.667227,35.373216],[34.576474,35.671596],[33.900804,35.245756],[33.973617,35.058506],[33.86644,35.093595],[33.675392,35.017863],[33.525685,35.038688],[33.475817,35.000345],[33.455922,35.101424],[33.383833,35.162712],[33.190977,35.173125],[32.919572,35.087833],[32.73178,35.140026]]]}}, +{"type":"Feature","id":"CYP","properties":{"name":"Cyprus"},"geometry":{"type":"Polygon","coordinates":[[[33.973617,35.058506],[34.004881,34.978098],[32.979827,34.571869],[32.490296,34.701655],[32.256667,35.103232],[32.73178,35.140026],[32.919572,35.087833],[33.190977,35.173125],[33.383833,35.162712],[33.455922,35.101424],[33.475817,35.000345],[33.525685,35.038688],[33.675392,35.017863],[33.86644,35.093595],[33.973617,35.058506]]]}}, +{"type":"Feature","id":"CZE","properties":{"name":"Czech Republic"},"geometry":{"type":"Polygon","coordinates":[[[16.960288,48.596982],[16.499283,48.785808],[16.029647,48.733899],[15.253416,49.039074],[14.901447,48.964402],[14.338898,48.555305],[13.595946,48.877172],[13.031329,49.307068],[12.521024,49.547415],[12.415191,49.969121],[12.240111,50.266338],[12.966837,50.484076],[13.338132,50.733234],[14.056228,50.926918],[14.307013,51.117268],[14.570718,51.002339],[15.016996,51.106674],[15.490972,50.78473],[16.238627,50.697733],[16.176253,50.422607],[16.719476,50.215747],[16.868769,50.473974],[17.554567,50.362146],[17.649445,50.049038],[18.392914,49.988629],[18.853144,49.49623],[18.554971,49.495015],[18.399994,49.315001],[18.170498,49.271515],[18.104973,49.043983],[17.913512,48.996493],[17.886485,48.903475],[17.545007,48.800019],[17.101985,48.816969],[16.960288,48.596982]]]}}, +{"type":"Feature","id":"DEU","properties":{"name":"Germany"},"geometry":{"type":"Polygon","coordinates":[[[9.921906,54.983104],[9.93958,54.596642],[10.950112,54.363607],[10.939467,54.008693],[11.956252,54.196486],[12.51844,54.470371],[13.647467,54.075511],[14.119686,53.757029],[14.353315,53.248171],[14.074521,52.981263],[14.4376,52.62485],[14.685026,52.089947],[14.607098,51.745188],[15.016996,51.106674],[14.570718,51.002339],[14.307013,51.117268],[14.056228,50.926918],[13.338132,50.733234],[12.966837,50.484076],[12.240111,50.266338],[12.415191,49.969121],[12.521024,49.547415],[13.031329,49.307068],[13.595946,48.877172],[13.243357,48.416115],[12.884103,48.289146],[13.025851,47.637584],[12.932627,47.467646],[12.62076,47.672388],[12.141357,47.703083],[11.426414,47.523766],[10.544504,47.566399],[10.402084,47.302488],[9.896068,47.580197],[9.594226,47.525058],[8.522612,47.830828],[8.317301,47.61358],[7.466759,47.620582],[7.593676,48.333019],[8.099279,49.017784],[6.65823,49.201958],[6.18632,49.463803],[6.242751,49.902226],[6.043073,50.128052],[6.156658,50.803721],[5.988658,51.851616],[6.589397,51.852029],[6.84287,52.22844],[7.092053,53.144043],[6.90514,53.482162],[7.100425,53.693932],[7.936239,53.748296],[8.121706,53.527792],[8.800734,54.020786],[8.572118,54.395646],[8.526229,54.962744],[9.282049,54.830865],[9.921906,54.983104]]]}}, +{"type":"Feature","id":"DJI","properties":{"name":"Djibouti"},"geometry":{"type":"Polygon","coordinates":[[[43.081226,12.699639],[43.317852,12.390148],[43.286381,11.974928],[42.715874,11.735641],[43.145305,11.46204],[42.776852,10.926879],[42.55493,11.10511],[42.31414,11.0342],[41.75557,11.05091],[41.73959,11.35511],[41.66176,11.6312],[42,12.1],[42.35156,12.54223],[42.779642,12.455416],[43.081226,12.699639]]]}}, +{"type":"Feature","id":"DNK","properties":{"name":"Denmark"},"geometry":{"type":"MultiPolygon","coordinates":[[[[12.690006,55.609991],[12.089991,54.800015],[11.043543,55.364864],[10.903914,55.779955],[12.370904,56.111407],[12.690006,55.609991]]],[[[10.912182,56.458621],[10.667804,56.081383],[10.369993,56.190007],[9.649985,55.469999],[9.921906,54.983104],[9.282049,54.830865],[8.526229,54.962744],[8.120311,55.517723],[8.089977,56.540012],[8.256582,56.809969],[8.543438,57.110003],[9.424469,57.172066],[9.775559,57.447941],[10.580006,57.730017],[10.546106,57.215733],[10.25,56.890016],[10.369993,56.609982],[10.912182,56.458621]]]]}}, +{"type":"Feature","id":"DOM","properties":{"name":"Dominican Republic"},"geometry":{"type":"Polygon","coordinates":[[[-71.712361,19.714456],[-71.587304,19.884911],[-70.806706,19.880286],[-70.214365,19.622885],[-69.950815,19.648],[-69.76925,19.293267],[-69.222126,19.313214],[-69.254346,19.015196],[-68.809412,18.979074],[-68.317943,18.612198],[-68.689316,18.205142],[-69.164946,18.422648],[-69.623988,18.380713],[-69.952934,18.428307],[-70.133233,18.245915],[-70.517137,18.184291],[-70.669298,18.426886],[-70.99995,18.283329],[-71.40021,17.598564],[-71.657662,17.757573],[-71.708305,18.044997],[-71.687738,18.31666],[-71.945112,18.6169],[-71.701303,18.785417],[-71.624873,19.169838],[-71.712361,19.714456]]]}}, +{"type":"Feature","id":"DZA","properties":{"name":"Algeria"},"geometry":{"type":"Polygon","coordinates":[[[11.999506,23.471668],[8.572893,21.565661],[5.677566,19.601207],[4.267419,19.155265],[3.158133,19.057364],[3.146661,19.693579],[2.683588,19.85623],[2.060991,20.142233],[1.823228,20.610809],[-1.550055,22.792666],[-4.923337,24.974574],[-8.6844,27.395744],[-8.665124,27.589479],[-8.66559,27.656426],[-8.674116,28.841289],[-7.059228,29.579228],[-6.060632,29.7317],[-5.242129,30.000443],[-4.859646,30.501188],[-3.690441,30.896952],[-3.647498,31.637294],[-3.06898,31.724498],[-2.616605,32.094346],[-1.307899,32.262889],[-1.124551,32.651522],[-1.388049,32.864015],[-1.733455,33.919713],[-1.792986,34.527919],[-2.169914,35.168396],[-1.208603,35.714849],[-0.127454,35.888662],[0.503877,36.301273],[1.466919,36.605647],[3.161699,36.783905],[4.815758,36.865037],[5.32012,36.716519],[6.26182,37.110655],[7.330385,37.118381],[7.737078,36.885708],[8.420964,36.946427],[8.217824,36.433177],[8.376368,35.479876],[8.140981,34.655146],[7.524482,34.097376],[7.612642,33.344115],[8.430473,32.748337],[8.439103,32.506285],[9.055603,32.102692],[9.48214,30.307556],[9.805634,29.424638],[9.859998,28.95999],[9.683885,28.144174],[9.756128,27.688259],[9.629056,27.140953],[9.716286,26.512206],[9.319411,26.094325],[9.910693,25.365455],[9.948261,24.936954],[10.303847,24.379313],[10.771364,24.562532],[11.560669,24.097909],[11.999506,23.471668]]]}}, +{"type":"Feature","id":"ECU","properties":{"name":"Ecuador"},"geometry":{"type":"Polygon","coordinates":[[[-80.302561,-3.404856],[-79.770293,-2.657512],[-79.986559,-2.220794],[-80.368784,-2.685159],[-80.967765,-2.246943],[-80.764806,-1.965048],[-80.933659,-1.057455],[-80.58337,-0.906663],[-80.399325,-0.283703],[-80.020898,0.36034],[-80.09061,0.768429],[-79.542762,0.982938],[-78.855259,1.380924],[-77.855061,0.809925],[-77.668613,0.825893],[-77.424984,0.395687],[-76.57638,0.256936],[-76.292314,0.416047],[-75.801466,0.084801],[-75.373223,-0.152032],[-75.233723,-0.911417],[-75.544996,-1.56161],[-76.635394,-2.608678],[-77.837905,-3.003021],[-78.450684,-3.873097],[-78.639897,-4.547784],[-79.205289,-4.959129],[-79.624979,-4.454198],[-80.028908,-4.346091],[-80.442242,-4.425724],[-80.469295,-4.059287],[-80.184015,-3.821162],[-80.302561,-3.404856]]]}}, +{"type":"Feature","id":"EGY","properties":{"name":"Egypt"},"geometry":{"type":"Polygon","coordinates":[[[34.9226,29.50133],[34.64174,29.09942],[34.42655,28.34399],[34.15451,27.8233],[33.92136,27.6487],[33.58811,27.97136],[33.13676,28.41765],[32.42323,29.85108],[32.32046,29.76043],[32.73482,28.70523],[33.34876,27.69989],[34.10455,26.14227],[34.47387,25.59856],[34.79507,25.03375],[35.69241,23.92671],[35.49372,23.75237],[35.52598,23.10244],[36.69069,22.20485],[36.86623,22],[32.9,22],[29.02,22],[25,22],[25,25.6825],[25,29.238655],[24.70007,30.04419],[24.95762,30.6616],[24.80287,31.08929],[25.16482,31.56915],[26.49533,31.58568],[27.45762,31.32126],[28.45048,31.02577],[28.91353,30.87005],[29.68342,31.18686],[30.09503,31.4734],[30.97693,31.55586],[31.68796,31.4296],[31.96041,30.9336],[32.19247,31.26034],[32.99392,31.02407],[33.7734,30.96746],[34.26544,31.21936],[34.9226,29.50133]]]}}, +{"type":"Feature","id":"ERI","properties":{"name":"Eritrea"},"geometry":{"type":"Polygon","coordinates":[[[42.35156,12.54223],[42.00975,12.86582],[41.59856,13.45209],[41.155194,13.77332],[40.8966,14.11864],[40.026219,14.519579],[39.34061,14.53155],[39.0994,14.74064],[38.51295,14.50547],[37.90607,14.95943],[37.59377,14.2131],[36.42951,14.42211],[36.323189,14.822481],[36.75386,16.291874],[36.85253,16.95655],[37.16747,17.26314],[37.904,17.42754],[38.41009,17.998307],[38.990623,16.840626],[39.26611,15.922723],[39.814294,15.435647],[41.179275,14.49108],[41.734952,13.921037],[42.276831,13.343992],[42.589576,13.000421],[43.081226,12.699639],[42.779642,12.455416],[42.35156,12.54223]]]}}, +{"type":"Feature","id":"ESP","properties":{"name":"Spain"},"geometry":{"type":"Polygon","coordinates":[[[-9.034818,41.880571],[-8.984433,42.592775],[-9.392884,43.026625],[-7.97819,43.748338],[-6.754492,43.567909],[-5.411886,43.57424],[-4.347843,43.403449],[-3.517532,43.455901],[-1.901351,43.422802],[-1.502771,43.034014],[0.338047,42.579546],[0.701591,42.795734],[1.826793,42.343385],[2.985999,42.473015],[3.039484,41.89212],[2.091842,41.226089],[0.810525,41.014732],[0.721331,40.678318],[0.106692,40.123934],[-0.278711,39.309978],[0.111291,38.738514],[-0.467124,38.292366],[-0.683389,37.642354],[-1.438382,37.443064],[-2.146453,36.674144],[-3.415781,36.6589],[-4.368901,36.677839],[-4.995219,36.324708],[-5.37716,35.94685],[-5.866432,36.029817],[-6.236694,36.367677],[-6.520191,36.942913],[-7.453726,37.097788],[-7.537105,37.428904],[-7.166508,37.803894],[-7.029281,38.075764],[-7.374092,38.373059],[-7.098037,39.030073],[-7.498632,39.629571],[-7.066592,39.711892],[-7.026413,40.184524],[-6.86402,40.330872],[-6.851127,41.111083],[-6.389088,41.381815],[-6.668606,41.883387],[-7.251309,41.918346],[-7.422513,41.792075],[-8.013175,41.790886],[-8.263857,42.280469],[-8.671946,42.134689],[-9.034818,41.880571]]]}}, +{"type":"Feature","id":"EST","properties":{"name":"Estonia"},"geometry":{"type":"Polygon","coordinates":[[[24.312863,57.793424],[24.428928,58.383413],[24.061198,58.257375],[23.42656,58.612753],[23.339795,59.18724],[24.604214,59.465854],[25.864189,59.61109],[26.949136,59.445803],[27.981114,59.475388],[28.131699,59.300825],[27.420166,58.724581],[27.716686,57.791899],[27.288185,57.474528],[26.463532,57.476389],[25.60281,57.847529],[25.164594,57.970157],[24.312863,57.793424]]]}}, +{"type":"Feature","id":"ETH","properties":{"name":"Ethiopia"},"geometry":{"type":"Polygon","coordinates":[[[37.90607,14.95943],[38.51295,14.50547],[39.0994,14.74064],[39.34061,14.53155],[40.02625,14.51959],[40.8966,14.11864],[41.1552,13.77333],[41.59856,13.45209],[42.00975,12.86582],[42.35156,12.54223],[42,12.1],[41.66176,11.6312],[41.73959,11.35511],[41.75557,11.05091],[42.31414,11.0342],[42.55493,11.10511],[42.776852,10.926879],[42.55876,10.57258],[42.92812,10.02194],[43.29699,9.54048],[43.67875,9.18358],[46.94834,7.99688],[47.78942,8.003],[44.9636,5.00162],[43.66087,4.95755],[42.76967,4.25259],[42.12861,4.23413],[41.855083,3.918912],[41.1718,3.91909],[40.76848,4.25702],[39.85494,3.83879],[39.559384,3.42206],[38.89251,3.50074],[38.67114,3.61607],[38.43697,3.58851],[38.120915,3.598605],[36.855093,4.447864],[36.159079,4.447864],[35.817448,4.776966],[35.817448,5.338232],[35.298007,5.506],[34.70702,6.59422],[34.25032,6.82607],[34.0751,7.22595],[33.56829,7.71334],[32.95418,7.78497],[33.2948,8.35458],[33.8255,8.37916],[33.97498,8.68456],[33.96162,9.58358],[34.25745,10.63009],[34.73115,10.91017],[34.83163,11.31896],[35.26049,12.08286],[35.86363,12.57828],[36.27022,13.56333],[36.42951,14.42211],[37.59377,14.2131],[37.90607,14.95943]]]}}, +{"type":"Feature","id":"FIN","properties":{"name":"Finland"},"geometry":{"type":"Polygon","coordinates":[[[28.59193,69.064777],[28.445944,68.364613],[29.977426,67.698297],[29.054589,66.944286],[30.21765,65.80598],[29.54443,64.948672],[30.444685,64.204453],[30.035872,63.552814],[31.516092,62.867687],[31.139991,62.357693],[30.211107,61.780028],[28.069998,60.503517],[26.255173,60.423961],[24.496624,60.057316],[22.869695,59.846373],[22.290764,60.391921],[21.322244,60.72017],[21.544866,61.705329],[21.059211,62.607393],[21.536029,63.189735],[22.442744,63.81781],[24.730512,64.902344],[25.398068,65.111427],[25.294043,65.534346],[23.903379,66.006927],[23.56588,66.396051],[23.539473,67.936009],[21.978535,68.616846],[20.645593,69.106247],[21.244936,69.370443],[22.356238,68.841741],[23.66205,68.891247],[24.735679,68.649557],[25.689213,69.092114],[26.179622,69.825299],[27.732292,70.164193],[29.015573,69.766491],[28.59193,69.064777]]]}}, +{"type":"Feature","id":"FJI","properties":{"name":"Fiji"},"geometry":{"type":"MultiPolygon","coordinates":[[[[178.3736,-17.33992],[178.71806,-17.62846],[178.55271,-18.15059],[177.93266,-18.28799],[177.38146,-18.16432],[177.28504,-17.72465],[177.67087,-17.38114],[178.12557,-17.50481],[178.3736,-17.33992]]],[[[179.364143,-16.801354],[178.725059,-17.012042],[178.596839,-16.63915],[179.096609,-16.433984],[179.413509,-16.379054],[180,-16.067133],[180,-16.555217],[179.364143,-16.801354]]],[[[-179.917369,-16.501783],[-180,-16.555217],[-180,-16.067133],[-179.79332,-16.020882],[-179.917369,-16.501783]]]]}}, +{"type":"Feature","id":"FLK","properties":{"name":"Falkland Islands"},"geometry":{"type":"Polygon","coordinates":[[[-61.2,-51.85],[-60,-51.25],[-59.15,-51.5],[-58.55,-51.1],[-57.75,-51.55],[-58.05,-51.9],[-59.4,-52.2],[-59.85,-51.85],[-60.7,-52.3],[-61.2,-51.85]]]}}, +{"type":"Feature","id":"FRA","properties":{"name":"France"},"geometry":{"type":"MultiPolygon","coordinates":[[[[9.560016,42.152492],[9.229752,41.380007],[8.775723,41.583612],[8.544213,42.256517],[8.746009,42.628122],[9.390001,43.009985],[9.560016,42.152492]]],[[[3.588184,50.378992],[4.286023,49.907497],[4.799222,49.985373],[5.674052,49.529484],[5.897759,49.442667],[6.18632,49.463803],[6.65823,49.201958],[8.099279,49.017784],[7.593676,48.333019],[7.466759,47.620582],[7.192202,47.449766],[6.736571,47.541801],[6.768714,47.287708],[6.037389,46.725779],[6.022609,46.27299],[6.5001,46.429673],[6.843593,45.991147],[6.802355,45.70858],[7.096652,45.333099],[6.749955,45.028518],[7.007562,44.254767],[7.549596,44.127901],[7.435185,43.693845],[6.529245,43.128892],[4.556963,43.399651],[3.100411,43.075201],[2.985999,42.473015],[1.826793,42.343385],[0.701591,42.795734],[0.338047,42.579546],[-1.502771,43.034014],[-1.901351,43.422802],[-1.384225,44.02261],[-1.193798,46.014918],[-2.225724,47.064363],[-2.963276,47.570327],[-4.491555,47.954954],[-4.59235,48.68416],[-3.295814,48.901692],[-1.616511,48.644421],[-1.933494,49.776342],[-0.989469,49.347376],[1.338761,50.127173],[1.639001,50.946606],[2.513573,51.148506],[2.658422,50.796848],[3.123252,50.780363],[3.588184,50.378992]]]]}}, +{"type":"Feature","id":"GAB","properties":{"name":"Gabon"},"geometry":{"type":"Polygon","coordinates":[[[11.093773,-3.978827],[10.066135,-2.969483],[9.405245,-2.144313],[8.797996,-1.111301],[8.830087,-0.779074],[9.04842,-0.459351],[9.291351,0.268666],[9.492889,1.01012],[9.830284,1.067894],[11.285079,1.057662],[11.276449,2.261051],[11.751665,2.326758],[12.35938,2.192812],[12.951334,2.321616],[13.075822,2.267097],[13.003114,1.830896],[13.282631,1.314184],[14.026669,1.395677],[14.276266,1.19693],[13.843321,0.038758],[14.316418,-0.552627],[14.425456,-1.333407],[14.29921,-1.998276],[13.992407,-2.470805],[13.109619,-2.42874],[12.575284,-1.948511],[12.495703,-2.391688],[11.820964,-2.514161],[11.478039,-2.765619],[11.855122,-3.426871],[11.093773,-3.978827]]]}}, +{"type":"Feature","id":"GBR","properties":{"name":"United Kingdom"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-5.661949,54.554603],[-6.197885,53.867565],[-6.95373,54.073702],[-7.572168,54.059956],[-7.366031,54.595841],[-7.572168,55.131622],[-6.733847,55.17286],[-5.661949,54.554603]]],[[[-3.005005,58.635],[-4.073828,57.553025],[-3.055002,57.690019],[-1.959281,57.6848],[-2.219988,56.870017],[-3.119003,55.973793],[-2.085009,55.909998],[-2.005676,55.804903],[-1.114991,54.624986],[-0.430485,54.464376],[0.184981,53.325014],[0.469977,52.929999],[1.681531,52.73952],[1.559988,52.099998],[1.050562,51.806761],[1.449865,51.289428],[0.550334,50.765739],[-0.787517,50.774989],[-2.489998,50.500019],[-2.956274,50.69688],[-3.617448,50.228356],[-4.542508,50.341837],[-5.245023,49.96],[-5.776567,50.159678],[-4.30999,51.210001],[-3.414851,51.426009],[-3.422719,51.426848],[-4.984367,51.593466],[-5.267296,51.9914],[-4.222347,52.301356],[-4.770013,52.840005],[-4.579999,53.495004],[-3.093831,53.404547],[-3.09208,53.404441],[-2.945009,53.985],[-3.614701,54.600937],[-3.630005,54.615013],[-4.844169,54.790971],[-5.082527,55.061601],[-4.719112,55.508473],[-5.047981,55.783986],[-5.586398,55.311146],[-5.644999,56.275015],[-6.149981,56.78501],[-5.786825,57.818848],[-5.009999,58.630013],[-4.211495,58.550845],[-3.005005,58.635]]]]}}, +{"type":"Feature","id":"GEO","properties":{"name":"Georgia"},"geometry":{"type":"Polygon","coordinates":[[[41.554084,41.535656],[41.703171,41.962943],[41.45347,42.645123],[40.875469,43.013628],[40.321394,43.128634],[39.955009,43.434998],[40.076965,43.553104],[40.922185,43.382159],[42.394395,43.220308],[43.756017,42.740828],[43.9312,42.554974],[44.537623,42.711993],[45.470279,42.502781],[45.77641,42.092444],[46.404951,41.860675],[46.145432,41.722802],[46.637908,41.181673],[46.501637,41.064445],[45.962601,41.123873],[45.217426,41.411452],[44.97248,41.248129],[43.582746,41.092143],[42.619549,41.583173],[41.554084,41.535656]]]}}, +{"type":"Feature","id":"GHA","properties":{"name":"Ghana"},"geometry":{"type":"Polygon","coordinates":[[[1.060122,5.928837],[-0.507638,5.343473],[-1.063625,5.000548],[-1.964707,4.710462],[-2.856125,4.994476],[-2.810701,5.389051],[-3.24437,6.250472],[-2.983585,7.379705],[-2.56219,8.219628],[-2.827496,9.642461],[-2.963896,10.395335],[-2.940409,10.96269],[-1.203358,11.009819],[-0.761576,10.93693],[-0.438702,11.098341],[0.023803,11.018682],[-0.049785,10.706918],[0.36758,10.191213],[0.365901,9.465004],[0.461192,8.677223],[0.712029,8.312465],[0.490957,7.411744],[0.570384,6.914359],[0.836931,6.279979],[1.060122,5.928837]]]}}, +{"type":"Feature","id":"GIN","properties":{"name":"Guinea"},"geometry":{"type":"Polygon","coordinates":[[[-8.439298,7.686043],[-8.722124,7.711674],[-8.926065,7.309037],[-9.208786,7.313921],[-9.403348,7.526905],[-9.33728,7.928534],[-9.755342,8.541055],[-10.016567,8.428504],[-10.230094,8.406206],[-10.505477,8.348896],[-10.494315,8.715541],[-10.65477,8.977178],[-10.622395,9.26791],[-10.839152,9.688246],[-11.117481,10.045873],[-11.917277,10.046984],[-12.150338,9.858572],[-12.425929,9.835834],[-12.596719,9.620188],[-12.711958,9.342712],[-13.24655,8.903049],[-13.685154,9.494744],[-14.074045,9.886167],[-14.330076,10.01572],[-14.579699,10.214467],[-14.693232,10.656301],[-14.839554,10.876572],[-15.130311,11.040412],[-14.685687,11.527824],[-14.382192,11.509272],[-14.121406,11.677117],[-13.9008,11.678719],[-13.743161,11.811269],[-13.828272,12.142644],[-13.718744,12.247186],[-13.700476,12.586183],[-13.217818,12.575874],[-12.499051,12.33209],[-12.278599,12.35444],[-12.203565,12.465648],[-11.658301,12.386583],[-11.513943,12.442988],[-11.456169,12.076834],[-11.297574,12.077971],[-11.036556,12.211245],[-10.87083,12.177887],[-10.593224,11.923975],[-10.165214,11.844084],[-9.890993,12.060479],[-9.567912,12.194243],[-9.327616,12.334286],[-9.127474,12.30806],[-8.905265,12.088358],[-8.786099,11.812561],[-8.376305,11.393646],[-8.581305,11.136246],[-8.620321,10.810891],[-8.407311,10.909257],[-8.282357,10.792597],[-8.335377,10.494812],[-8.029944,10.206535],[-8.229337,10.12902],[-8.309616,9.789532],[-8.079114,9.376224],[-7.8321,8.575704],[-8.203499,8.455453],[-8.299049,8.316444],[-8.221792,8.123329],[-8.280703,7.68718],[-8.439298,7.686043]]]}}, +{"type":"Feature","id":"GMB","properties":{"name":"Gambia"},"geometry":{"type":"Polygon","coordinates":[[[-16.841525,13.151394],[-16.713729,13.594959],[-15.624596,13.623587],[-15.39877,13.860369],[-15.081735,13.876492],[-14.687031,13.630357],[-14.376714,13.62568],[-14.046992,13.794068],[-13.844963,13.505042],[-14.277702,13.280585],[-14.712197,13.298207],[-15.141163,13.509512],[-15.511813,13.27857],[-15.691001,13.270353],[-15.931296,13.130284],[-16.841525,13.151394]]]}}, +{"type":"Feature","id":"GNB","properties":{"name":"Guinea Bissau"},"geometry":{"type":"Polygon","coordinates":[[[-15.130311,11.040412],[-15.66418,11.458474],[-16.085214,11.524594],[-16.314787,11.806515],[-16.308947,11.958702],[-16.613838,12.170911],[-16.677452,12.384852],[-16.147717,12.547762],[-15.816574,12.515567],[-15.548477,12.62817],[-13.700476,12.586183],[-13.718744,12.247186],[-13.828272,12.142644],[-13.743161,11.811269],[-13.9008,11.678719],[-14.121406,11.677117],[-14.382192,11.509272],[-14.685687,11.527824],[-15.130311,11.040412]]]}}, +{"type":"Feature","id":"GNQ","properties":{"name":"Equatorial Guinea"},"geometry":{"type":"Polygon","coordinates":[[[9.492889,1.01012],[9.305613,1.160911],[9.649158,2.283866],[11.276449,2.261051],[11.285079,1.057662],[9.830284,1.067894],[9.492889,1.01012]]]}}, +{"type":"Feature","id":"GRC","properties":{"name":"Greece"},"geometry":{"type":"MultiPolygon","coordinates":[[[[23.69998,35.705004],[24.246665,35.368022],[25.025015,35.424996],[25.769208,35.354018],[25.745023,35.179998],[26.290003,35.29999],[26.164998,35.004995],[24.724982,34.919988],[24.735007,35.084991],[23.514978,35.279992],[23.69998,35.705004]]],[[[26.604196,41.562115],[26.294602,40.936261],[26.056942,40.824123],[25.447677,40.852545],[24.925848,40.947062],[23.714811,40.687129],[24.407999,40.124993],[23.899968,39.962006],[23.342999,39.960998],[22.813988,40.476005],[22.626299,40.256561],[22.849748,39.659311],[23.350027,39.190011],[22.973099,38.970903],[23.530016,38.510001],[24.025025,38.219993],[24.040011,37.655015],[23.115003,37.920011],[23.409972,37.409991],[22.774972,37.30501],[23.154225,36.422506],[22.490028,36.41],[21.670026,36.844986],[21.295011,37.644989],[21.120034,38.310323],[20.730032,38.769985],[20.217712,39.340235],[20.150016,39.624998],[20.615,40.110007],[20.674997,40.435],[20.99999,40.580004],[21.02004,40.842727],[21.674161,40.931275],[22.055378,41.149866],[22.597308,41.130487],[22.76177,41.3048],[22.952377,41.337994],[23.692074,41.309081],[24.492645,41.583896],[25.197201,41.234486],[26.106138,41.328899],[26.117042,41.826905],[26.604196,41.562115]]]]}}, +{"type":"Feature","id":"GRL","properties":{"name":"Greenland"},"geometry":{"type":"Polygon","coordinates":[[[-46.76379,82.62796],[-43.40644,83.22516],[-39.89753,83.18018],[-38.62214,83.54905],[-35.08787,83.64513],[-27.10046,83.51966],[-20.84539,82.72669],[-22.69182,82.34165],[-26.51753,82.29765],[-31.9,82.2],[-31.39646,82.02154],[-27.85666,82.13178],[-24.84448,81.78697],[-22.90328,82.09317],[-22.07175,81.73449],[-23.16961,81.15271],[-20.62363,81.52462],[-15.76818,81.91245],[-12.77018,81.71885],[-12.20855,81.29154],[-16.28533,80.58004],[-16.85,80.35],[-20.04624,80.17708],[-17.73035,80.12912],[-18.9,79.4],[-19.70499,78.75128],[-19.67353,77.63859],[-18.47285,76.98565],[-20.03503,76.94434],[-21.67944,76.62795],[-19.83407,76.09808],[-19.59896,75.24838],[-20.66818,75.15585],[-19.37281,74.29561],[-21.59422,74.22382],[-20.43454,73.81713],[-20.76234,73.46436],[-22.17221,73.30955],[-23.56593,73.30663],[-22.31311,72.62928],[-22.29954,72.18409],[-24.27834,72.59788],[-24.79296,72.3302],[-23.44296,72.08016],[-22.13281,71.46898],[-21.75356,70.66369],[-23.53603,70.471],[-24.30702,70.85649],[-25.54341,71.43094],[-25.20135,70.75226],[-26.36276,70.22646],[-23.72742,70.18401],[-22.34902,70.12946],[-25.02927,69.2588],[-27.74737,68.47046],[-30.67371,68.12503],[-31.77665,68.12078],[-32.81105,67.73547],[-34.20196,66.67974],[-36.35284,65.9789],[-37.04378,65.93768],[-38.37505,65.69213],[-39.81222,65.45848],[-40.66899,64.83997],[-40.68281,64.13902],[-41.1887,63.48246],[-42.81938,62.68233],[-42.41666,61.90093],[-42.86619,61.07404],[-43.3784,60.09772],[-44.7875,60.03676],[-46.26364,60.85328],[-48.26294,60.85843],[-49.23308,61.40681],[-49.90039,62.38336],[-51.63325,63.62691],[-52.14014,64.27842],[-52.27659,65.1767],[-53.66166,66.09957],[-53.30161,66.8365],[-53.96911,67.18899],[-52.9804,68.35759],[-51.47536,68.72958],[-51.08041,69.14781],[-50.87122,69.9291],[-52.013585,69.574925],[-52.55792,69.42616],[-53.45629,69.283625],[-54.68336,69.61003],[-54.75001,70.28932],[-54.35884,70.821315],[-53.431315,70.835755],[-51.39014,70.56978],[-53.10937,71.20485],[-54.00422,71.54719],[-55,71.406537],[-55.83468,71.65444],[-54.71819,72.58625],[-55.32634,72.95861],[-56.12003,73.64977],[-57.32363,74.71026],[-58.59679,75.09861],[-58.58516,75.51727],[-61.26861,76.10238],[-63.39165,76.1752],[-66.06427,76.13486],[-68.50438,76.06141],[-69.66485,76.37975],[-71.40257,77.00857],[-68.77671,77.32312],[-66.76397,77.37595],[-71.04293,77.63595],[-73.297,78.04419],[-73.15938,78.43271],[-69.37345,78.91388],[-65.7107,79.39436],[-65.3239,79.75814],[-68.02298,80.11721],[-67.15129,80.51582],[-63.68925,81.21396],[-62.23444,81.3211],[-62.65116,81.77042],[-60.28249,82.03363],[-57.20744,82.19074],[-54.13442,82.19962],[-53.04328,81.88833],[-50.39061,82.43883],[-48.00386,82.06481],[-46.59984,81.985945],[-44.523,81.6607],[-46.9007,82.19979],[-46.76379,82.62796]]]}}, +{"type":"Feature","id":"GTM","properties":{"name":"Guatemala"},"geometry":{"type":"Polygon","coordinates":[[[-90.095555,13.735338],[-90.608624,13.909771],[-91.23241,13.927832],[-91.689747,14.126218],[-92.22775,14.538829],[-92.20323,14.830103],[-92.087216,15.064585],[-92.229249,15.251447],[-91.74796,16.066565],[-90.464473,16.069562],[-90.438867,16.41011],[-90.600847,16.470778],[-90.711822,16.687483],[-91.08167,16.918477],[-91.453921,17.252177],[-91.002269,17.254658],[-91.00152,17.817595],[-90.067934,17.819326],[-89.14308,17.808319],[-89.150806,17.015577],[-89.229122,15.886938],[-88.930613,15.887273],[-88.604586,15.70638],[-88.518364,15.855389],[-88.225023,15.727722],[-88.68068,15.346247],[-89.154811,15.066419],[-89.22522,14.874286],[-89.145535,14.678019],[-89.353326,14.424133],[-89.587343,14.362586],[-89.534219,14.244816],[-89.721934,14.134228],[-90.064678,13.88197],[-90.095555,13.735338]]]}}, +{"type":"Feature","id":"GUF","properties":{"name":"French Guiana"},"geometry":{"type":"Polygon","coordinates":[[[-52.556425,2.504705],[-52.939657,2.124858],[-53.418465,2.053389],[-53.554839,2.334897],[-53.778521,2.376703],[-54.088063,2.105557],[-54.524754,2.311849],[-54.27123,2.738748],[-54.184284,3.194172],[-54.011504,3.62257],[-54.399542,4.212611],[-54.478633,4.896756],[-53.958045,5.756548],[-53.618453,5.646529],[-52.882141,5.409851],[-51.823343,4.565768],[-51.657797,4.156232],[-52.249338,3.241094],[-52.556425,2.504705]]]}}, +{"type":"Feature","id":"GUY","properties":{"name":"Guyana"},"geometry":{"type":"Polygon","coordinates":[[[-59.758285,8.367035],[-59.101684,7.999202],[-58.482962,7.347691],[-58.454876,6.832787],[-58.078103,6.809094],[-57.542219,6.321268],[-57.147436,5.97315],[-57.307246,5.073567],[-57.914289,4.812626],[-57.86021,4.576801],[-58.044694,4.060864],[-57.601569,3.334655],[-57.281433,3.333492],[-57.150098,2.768927],[-56.539386,1.899523],[-56.782704,1.863711],[-57.335823,1.948538],[-57.660971,1.682585],[-58.11345,1.507195],[-58.429477,1.463942],[-58.540013,1.268088],[-59.030862,1.317698],[-59.646044,1.786894],[-59.718546,2.24963],[-59.974525,2.755233],[-59.815413,3.606499],[-59.53804,3.958803],[-59.767406,4.423503],[-60.111002,4.574967],[-59.980959,5.014061],[-60.213683,5.244486],[-60.733574,5.200277],[-61.410303,5.959068],[-61.139415,6.234297],[-61.159336,6.696077],[-60.543999,6.856584],[-60.295668,7.043911],[-60.637973,7.415],[-60.550588,7.779603],[-59.758285,8.367035]]]}}, +{"type":"Feature","id":"HND","properties":{"name":"Honduras"},"geometry":{"type":"Polygon","coordinates":[[[-87.316654,12.984686],[-87.489409,13.297535],[-87.793111,13.38448],[-87.723503,13.78505],[-87.859515,13.893312],[-88.065343,13.964626],[-88.503998,13.845486],[-88.541231,13.980155],[-88.843073,14.140507],[-89.058512,14.340029],[-89.353326,14.424133],[-89.145535,14.678019],[-89.22522,14.874286],[-89.154811,15.066419],[-88.68068,15.346247],[-88.225023,15.727722],[-88.121153,15.688655],[-87.901813,15.864458],[-87.61568,15.878799],[-87.522921,15.797279],[-87.367762,15.84694],[-86.903191,15.756713],[-86.440946,15.782835],[-86.119234,15.893449],[-86.001954,16.005406],[-85.683317,15.953652],[-85.444004,15.885749],[-85.182444,15.909158],[-84.983722,15.995923],[-84.52698,15.857224],[-84.368256,15.835158],[-84.063055,15.648244],[-83.773977,15.424072],[-83.410381,15.270903],[-83.147219,14.995829],[-83.489989,15.016267],[-83.628585,14.880074],[-83.975721,14.749436],[-84.228342,14.748764],[-84.449336,14.621614],[-84.649582,14.666805],[-84.820037,14.819587],[-84.924501,14.790493],[-85.052787,14.551541],[-85.148751,14.560197],[-85.165365,14.35437],[-85.514413,14.079012],[-85.698665,13.960078],[-85.801295,13.836055],[-86.096264,14.038187],[-86.312142,13.771356],[-86.520708,13.778487],[-86.755087,13.754845],[-86.733822,13.263093],[-86.880557,13.254204],[-87.005769,13.025794],[-87.316654,12.984686]]]}}, +{"type":"Feature","id":"HRV","properties":{"name":"Croatia"},"geometry":{"type":"Polygon","coordinates":[[[18.829838,45.908878],[19.072769,45.521511],[19.390476,45.236516],[19.005486,44.860234],[18.553214,45.08159],[17.861783,45.06774],[17.002146,45.233777],[16.534939,45.211608],[16.318157,45.004127],[15.959367,45.233777],[15.750026,44.818712],[16.23966,44.351143],[16.456443,44.04124],[16.916156,43.667722],[17.297373,43.446341],[17.674922,43.028563],[18.56,42.65],[18.450016,42.479991],[17.50997,42.849995],[16.930006,43.209998],[16.015385,43.507215],[15.174454,44.243191],[15.37625,44.317915],[14.920309,44.738484],[14.901602,45.07606],[14.258748,45.233777],[13.952255,44.802124],[13.656976,45.136935],[13.679403,45.484149],[13.71506,45.500324],[14.411968,45.466166],[14.595109,45.634941],[14.935244,45.471695],[15.327675,45.452316],[15.323954,45.731783],[15.67153,45.834154],[15.768733,46.238108],[16.564808,46.503751],[16.882515,46.380632],[17.630066,45.951769],[18.456062,45.759481],[18.829838,45.908878]]]}}, +{"type":"Feature","id":"HTI","properties":{"name":"Haiti"},"geometry":{"type":"Polygon","coordinates":[[[-73.189791,19.915684],[-72.579673,19.871501],[-71.712361,19.714456],[-71.624873,19.169838],[-71.701303,18.785417],[-71.945112,18.6169],[-71.687738,18.31666],[-71.708305,18.044997],[-72.372476,18.214961],[-72.844411,18.145611],[-73.454555,18.217906],[-73.922433,18.030993],[-74.458034,18.34255],[-74.369925,18.664908],[-73.449542,18.526053],[-72.694937,18.445799],[-72.334882,18.668422],[-72.79165,19.101625],[-72.784105,19.483591],[-73.415022,19.639551],[-73.189791,19.915684]]]}}, +{"type":"Feature","id":"HUN","properties":{"name":"Hungary"},"geometry":{"type":"Polygon","coordinates":[[[16.202298,46.852386],[16.534268,47.496171],[16.340584,47.712902],[16.903754,47.714866],[16.979667,48.123497],[17.488473,47.867466],[17.857133,47.758429],[18.696513,47.880954],[18.777025,48.081768],[19.174365,48.111379],[19.661364,48.266615],[19.769471,48.202691],[20.239054,48.327567],[20.473562,48.56285],[20.801294,48.623854],[21.872236,48.319971],[22.085608,48.422264],[22.64082,48.15024],[22.710531,47.882194],[22.099768,47.672439],[21.626515,46.994238],[21.021952,46.316088],[20.220192,46.127469],[19.596045,46.17173],[18.829838,45.908878],[18.456062,45.759481],[17.630066,45.951769],[16.882515,46.380632],[16.564808,46.503751],[16.370505,46.841327],[16.202298,46.852386]]]}}, +{"type":"Feature","id":"IDN","properties":{"name":"Indonesia"},"geometry":{"type":"MultiPolygon","coordinates":[[[[120.715609,-10.239581],[120.295014,-10.25865],[118.967808,-9.557969],[119.90031,-9.36134],[120.425756,-9.665921],[120.775502,-9.969675],[120.715609,-10.239581]]],[[[124.43595,-10.140001],[123.579982,-10.359987],[123.459989,-10.239995],[123.550009,-9.900016],[123.980009,-9.290027],[124.968682,-8.89279],[125.07002,-9.089987],[125.08852,-9.393173],[124.43595,-10.140001]]],[[[117.900018,-8.095681],[118.260616,-8.362383],[118.87846,-8.280683],[119.126507,-8.705825],[117.970402,-8.906639],[117.277731,-9.040895],[116.740141,-9.032937],[117.083737,-8.457158],[117.632024,-8.449303],[117.900018,-8.095681]]],[[[122.903537,-8.094234],[122.756983,-8.649808],[121.254491,-8.933666],[119.924391,-8.810418],[119.920929,-8.444859],[120.715092,-8.236965],[121.341669,-8.53674],[122.007365,-8.46062],[122.903537,-8.094234]]],[[[108.623479,-6.777674],[110.539227,-6.877358],[110.759576,-6.465186],[112.614811,-6.946036],[112.978768,-7.594213],[114.478935,-7.776528],[115.705527,-8.370807],[114.564511,-8.751817],[113.464734,-8.348947],[112.559672,-8.376181],[111.522061,-8.302129],[110.58615,-8.122605],[109.427667,-7.740664],[108.693655,-7.6416],[108.277763,-7.766657],[106.454102,-7.3549],[106.280624,-6.9249],[105.365486,-6.851416],[106.051646,-5.895919],[107.265009,-5.954985],[108.072091,-6.345762],[108.486846,-6.421985],[108.623479,-6.777674]]],[[[134.724624,-6.214401],[134.210134,-6.895238],[134.112776,-6.142467],[134.290336,-5.783058],[134.499625,-5.445042],[134.727002,-5.737582],[134.724624,-6.214401]]],[[[127.249215,-3.459065],[126.874923,-3.790983],[126.183802,-3.607376],[125.989034,-3.177273],[127.000651,-3.129318],[127.249215,-3.459065]]],[[[130.471344,-3.093764],[130.834836,-3.858472],[129.990547,-3.446301],[129.155249,-3.362637],[128.590684,-3.428679],[127.898891,-3.393436],[128.135879,-2.84365],[129.370998,-2.802154],[130.471344,-3.093764]]],[[[134.143368,-1.151867],[134.422627,-2.769185],[135.457603,-3.367753],[136.293314,-2.307042],[137.440738,-1.703513],[138.329727,-1.702686],[139.184921,-2.051296],[139.926684,-2.409052],[141.00021,-2.600151],[141.017057,-5.859022],[141.033852,-9.117893],[140.143415,-8.297168],[139.127767,-8.096043],[138.881477,-8.380935],[137.614474,-8.411683],[138.039099,-7.597882],[138.668621,-7.320225],[138.407914,-6.232849],[137.92784,-5.393366],[135.98925,-4.546544],[135.164598,-4.462931],[133.66288,-3.538853],[133.367705,-4.024819],[132.983956,-4.112979],[132.756941,-3.746283],[132.753789,-3.311787],[131.989804,-2.820551],[133.066845,-2.460418],[133.780031,-2.479848],[133.696212,-2.214542],[132.232373,-2.212526],[131.836222,-1.617162],[130.94284,-1.432522],[130.519558,-0.93772],[131.867538,-0.695461],[132.380116,-0.369538],[133.985548,-0.78021],[134.143368,-1.151867]]],[[[125.240501,1.419836],[124.437035,0.427881],[123.685505,0.235593],[122.723083,0.431137],[121.056725,0.381217],[120.183083,0.237247],[120.04087,-0.519658],[120.935905,-1.408906],[121.475821,-0.955962],[123.340565,-0.615673],[123.258399,-1.076213],[122.822715,-0.930951],[122.38853,-1.516858],[121.508274,-1.904483],[122.454572,-3.186058],[122.271896,-3.5295],[123.170963,-4.683693],[123.162333,-5.340604],[122.628515,-5.634591],[122.236394,-5.282933],[122.719569,-4.464172],[121.738234,-4.851331],[121.489463,-4.574553],[121.619171,-4.188478],[120.898182,-3.602105],[120.972389,-2.627643],[120.305453,-2.931604],[120.390047,-4.097579],[120.430717,-5.528241],[119.796543,-5.6734],[119.366906,-5.379878],[119.653606,-4.459417],[119.498835,-3.494412],[119.078344,-3.487022],[118.767769,-2.801999],[119.180974,-2.147104],[119.323394,-1.353147],[119.825999,0.154254],[120.035702,0.566477],[120.885779,1.309223],[121.666817,1.013944],[122.927567,0.875192],[124.077522,0.917102],[125.065989,1.643259],[125.240501,1.419836]]],[[[128.688249,1.132386],[128.635952,0.258486],[128.12017,0.356413],[127.968034,-0.252077],[128.379999,-0.780004],[128.100016,-0.899996],[127.696475,-0.266598],[127.39949,1.011722],[127.600512,1.810691],[127.932378,2.174596],[128.004156,1.628531],[128.594559,1.540811],[128.688249,1.132386]]],[[[117.875627,1.827641],[118.996747,0.902219],[117.811858,0.784242],[117.478339,0.102475],[117.521644,-0.803723],[116.560048,-1.487661],[116.533797,-2.483517],[116.148084,-4.012726],[116.000858,-3.657037],[114.864803,-4.106984],[114.468652,-3.495704],[113.755672,-3.43917],[113.256994,-3.118776],[112.068126,-3.478392],[111.703291,-2.994442],[111.04824,-3.049426],[110.223846,-2.934032],[110.070936,-1.592874],[109.571948,-1.314907],[109.091874,-0.459507],[108.952658,0.415375],[109.069136,1.341934],[109.66326,2.006467],[109.830227,1.338136],[110.514061,0.773131],[111.159138,0.976478],[111.797548,0.904441],[112.380252,1.410121],[112.859809,1.49779],[113.80585,1.217549],[114.621355,1.430688],[115.134037,2.821482],[115.519078,3.169238],[115.865517,4.306559],[117.015214,4.306094],[117.882035,4.137551],[117.313232,3.234428],[118.04833,2.28769],[117.875627,1.827641]]],[[[105.817655,-5.852356],[104.710384,-5.873285],[103.868213,-5.037315],[102.584261,-4.220259],[102.156173,-3.614146],[101.399113,-2.799777],[100.902503,-2.050262],[100.141981,-0.650348],[99.26374,0.183142],[98.970011,1.042882],[98.601351,1.823507],[97.699598,2.453184],[97.176942,3.308791],[96.424017,3.86886],[95.380876,4.970782],[95.293026,5.479821],[95.936863,5.439513],[97.484882,5.246321],[98.369169,4.26837],[99.142559,3.59035],[99.693998,3.174329],[100.641434,2.099381],[101.658012,2.083697],[102.498271,1.3987],[103.07684,0.561361],[103.838396,0.104542],[103.437645,-0.711946],[104.010789,-1.059212],[104.369991,-1.084843],[104.53949,-1.782372],[104.887893,-2.340425],[105.622111,-2.428844],[106.108593,-3.061777],[105.857446,-4.305525],[105.817655,-5.852356]]]]}}, +{"type":"Feature","id":"IND","properties":{"name":"India"},"geometry":{"type":"Polygon","coordinates":[[[77.837451,35.49401],[78.912269,34.321936],[78.811086,33.506198],[79.208892,32.994395],[79.176129,32.48378],[78.458446,32.618164],[78.738894,31.515906],[79.721367,30.882715],[81.111256,30.183481],[80.476721,29.729865],[80.088425,28.79447],[81.057203,28.416095],[81.999987,27.925479],[83.304249,27.364506],[84.675018,27.234901],[85.251779,26.726198],[86.024393,26.630985],[87.227472,26.397898],[88.060238,26.414615],[88.174804,26.810405],[88.043133,27.445819],[88.120441,27.876542],[88.730326,28.086865],[88.814248,27.299316],[88.835643,27.098966],[89.744528,26.719403],[90.373275,26.875724],[91.217513,26.808648],[92.033484,26.83831],[92.103712,27.452614],[91.696657,27.771742],[92.503119,27.896876],[93.413348,28.640629],[94.56599,29.277438],[95.404802,29.031717],[96.117679,29.452802],[96.586591,28.83098],[96.248833,28.411031],[97.327114,28.261583],[97.402561,27.882536],[97.051989,27.699059],[97.133999,27.083774],[96.419366,27.264589],[95.124768,26.573572],[95.155153,26.001307],[94.603249,25.162495],[94.552658,24.675238],[94.106742,23.850741],[93.325188,24.078556],[93.286327,23.043658],[93.060294,22.703111],[93.166128,22.27846],[92.672721,22.041239],[92.146035,23.627499],[91.869928,23.624346],[91.706475,22.985264],[91.158963,23.503527],[91.46773,24.072639],[91.915093,24.130414],[92.376202,24.976693],[91.799596,25.147432],[90.872211,25.132601],[89.920693,25.26975],[89.832481,25.965082],[89.355094,26.014407],[88.563049,26.446526],[88.209789,25.768066],[88.931554,25.238692],[88.306373,24.866079],[88.084422,24.501657],[88.69994,24.233715],[88.52977,23.631142],[88.876312,22.879146],[89.031961,22.055708],[88.888766,21.690588],[88.208497,21.703172],[86.975704,21.495562],[87.033169,20.743308],[86.499351,20.151638],[85.060266,19.478579],[83.941006,18.30201],[83.189217,17.671221],[82.192792,17.016636],[82.191242,16.556664],[81.692719,16.310219],[80.791999,15.951972],[80.324896,15.899185],[80.025069,15.136415],[80.233274,13.835771],[80.286294,13.006261],[79.862547,12.056215],[79.857999,10.357275],[79.340512,10.308854],[78.885345,9.546136],[79.18972,9.216544],[78.277941,8.933047],[77.941165,8.252959],[77.539898,7.965535],[76.592979,8.899276],[76.130061,10.29963],[75.746467,11.308251],[75.396101,11.781245],[74.864816,12.741936],[74.616717,13.992583],[74.443859,14.617222],[73.534199,15.990652],[73.119909,17.92857],[72.820909,19.208234],[72.824475,20.419503],[72.630533,21.356009],[71.175273,20.757441],[70.470459,20.877331],[69.16413,22.089298],[69.644928,22.450775],[69.349597,22.84318],[68.176645,23.691965],[68.842599,24.359134],[71.04324,24.356524],[70.844699,25.215102],[70.282873,25.722229],[70.168927,26.491872],[69.514393,26.940966],[70.616496,27.989196],[71.777666,27.91318],[72.823752,28.961592],[73.450638,29.976413],[74.42138,30.979815],[74.405929,31.692639],[75.258642,32.271105],[74.451559,32.7649],[74.104294,33.441473],[73.749948,34.317699],[74.240203,34.748887],[75.757061,34.504923],[76.871722,34.653544],[77.837451,35.49401]]]}}, +{"type":"Feature","id":"IRL","properties":{"name":"Ireland"},"geometry":{"type":"Polygon","coordinates":[[[-6.197885,53.867565],[-6.032985,53.153164],[-6.788857,52.260118],[-8.561617,51.669301],[-9.977086,51.820455],[-9.166283,52.864629],[-9.688525,53.881363],[-8.327987,54.664519],[-7.572168,55.131622],[-7.366031,54.595841],[-7.572168,54.059956],[-6.95373,54.073702],[-6.197885,53.867565]]]}}, +{"type":"Feature","id":"IRN","properties":{"name":"Iran"},"geometry":{"type":"Polygon","coordinates":[[[53.921598,37.198918],[54.800304,37.392421],[55.511578,37.964117],[56.180375,37.935127],[56.619366,38.121394],[57.330434,38.029229],[58.436154,37.522309],[59.234762,37.412988],[60.377638,36.527383],[61.123071,36.491597],[61.210817,35.650072],[60.803193,34.404102],[60.52843,33.676446],[60.9637,33.528832],[60.536078,32.981269],[60.863655,32.18292],[60.941945,31.548075],[61.699314,31.379506],[61.781222,30.73585],[60.874248,29.829239],[61.369309,29.303276],[61.771868,28.699334],[62.72783,28.259645],[62.755426,27.378923],[63.233898,27.217047],[63.316632,26.756532],[61.874187,26.239975],[61.497363,25.078237],[59.616134,25.380157],[58.525761,25.609962],[57.397251,25.739902],[56.970766,26.966106],[56.492139,27.143305],[55.72371,26.964633],[54.71509,26.480658],[53.493097,26.812369],[52.483598,27.580849],[51.520763,27.86569],[50.852948,28.814521],[50.115009,30.147773],[49.57685,29.985715],[48.941333,30.31709],[48.567971,29.926778],[48.014568,30.452457],[48.004698,30.985137],[47.685286,30.984853],[47.849204,31.709176],[47.334661,32.469155],[46.109362,33.017287],[45.416691,33.967798],[45.64846,34.748138],[46.151788,35.093259],[46.07634,35.677383],[45.420618,35.977546],[44.77267,37.17045],[44.225756,37.971584],[44.421403,38.281281],[44.109225,39.428136],[44.79399,39.713003],[44.952688,39.335765],[45.457722,38.874139],[46.143623,38.741201],[46.50572,38.770605],[47.685079,39.508364],[48.060095,39.582235],[48.355529,39.288765],[48.010744,38.794015],[48.634375,38.270378],[48.883249,38.320245],[49.199612,37.582874],[50.147771,37.374567],[50.842354,36.872814],[52.264025,36.700422],[53.82579,36.965031],[53.921598,37.198918]]]}}, +{"type":"Feature","id":"IRQ","properties":{"name":"Iraq"},"geometry":{"type":"Polygon","coordinates":[[[45.420618,35.977546],[46.07634,35.677383],[46.151788,35.093259],[45.64846,34.748138],[45.416691,33.967798],[46.109362,33.017287],[47.334661,32.469155],[47.849204,31.709176],[47.685286,30.984853],[48.004698,30.985137],[48.014568,30.452457],[48.567971,29.926778],[47.974519,29.975819],[47.302622,30.05907],[46.568713,29.099025],[44.709499,29.178891],[41.889981,31.190009],[40.399994,31.889992],[39.195468,32.161009],[38.792341,33.378686],[41.006159,34.419372],[41.383965,35.628317],[41.289707,36.358815],[41.837064,36.605854],[42.349591,37.229873],[42.779126,37.385264],[43.942259,37.256228],[44.293452,37.001514],[44.772699,37.170445],[45.420618,35.977546]]]}}, +{"type":"Feature","id":"ISL","properties":{"name":"Iceland"},"geometry":{"type":"Polygon","coordinates":[[[-14.508695,66.455892],[-14.739637,65.808748],[-13.609732,65.126671],[-14.909834,64.364082],[-17.794438,63.678749],[-18.656246,63.496383],[-19.972755,63.643635],[-22.762972,63.960179],[-21.778484,64.402116],[-23.955044,64.89113],[-22.184403,65.084968],[-22.227423,65.378594],[-24.326184,65.611189],[-23.650515,66.262519],[-22.134922,66.410469],[-20.576284,65.732112],[-19.056842,66.276601],[-17.798624,65.993853],[-16.167819,66.526792],[-14.508695,66.455892]]]}}, +{"type":"Feature","id":"ISR","properties":{"name":"Israel"},"geometry":{"type":"Polygon","coordinates":[[[35.719918,32.709192],[35.545665,32.393992],[35.18393,32.532511],[34.974641,31.866582],[35.225892,31.754341],[34.970507,31.616778],[34.927408,31.353435],[35.397561,31.489086],[35.420918,31.100066],[34.922603,29.501326],[34.265433,31.219361],[34.556372,31.548824],[34.488107,31.605539],[34.752587,32.072926],[34.955417,32.827376],[35.098457,33.080539],[35.126053,33.0909],[35.460709,33.08904],[35.552797,33.264275],[35.821101,33.277426],[35.836397,32.868123],[35.700798,32.716014],[35.719918,32.709192]]]}}, +{"type":"Feature","id":"ITA","properties":{"name":"Italy"},"geometry":{"type":"MultiPolygon","coordinates":[[[[15.520376,38.231155],[15.160243,37.444046],[15.309898,37.134219],[15.099988,36.619987],[14.335229,36.996631],[13.826733,37.104531],[12.431004,37.61295],[12.570944,38.126381],[13.741156,38.034966],[14.761249,38.143874],[15.520376,38.231155]]],[[[9.210012,41.209991],[9.809975,40.500009],[9.669519,39.177376],[9.214818,39.240473],[8.806936,38.906618],[8.428302,39.171847],[8.388253,40.378311],[8.159998,40.950007],[8.709991,40.899984],[9.210012,41.209991]]],[[[12.376485,46.767559],[13.806475,46.509306],[13.69811,46.016778],[13.93763,45.591016],[13.141606,45.736692],[12.328581,45.381778],[12.383875,44.885374],[12.261453,44.600482],[12.589237,44.091366],[13.526906,43.587727],[14.029821,42.761008],[15.14257,41.95514],[15.926191,41.961315],[16.169897,41.740295],[15.889346,41.541082],[16.785002,41.179606],[17.519169,40.877143],[18.376687,40.355625],[18.480247,40.168866],[18.293385,39.810774],[17.73838,40.277671],[16.869596,40.442235],[16.448743,39.795401],[17.17149,39.4247],[17.052841,38.902871],[16.635088,38.843572],[16.100961,37.985899],[15.684087,37.908849],[15.687963,38.214593],[15.891981,38.750942],[16.109332,38.964547],[15.718814,39.544072],[15.413613,40.048357],[14.998496,40.172949],[14.703268,40.60455],[14.060672,40.786348],[13.627985,41.188287],[12.888082,41.25309],[12.106683,41.704535],[11.191906,42.355425],[10.511948,42.931463],[10.200029,43.920007],[9.702488,44.036279],[8.888946,44.366336],[8.428561,44.231228],[7.850767,43.767148],[7.435185,43.693845],[7.549596,44.127901],[7.007562,44.254767],[6.749955,45.028518],[7.096652,45.333099],[6.802355,45.70858],[6.843593,45.991147],[7.273851,45.776948],[7.755992,45.82449],[8.31663,46.163642],[8.489952,46.005151],[8.966306,46.036932],[9.182882,46.440215],[9.922837,46.314899],[10.363378,46.483571],[10.442701,46.893546],[11.048556,46.751359],[11.164828,46.941579],[12.153088,47.115393],[12.376485,46.767559]]]]}}, +{"type":"Feature","id":"JAM","properties":{"name":"Jamaica"},"geometry":{"type":"Polygon","coordinates":[[[-77.569601,18.490525],[-76.896619,18.400867],[-76.365359,18.160701],[-76.199659,17.886867],[-76.902561,17.868238],[-77.206341,17.701116],[-77.766023,17.861597],[-78.337719,18.225968],[-78.217727,18.454533],[-77.797365,18.524218],[-77.569601,18.490525]]]}}, +{"type":"Feature","id":"JOR","properties":{"name":"Jordan"},"geometry":{"type":"Polygon","coordinates":[[[35.545665,32.393992],[35.719918,32.709192],[36.834062,32.312938],[38.792341,33.378686],[39.195468,32.161009],[39.004886,32.010217],[37.002166,31.508413],[37.998849,30.5085],[37.66812,30.338665],[37.503582,30.003776],[36.740528,29.865283],[36.501214,29.505254],[36.068941,29.197495],[34.956037,29.356555],[34.922603,29.501326],[35.420918,31.100066],[35.397561,31.489086],[35.545252,31.782505],[35.545665,32.393992]]]}}, +{"type":"Feature","id":"JPN","properties":{"name":"Japan"},"geometry":{"type":"MultiPolygon","coordinates":[[[[134.638428,34.149234],[134.766379,33.806335],[134.203416,33.201178],[133.79295,33.521985],[133.280268,33.28957],[133.014858,32.704567],[132.363115,32.989382],[132.371176,33.463642],[132.924373,34.060299],[133.492968,33.944621],[133.904106,34.364931],[134.638428,34.149234]]],[[[140.976388,37.142074],[140.59977,36.343983],[140.774074,35.842877],[140.253279,35.138114],[138.975528,34.6676],[137.217599,34.606286],[135.792983,33.464805],[135.120983,33.849071],[135.079435,34.596545],[133.340316,34.375938],[132.156771,33.904933],[130.986145,33.885761],[132.000036,33.149992],[131.33279,31.450355],[130.686318,31.029579],[130.20242,31.418238],[130.447676,32.319475],[129.814692,32.61031],[129.408463,33.296056],[130.353935,33.604151],[130.878451,34.232743],[131.884229,34.749714],[132.617673,35.433393],[134.608301,35.731618],[135.677538,35.527134],[136.723831,37.304984],[137.390612,36.827391],[138.857602,37.827485],[139.426405,38.215962],[140.05479,39.438807],[139.883379,40.563312],[140.305783,41.195005],[141.368973,41.37856],[141.914263,39.991616],[141.884601,39.180865],[140.959489,38.174001],[140.976388,37.142074]]],[[[143.910162,44.1741],[144.613427,43.960883],[145.320825,44.384733],[145.543137,43.262088],[144.059662,42.988358],[143.18385,41.995215],[141.611491,42.678791],[141.067286,41.584594],[139.955106,41.569556],[139.817544,42.563759],[140.312087,43.333273],[141.380549,43.388825],[141.671952,44.772125],[141.967645,45.551483],[143.14287,44.510358],[143.910162,44.1741]]]]}}, +{"type":"Feature","id":"KAZ","properties":{"name":"Kazakhstan"},"geometry":{"type":"Polygon","coordinates":[[[70.962315,42.266154],[70.388965,42.081308],[69.070027,41.384244],[68.632483,40.668681],[68.259896,40.662325],[67.985856,41.135991],[66.714047,41.168444],[66.510649,41.987644],[66.023392,41.994646],[66.098012,42.99766],[64.900824,43.728081],[63.185787,43.650075],[62.0133,43.504477],[61.05832,44.405817],[60.239972,44.784037],[58.689989,45.500014],[58.503127,45.586804],[55.928917,44.995858],[55.968191,41.308642],[55.455251,41.259859],[54.755345,42.043971],[54.079418,42.324109],[52.944293,42.116034],[52.50246,41.783316],[52.446339,42.027151],[52.692112,42.443895],[52.501426,42.792298],[51.342427,43.132975],[50.891292,44.031034],[50.339129,44.284016],[50.305643,44.609836],[51.278503,44.514854],[51.316899,45.245998],[52.16739,45.408391],[53.040876,45.259047],[53.220866,46.234646],[53.042737,46.853006],[52.042023,46.804637],[51.191945,47.048705],[50.034083,46.60899],[49.10116,46.39933],[48.593241,46.561034],[48.694734,47.075628],[48.057253,47.743753],[47.315231,47.715847],[46.466446,48.394152],[47.043672,49.152039],[46.751596,49.356006],[47.54948,50.454698],[48.577841,49.87476],[48.702382,50.605128],[50.766648,51.692762],[52.328724,51.718652],[54.532878,51.02624],[55.716941,50.621717],[56.777961,51.043551],[58.363291,51.063653],[59.642282,50.545442],[59.932807,50.842194],[61.337424,50.79907],[61.588003,51.272659],[59.967534,51.96042],[60.927269,52.447548],[60.739993,52.719986],[61.699986,52.979996],[60.978066,53.664993],[61.436591,54.006265],[65.178534,54.354228],[65.666876,54.601267],[68.1691,54.970392],[69.068167,55.38525],[70.865267,55.169734],[71.180131,54.133285],[72.22415,54.376655],[73.508516,54.035617],[73.425679,53.48981],[74.384845,53.546861],[76.8911,54.490524],[76.525179,54.177003],[77.800916,53.404415],[80.03556,50.864751],[80.568447,51.388336],[81.945986,50.812196],[83.383004,51.069183],[83.935115,50.889246],[84.416377,50.3114],[85.11556,50.117303],[85.54127,49.692859],[86.829357,49.826675],[87.35997,49.214981],[86.598776,48.549182],[85.768233,48.455751],[85.720484,47.452969],[85.16429,47.000956],[83.180484,47.330031],[82.458926,45.53965],[81.947071,45.317027],[79.966106,44.917517],[80.866206,43.180362],[80.18015,42.920068],[80.25999,42.349999],[79.643645,42.496683],[79.142177,42.856092],[77.658392,42.960686],[76.000354,42.988022],[75.636965,42.8779],[74.212866,43.298339],[73.645304,43.091272],[73.489758,42.500894],[71.844638,42.845395],[71.186281,42.704293],[70.962315,42.266154]]]}}, +{"type":"Feature","id":"KEN","properties":{"name":"Kenya"},"geometry":{"type":"Polygon","coordinates":[[[40.993,-0.85829],[41.58513,-1.68325],[40.88477,-2.08255],[40.63785,-2.49979],[40.26304,-2.57309],[40.12119,-3.27768],[39.80006,-3.68116],[39.60489,-4.34653],[39.20222,-4.67677],[37.7669,-3.67712],[37.69869,-3.09699],[34.07262,-1.05982],[33.903711,-0.95],[33.893569,0.109814],[34.18,0.515],[34.6721,1.17694],[35.03599,1.90584],[34.59607,3.05374],[34.47913,3.5556],[34.005,4.249885],[34.620196,4.847123],[35.298007,5.506],[35.817448,5.338232],[35.817448,4.776966],[36.159079,4.447864],[36.855093,4.447864],[38.120915,3.598605],[38.43697,3.58851],[38.67114,3.61607],[38.89251,3.50074],[39.559384,3.42206],[39.85494,3.83879],[40.76848,4.25702],[41.1718,3.91909],[41.855083,3.918912],[40.98105,2.78452],[40.993,-0.85829]]]}}, +{"type":"Feature","id":"KGZ","properties":{"name":"Kyrgyzstan"},"geometry":{"type":"Polygon","coordinates":[[[70.962315,42.266154],[71.186281,42.704293],[71.844638,42.845395],[73.489758,42.500894],[73.645304,43.091272],[74.212866,43.298339],[75.636965,42.8779],[76.000354,42.988022],[77.658392,42.960686],[79.142177,42.856092],[79.643645,42.496683],[80.25999,42.349999],[80.11943,42.123941],[78.543661,41.582243],[78.187197,41.185316],[76.904484,41.066486],[76.526368,40.427946],[75.467828,40.562072],[74.776862,40.366425],[73.822244,39.893973],[73.960013,39.660008],[73.675379,39.431237],[71.784694,39.279463],[70.549162,39.604198],[69.464887,39.526683],[69.55961,40.103211],[70.648019,39.935754],[71.014198,40.244366],[71.774875,40.145844],[73.055417,40.866033],[71.870115,41.3929],[71.157859,41.143587],[70.420022,41.519998],[71.259248,42.167711],[70.962315,42.266154]]]}}, +{"type":"Feature","id":"KHM","properties":{"name":"Cambodia"},"geometry":{"type":"Polygon","coordinates":[[[103.49728,10.632555],[103.09069,11.153661],[102.584932,12.186595],[102.348099,13.394247],[102.988422,14.225721],[104.281418,14.416743],[105.218777,14.273212],[106.043946,13.881091],[106.496373,14.570584],[107.382727,14.202441],[107.614548,13.535531],[107.491403,12.337206],[105.810524,11.567615],[106.24967,10.961812],[105.199915,10.88931],[104.334335,10.486544],[103.49728,10.632555]]]}}, +{"type":"Feature","id":"KOR","properties":{"name":"South Korea"},"geometry":{"type":"Polygon","coordinates":[[[128.349716,38.612243],[129.21292,37.432392],[129.46045,36.784189],[129.468304,35.632141],[129.091377,35.082484],[128.18585,34.890377],[127.386519,34.475674],[126.485748,34.390046],[126.37392,34.93456],[126.559231,35.684541],[126.117398,36.725485],[126.860143,36.893924],[126.174759,37.749686],[126.237339,37.840378],[126.68372,37.804773],[127.073309,38.256115],[127.780035,38.304536],[128.205746,38.370397],[128.349716,38.612243]]]}}, +{"type":"Feature","id":"CS-KM","properties":{"name":"Kosovo"},"geometry":{"type":"Polygon","coordinates":[[[20.76216,42.05186],[20.71731,41.84711],[20.59023,41.85541],[20.52295,42.21787],[20.28374,42.32025],[20.0707,42.58863],[20.25758,42.81275],[20.49679,42.88469],[20.63508,43.21671],[20.81448,43.27205],[20.95651,43.13094],[21.143395,43.068685],[21.27421,42.90959],[21.43866,42.86255],[21.63302,42.67717],[21.77505,42.6827],[21.66292,42.43922],[21.54332,42.32025],[21.576636,42.245224],[21.3527,42.2068],[20.76216,42.05186]]]}}, +{"type":"Feature","id":"KWT","properties":{"name":"Kuwait"},"geometry":{"type":"Polygon","coordinates":[[[47.974519,29.975819],[48.183189,29.534477],[48.093943,29.306299],[48.416094,28.552004],[47.708851,28.526063],[47.459822,29.002519],[46.568713,29.099025],[47.302622,30.05907],[47.974519,29.975819]]]}}, +{"type":"Feature","id":"LAO","properties":{"name":"Laos"},"geometry":{"type":"Polygon","coordinates":[[[105.218777,14.273212],[105.544338,14.723934],[105.589039,15.570316],[104.779321,16.441865],[104.716947,17.428859],[103.956477,18.240954],[103.200192,18.309632],[102.998706,17.961695],[102.413005,17.932782],[102.113592,18.109102],[101.059548,17.512497],[101.035931,18.408928],[101.282015,19.462585],[100.606294,19.508344],[100.548881,20.109238],[100.115988,20.41785],[100.329101,20.786122],[101.180005,21.436573],[101.270026,21.201652],[101.80312,21.174367],[101.652018,22.318199],[102.170436,22.464753],[102.754896,21.675137],[103.203861,20.766562],[104.435,20.758733],[104.822574,19.886642],[104.183388,19.624668],[103.896532,19.265181],[105.094598,18.666975],[105.925762,17.485315],[106.556008,16.604284],[107.312706,15.908538],[107.564525,15.202173],[107.382727,14.202441],[106.496373,14.570584],[106.043946,13.881091],[105.218777,14.273212]]]}}, +{"type":"Feature","id":"LBN","properties":{"name":"Lebanon"},"geometry":{"type":"Polygon","coordinates":[[[35.821101,33.277426],[35.552797,33.264275],[35.460709,33.08904],[35.126053,33.0909],[35.482207,33.90545],[35.979592,34.610058],[35.998403,34.644914],[36.448194,34.593935],[36.61175,34.201789],[36.06646,33.824912],[35.821101,33.277426]]]}}, +{"type":"Feature","id":"LBR","properties":{"name":"Liberia"},"geometry":{"type":"Polygon","coordinates":[[[-7.712159,4.364566],[-7.974107,4.355755],[-9.004794,4.832419],[-9.91342,5.593561],[-10.765384,6.140711],[-11.438779,6.785917],[-11.199802,7.105846],[-11.146704,7.396706],[-10.695595,7.939464],[-10.230094,8.406206],[-10.016567,8.428504],[-9.755342,8.541055],[-9.33728,7.928534],[-9.403348,7.526905],[-9.208786,7.313921],[-8.926065,7.309037],[-8.722124,7.711674],[-8.439298,7.686043],[-8.485446,7.395208],[-8.385452,6.911801],[-8.60288,6.467564],[-8.311348,6.193033],[-7.993693,6.12619],[-7.570153,5.707352],[-7.539715,5.313345],[-7.635368,5.188159],[-7.712159,4.364566]]]}}, +{"type":"Feature","id":"LBY","properties":{"name":"Libya"},"geometry":{"type":"Polygon","coordinates":[[[14.8513,22.86295],[14.143871,22.491289],[13.581425,23.040506],[11.999506,23.471668],[11.560669,24.097909],[10.771364,24.562532],[10.303847,24.379313],[9.948261,24.936954],[9.910693,25.365455],[9.319411,26.094325],[9.716286,26.512206],[9.629056,27.140953],[9.756128,27.688259],[9.683885,28.144174],[9.859998,28.95999],[9.805634,29.424638],[9.48214,30.307556],[9.970017,30.539325],[10.056575,30.961831],[9.950225,31.37607],[10.636901,31.761421],[10.94479,32.081815],[11.432253,32.368903],[11.488787,33.136996],[12.66331,32.79278],[13.08326,32.87882],[13.91868,32.71196],[15.24563,32.26508],[15.71394,31.37626],[16.61162,31.18218],[18.02109,30.76357],[19.08641,30.26639],[19.57404,30.52582],[20.05335,30.98576],[19.82033,31.75179],[20.13397,32.2382],[20.85452,32.7068],[21.54298,32.8432],[22.89576,32.63858],[23.2368,32.19149],[23.60913,32.18726],[23.9275,32.01667],[24.92114,31.89936],[25.16482,31.56915],[24.80287,31.08929],[24.95762,30.6616],[24.70007,30.04419],[25,29.238655],[25,25.6825],[25,22],[25,20.00304],[23.85,20],[23.83766,19.58047],[19.84926,21.49509],[15.86085,23.40972],[14.8513,22.86295]]]}}, +{"type":"Feature","id":"LKA","properties":{"name":"Sri Lanka"},"geometry":{"type":"Polygon","coordinates":[[[81.787959,7.523055],[81.637322,6.481775],[81.21802,6.197141],[80.348357,5.96837],[79.872469,6.763463],[79.695167,8.200843],[80.147801,9.824078],[80.838818,9.268427],[81.304319,8.564206],[81.787959,7.523055]]]}}, +{"type":"Feature","id":"LSO","properties":{"name":"Lesotho"},"geometry":{"type":"Polygon","coordinates":[[[28.978263,-28.955597],[29.325166,-29.257387],[29.018415,-29.743766],[28.8484,-30.070051],[28.291069,-30.226217],[28.107205,-30.545732],[27.749397,-30.645106],[26.999262,-29.875954],[27.532511,-29.242711],[28.074338,-28.851469],[28.5417,-28.647502],[28.978263,-28.955597]]]}}, +{"type":"Feature","id":"LTU","properties":{"name":"Lithuania"},"geometry":{"type":"Polygon","coordinates":[[[22.731099,54.327537],[22.651052,54.582741],[22.757764,54.856574],[22.315724,55.015299],[21.268449,55.190482],[21.0558,56.031076],[22.201157,56.337802],[23.878264,56.273671],[24.860684,56.372528],[25.000934,56.164531],[25.533047,56.100297],[26.494331,55.615107],[26.588279,55.167176],[25.768433,54.846963],[25.536354,54.282423],[24.450684,53.905702],[23.484128,53.912498],[23.243987,54.220567],[22.731099,54.327537]]]}}, +{"type":"Feature","id":"LUX","properties":{"name":"Luxembourg"},"geometry":{"type":"Polygon","coordinates":[[[6.043073,50.128052],[6.242751,49.902226],[6.18632,49.463803],[5.897759,49.442667],[5.674052,49.529484],[5.782417,50.090328],[6.043073,50.128052]]]}}, +{"type":"Feature","id":"LVA","properties":{"name":"Latvia"},"geometry":{"type":"Polygon","coordinates":[[[21.0558,56.031076],[21.090424,56.783873],[21.581866,57.411871],[22.524341,57.753374],[23.318453,57.006236],[24.12073,57.025693],[24.312863,57.793424],[25.164594,57.970157],[25.60281,57.847529],[26.463532,57.476389],[27.288185,57.474528],[27.770016,57.244258],[27.855282,56.759326],[28.176709,56.16913],[27.10246,55.783314],[26.494331,55.615107],[25.533047,56.100297],[25.000934,56.164531],[24.860684,56.372528],[23.878264,56.273671],[22.201157,56.337802],[21.0558,56.031076]]]}}, +{"type":"Feature","id":"MAR","properties":{"name":"Morocco"},"geometry":{"type":"Polygon","coordinates":[[[-5.193863,35.755182],[-4.591006,35.330712],[-3.640057,35.399855],[-2.604306,35.179093],[-2.169914,35.168396],[-1.792986,34.527919],[-1.733455,33.919713],[-1.388049,32.864015],[-1.124551,32.651522],[-1.307899,32.262889],[-2.616605,32.094346],[-3.06898,31.724498],[-3.647498,31.637294],[-3.690441,30.896952],[-4.859646,30.501188],[-5.242129,30.000443],[-6.060632,29.7317],[-7.059228,29.579228],[-8.674116,28.841289],[-8.66559,27.656426],[-8.817809,27.656426],[-8.817828,27.656426],[-8.794884,27.120696],[-9.413037,27.088476],[-9.735343,26.860945],[-10.189424,26.860945],[-10.551263,26.990808],[-11.392555,26.883424],[-11.71822,26.104092],[-12.030759,26.030866],[-12.500963,24.770116],[-13.89111,23.691009],[-14.221168,22.310163],[-14.630833,21.86094],[-14.750955,21.5006],[-17.002962,21.420734],[-17.020428,21.42231],[-16.973248,21.885745],[-16.589137,22.158234],[-16.261922,22.67934],[-16.326414,23.017768],[-15.982611,23.723358],[-15.426004,24.359134],[-15.089332,24.520261],[-14.824645,25.103533],[-14.800926,25.636265],[-14.43994,26.254418],[-13.773805,26.618892],[-13.139942,27.640148],[-13.121613,27.654148],[-12.618837,28.038186],[-11.688919,28.148644],[-10.900957,28.832142],[-10.399592,29.098586],[-9.564811,29.933574],[-9.814718,31.177736],[-9.434793,32.038096],[-9.300693,32.564679],[-8.657476,33.240245],[-7.654178,33.697065],[-6.912544,34.110476],[-6.244342,35.145865],[-5.929994,35.759988],[-5.193863,35.755182]]]}}, +{"type":"Feature","id":"MDA","properties":{"name":"Moldova"},"geometry":{"type":"Polygon","coordinates":[[[26.619337,48.220726],[26.857824,48.368211],[27.522537,48.467119],[28.259547,48.155562],[28.670891,48.118149],[29.122698,47.849095],[29.050868,47.510227],[29.415135,47.346645],[29.559674,46.928583],[29.908852,46.674361],[29.83821,46.525326],[30.024659,46.423937],[29.759972,46.349988],[29.170654,46.379262],[29.072107,46.517678],[28.862972,46.437889],[28.933717,46.25883],[28.659987,45.939987],[28.485269,45.596907],[28.233554,45.488283],[28.054443,45.944586],[28.160018,46.371563],[28.12803,46.810476],[27.551166,47.405117],[27.233873,47.826771],[26.924176,48.123264],[26.619337,48.220726]]]}}, +{"type":"Feature","id":"MDG","properties":{"name":"Madagascar"},"geometry":{"type":"Polygon","coordinates":[[[49.543519,-12.469833],[49.808981,-12.895285],[50.056511,-13.555761],[50.217431,-14.758789],[50.476537,-15.226512],[50.377111,-15.706069],[50.200275,-16.000263],[49.860606,-15.414253],[49.672607,-15.710204],[49.863344,-16.451037],[49.774564,-16.875042],[49.498612,-17.106036],[49.435619,-17.953064],[49.041792,-19.118781],[48.548541,-20.496888],[47.930749,-22.391501],[47.547723,-23.781959],[47.095761,-24.94163],[46.282478,-25.178463],[45.409508,-25.601434],[44.833574,-25.346101],[44.03972,-24.988345],[43.763768,-24.460677],[43.697778,-23.574116],[43.345654,-22.776904],[43.254187,-22.057413],[43.433298,-21.336475],[43.893683,-21.163307],[43.89637,-20.830459],[44.374325,-20.072366],[44.464397,-19.435454],[44.232422,-18.961995],[44.042976,-18.331387],[43.963084,-17.409945],[44.312469,-16.850496],[44.446517,-16.216219],[44.944937,-16.179374],[45.502732,-15.974373],[45.872994,-15.793454],[46.312243,-15.780018],[46.882183,-15.210182],[47.70513,-14.594303],[48.005215,-14.091233],[47.869047,-13.663869],[48.293828,-13.784068],[48.84506,-13.089175],[48.863509,-12.487868],[49.194651,-12.040557],[49.543519,-12.469833]]]}}, +{"type":"Feature","id":"MEX","properties":{"name":"Mexico"},"geometry":{"type":"Polygon","coordinates":[[[-97.140008,25.869997],[-97.528072,24.992144],[-97.702946,24.272343],[-97.776042,22.93258],[-97.872367,22.444212],[-97.699044,21.898689],[-97.38896,21.411019],[-97.189333,20.635433],[-96.525576,19.890931],[-96.292127,19.320371],[-95.900885,18.828024],[-94.839063,18.562717],[-94.42573,18.144371],[-93.548651,18.423837],[-92.786114,18.524839],[-92.037348,18.704569],[-91.407903,18.876083],[-90.77187,19.28412],[-90.53359,19.867418],[-90.451476,20.707522],[-90.278618,20.999855],[-89.601321,21.261726],[-88.543866,21.493675],[-87.658417,21.458846],[-87.05189,21.543543],[-86.811982,21.331515],[-86.845908,20.849865],[-87.383291,20.255405],[-87.621054,19.646553],[-87.43675,19.472403],[-87.58656,19.04013],[-87.837191,18.259816],[-88.090664,18.516648],[-88.300031,18.499982],[-88.490123,18.486831],[-88.848344,17.883198],[-89.029857,18.001511],[-89.150909,17.955468],[-89.14308,17.808319],[-90.067934,17.819326],[-91.00152,17.817595],[-91.002269,17.254658],[-91.453921,17.252177],[-91.08167,16.918477],[-90.711822,16.687483],[-90.600847,16.470778],[-90.438867,16.41011],[-90.464473,16.069562],[-91.74796,16.066565],[-92.229249,15.251447],[-92.087216,15.064585],[-92.20323,14.830103],[-92.22775,14.538829],[-93.359464,15.61543],[-93.875169,15.940164],[-94.691656,16.200975],[-95.250227,16.128318],[-96.053382,15.752088],[-96.557434,15.653515],[-97.263592,15.917065],[-98.01303,16.107312],[-98.947676,16.566043],[-99.697397,16.706164],[-100.829499,17.171071],[-101.666089,17.649026],[-101.918528,17.91609],[-102.478132,17.975751],[-103.50099,18.292295],[-103.917527,18.748572],[-104.99201,19.316134],[-105.493038,19.946767],[-105.731396,20.434102],[-105.397773,20.531719],[-105.500661,20.816895],[-105.270752,21.076285],[-105.265817,21.422104],[-105.603161,21.871146],[-105.693414,22.26908],[-106.028716,22.773752],[-106.90998,23.767774],[-107.915449,24.548915],[-108.401905,25.172314],[-109.260199,25.580609],[-109.444089,25.824884],[-109.291644,26.442934],[-109.801458,26.676176],[-110.391732,27.162115],[-110.641019,27.859876],[-111.178919,27.941241],[-111.759607,28.467953],[-112.228235,28.954409],[-112.271824,29.266844],[-112.809594,30.021114],[-113.163811,30.786881],[-113.148669,31.170966],[-113.871881,31.567608],[-114.205737,31.524045],[-114.776451,31.799532],[-114.9367,31.393485],[-114.771232,30.913617],[-114.673899,30.162681],[-114.330974,29.750432],[-113.588875,29.061611],[-113.424053,28.826174],[-113.271969,28.754783],[-113.140039,28.411289],[-112.962298,28.42519],[-112.761587,27.780217],[-112.457911,27.525814],[-112.244952,27.171727],[-111.616489,26.662817],[-111.284675,25.73259],[-110.987819,25.294606],[-110.710007,24.826004],[-110.655049,24.298595],[-110.172856,24.265548],[-109.771847,23.811183],[-109.409104,23.364672],[-109.433392,23.185588],[-109.854219,22.818272],[-110.031392,22.823078],[-110.295071,23.430973],[-110.949501,24.000964],[-111.670568,24.484423],[-112.182036,24.738413],[-112.148989,25.470125],[-112.300711,26.012004],[-112.777297,26.32196],[-113.464671,26.768186],[-113.59673,26.63946],[-113.848937,26.900064],[-114.465747,27.14209],[-115.055142,27.722727],[-114.982253,27.7982],[-114.570366,27.741485],[-114.199329,28.115003],[-114.162018,28.566112],[-114.931842,29.279479],[-115.518654,29.556362],[-115.887365,30.180794],[-116.25835,30.836464],[-116.721526,31.635744],[-117.12776,32.53534],[-115.99135,32.61239],[-114.72139,32.72083],[-114.815,32.52528],[-113.30498,32.03914],[-111.02361,31.33472],[-109.035,31.34194],[-108.24194,31.34222],[-108.24,31.754854],[-106.50759,31.75452],[-106.1429,31.39995],[-105.63159,31.08383],[-105.03737,30.64402],[-104.70575,30.12173],[-104.45697,29.57196],[-103.94,29.27],[-103.11,28.97],[-102.48,29.76],[-101.6624,29.7793],[-100.9576,29.38071],[-100.45584,28.69612],[-100.11,28.11],[-99.52,27.54],[-99.3,26.84],[-99.02,26.37],[-98.24,26.06],[-97.53,25.84],[-97.140008,25.869997]]]}}, +{"type":"Feature","id":"MKD","properties":{"name":"Macedonia"},"geometry":{"type":"Polygon","coordinates":[[[20.59023,41.85541],[20.71731,41.84711],[20.76216,42.05186],[21.3527,42.2068],[21.576636,42.245224],[21.91708,42.30364],[22.380526,42.32026],[22.881374,41.999297],[22.952377,41.337994],[22.76177,41.3048],[22.597308,41.130487],[22.055378,41.149866],[21.674161,40.931275],[21.02004,40.842727],[20.60518,41.08622],[20.46315,41.51509],[20.59023,41.85541]]]}}, +{"type":"Feature","id":"MLI","properties":{"name":"Mali"},"geometry":{"type":"Polygon","coordinates":[[[-12.17075,14.616834],[-11.834208,14.799097],[-11.666078,15.388208],[-11.349095,15.411256],[-10.650791,15.132746],[-10.086846,15.330486],[-9.700255,15.264107],[-9.550238,15.486497],[-5.537744,15.50169],[-5.315277,16.201854],[-5.488523,16.325102],[-5.971129,20.640833],[-6.453787,24.956591],[-4.923337,24.974574],[-1.550055,22.792666],[1.823228,20.610809],[2.060991,20.142233],[2.683588,19.85623],[3.146661,19.693579],[3.158133,19.057364],[4.267419,19.155265],[4.27021,16.852227],[3.723422,16.184284],[3.638259,15.56812],[2.749993,15.409525],[1.385528,15.323561],[1.015783,14.968182],[0.374892,14.928908],[-0.266257,14.924309],[-0.515854,15.116158],[-1.066363,14.973815],[-2.001035,14.559008],[-2.191825,14.246418],[-2.967694,13.79815],[-3.103707,13.541267],[-3.522803,13.337662],[-4.006391,13.472485],[-4.280405,13.228444],[-4.427166,12.542646],[-5.220942,11.713859],[-5.197843,11.375146],[-5.470565,10.95127],[-5.404342,10.370737],[-5.816926,10.222555],[-6.050452,10.096361],[-6.205223,10.524061],[-6.493965,10.411303],[-6.666461,10.430811],[-6.850507,10.138994],[-7.622759,10.147236],[-7.89959,10.297382],[-8.029944,10.206535],[-8.335377,10.494812],[-8.282357,10.792597],[-8.407311,10.909257],[-8.620321,10.810891],[-8.581305,11.136246],[-8.376305,11.393646],[-8.786099,11.812561],[-8.905265,12.088358],[-9.127474,12.30806],[-9.327616,12.334286],[-9.567912,12.194243],[-9.890993,12.060479],[-10.165214,11.844084],[-10.593224,11.923975],[-10.87083,12.177887],[-11.036556,12.211245],[-11.297574,12.077971],[-11.456169,12.076834],[-11.513943,12.442988],[-11.467899,12.754519],[-11.553398,13.141214],[-11.927716,13.422075],[-12.124887,13.994727],[-12.17075,14.616834]]]}}, +{"type":"Feature","id":"MMR","properties":{"name":"Myanmar"},"geometry":{"type":"Polygon","coordinates":[[[99.543309,20.186598],[98.959676,19.752981],[98.253724,19.708203],[97.797783,18.62708],[97.375896,18.445438],[97.859123,17.567946],[98.493761,16.837836],[98.903348,16.177824],[98.537376,15.308497],[98.192074,15.123703],[98.430819,14.622028],[99.097755,13.827503],[99.212012,13.269294],[99.196354,12.804748],[99.587286,11.892763],[99.038121,10.960546],[98.553551,9.93296],[98.457174,10.675266],[98.764546,11.441292],[98.428339,12.032987],[98.509574,13.122378],[98.103604,13.64046],[97.777732,14.837286],[97.597072,16.100568],[97.16454,16.928734],[96.505769,16.427241],[95.369352,15.71439],[94.808405,15.803454],[94.188804,16.037936],[94.533486,17.27724],[94.324817,18.213514],[93.540988,19.366493],[93.663255,19.726962],[93.078278,19.855145],[92.368554,20.670883],[92.303234,21.475485],[92.652257,21.324048],[92.672721,22.041239],[93.166128,22.27846],[93.060294,22.703111],[93.286327,23.043658],[93.325188,24.078556],[94.106742,23.850741],[94.552658,24.675238],[94.603249,25.162495],[95.155153,26.001307],[95.124768,26.573572],[96.419366,27.264589],[97.133999,27.083774],[97.051989,27.699059],[97.402561,27.882536],[97.327114,28.261583],[97.911988,28.335945],[98.246231,27.747221],[98.68269,27.508812],[98.712094,26.743536],[98.671838,25.918703],[97.724609,25.083637],[97.60472,23.897405],[98.660262,24.063286],[98.898749,23.142722],[99.531992,22.949039],[99.240899,22.118314],[99.983489,21.742937],[100.416538,21.558839],[101.150033,21.849984],[101.180005,21.436573],[100.329101,20.786122],[100.115988,20.41785],[99.543309,20.186598]]]}}, +{"type":"Feature","id":"MNE","properties":{"name":"Montenegro"},"geometry":{"type":"Polygon","coordinates":[[[19.801613,42.500093],[19.738051,42.688247],[19.30449,42.19574],[19.37177,41.87755],[19.16246,41.95502],[18.88214,42.28151],[18.45,42.48],[18.56,42.65],[18.70648,43.20011],[19.03165,43.43253],[19.21852,43.52384],[19.48389,43.35229],[19.63,43.21378],[19.95857,43.10604],[20.3398,42.89852],[20.25758,42.81275],[20.0707,42.58863],[19.801613,42.500093]]]}}, +{"type":"Feature","id":"MNG","properties":{"name":"Mongolia"},"geometry":{"type":"Polygon","coordinates":[[[87.751264,49.297198],[88.805567,49.470521],[90.713667,50.331812],[92.234712,50.802171],[93.104219,50.49529],[94.147566,50.480537],[94.815949,50.013433],[95.814028,49.977467],[97.259728,49.726061],[98.231762,50.422401],[97.82574,51.010995],[98.861491,52.047366],[99.981732,51.634006],[100.88948,51.516856],[102.065223,51.259921],[102.255909,50.510561],[103.676545,50.089966],[104.621552,50.275329],[105.886591,50.406019],[106.888804,50.274296],[107.868176,49.793705],[108.475167,49.282548],[109.402449,49.292961],[110.662011,49.130128],[111.581231,49.377968],[112.89774,49.543565],[114.362456,50.248303],[114.96211,50.140247],[115.485695,49.805177],[116.678801,49.888531],[116.191802,49.134598],[115.485282,48.135383],[115.742837,47.726545],[116.308953,47.85341],[117.295507,47.697709],[118.064143,48.06673],[118.866574,47.74706],[119.772824,47.048059],[119.66327,46.69268],[118.874326,46.805412],[117.421701,46.672733],[116.717868,46.388202],[115.985096,45.727235],[114.460332,45.339817],[113.463907,44.808893],[112.436062,45.011646],[111.873306,45.102079],[111.348377,44.457442],[111.667737,44.073176],[111.829588,43.743118],[111.129682,43.406834],[110.412103,42.871234],[109.243596,42.519446],[107.744773,42.481516],[106.129316,42.134328],[104.964994,41.59741],[104.522282,41.908347],[103.312278,41.907468],[101.83304,42.514873],[100.845866,42.663804],[99.515817,42.524691],[97.451757,42.74889],[96.349396,42.725635],[95.762455,43.319449],[95.306875,44.241331],[94.688929,44.352332],[93.480734,44.975472],[92.133891,45.115076],[90.94554,45.286073],[90.585768,45.719716],[90.970809,46.888146],[90.280826,47.693549],[88.854298,48.069082],[88.013832,48.599463],[87.751264,49.297198]]]}}, +{"type":"Feature","id":"MOZ","properties":{"name":"Mozambique"},"geometry":{"type":"Polygon","coordinates":[[[34.559989,-11.52002],[35.312398,-11.439146],[36.514082,-11.720938],[36.775151,-11.594537],[37.471284,-11.568751],[37.827645,-11.268769],[38.427557,-11.285202],[39.52103,-10.896854],[40.316589,-10.317096],[40.478387,-10.765441],[40.437253,-11.761711],[40.560811,-12.639177],[40.59962,-14.201975],[40.775475,-14.691764],[40.477251,-15.406294],[40.089264,-16.100774],[39.452559,-16.720891],[38.538351,-17.101023],[37.411133,-17.586368],[36.281279,-18.659688],[35.896497,-18.84226],[35.1984,-19.552811],[34.786383,-19.784012],[34.701893,-20.497043],[35.176127,-21.254361],[35.373428,-21.840837],[35.385848,-22.14],[35.562546,-22.09],[35.533935,-23.070788],[35.371774,-23.535359],[35.60747,-23.706563],[35.458746,-24.12261],[35.040735,-24.478351],[34.215824,-24.816314],[33.01321,-25.357573],[32.574632,-25.727318],[32.660363,-26.148584],[32.915955,-26.215867],[32.83012,-26.742192],[32.071665,-26.73382],[31.985779,-26.29178],[31.837778,-25.843332],[31.752408,-25.484284],[31.930589,-24.369417],[31.670398,-23.658969],[31.191409,-22.25151],[32.244988,-21.116489],[32.508693,-20.395292],[32.659743,-20.30429],[32.772708,-19.715592],[32.611994,-19.419383],[32.654886,-18.67209],[32.849861,-17.979057],[32.847639,-16.713398],[32.328239,-16.392074],[31.852041,-16.319417],[31.636498,-16.07199],[31.173064,-15.860944],[30.338955,-15.880839],[30.274256,-15.507787],[30.179481,-14.796099],[33.214025,-13.97186],[33.7897,-14.451831],[34.064825,-14.35995],[34.459633,-14.61301],[34.517666,-15.013709],[34.307291,-15.478641],[34.381292,-16.18356],[35.03381,-16.8013],[35.339063,-16.10744],[35.771905,-15.896859],[35.686845,-14.611046],[35.267956,-13.887834],[34.907151,-13.565425],[34.559989,-13.579998],[34.280006,-12.280025],[34.559989,-11.52002]]]}}, +{"type":"Feature","id":"MRT","properties":{"name":"Mauritania"},"geometry":{"type":"Polygon","coordinates":[[[-12.17075,14.616834],[-12.830658,15.303692],[-13.435738,16.039383],[-14.099521,16.304302],[-14.577348,16.598264],[-15.135737,16.587282],[-15.623666,16.369337],[-16.12069,16.455663],[-16.463098,16.135036],[-16.549708,16.673892],[-16.270552,17.166963],[-16.146347,18.108482],[-16.256883,19.096716],[-16.377651,19.593817],[-16.277838,20.092521],[-16.536324,20.567866],[-17.063423,20.999752],[-16.845194,21.333323],[-12.929102,21.327071],[-13.118754,22.77122],[-12.874222,23.284832],[-11.937224,23.374594],[-11.969419,25.933353],[-8.687294,25.881056],[-8.6844,27.395744],[-4.923337,24.974574],[-6.453787,24.956591],[-5.971129,20.640833],[-5.488523,16.325102],[-5.315277,16.201854],[-5.537744,15.50169],[-9.550238,15.486497],[-9.700255,15.264107],[-10.086846,15.330486],[-10.650791,15.132746],[-11.349095,15.411256],[-11.666078,15.388208],[-11.834208,14.799097],[-12.17075,14.616834]]]}}, +{"type":"Feature","id":"MWI","properties":{"name":"Malawi"},"geometry":{"type":"Polygon","coordinates":[[[34.559989,-11.52002],[34.280006,-12.280025],[34.559989,-13.579998],[34.907151,-13.565425],[35.267956,-13.887834],[35.686845,-14.611046],[35.771905,-15.896859],[35.339063,-16.10744],[35.03381,-16.8013],[34.381292,-16.18356],[34.307291,-15.478641],[34.517666,-15.013709],[34.459633,-14.61301],[34.064825,-14.35995],[33.7897,-14.451831],[33.214025,-13.97186],[32.688165,-13.712858],[32.991764,-12.783871],[33.306422,-12.435778],[33.114289,-11.607198],[33.31531,-10.79655],[33.485688,-10.525559],[33.231388,-9.676722],[32.759375,-9.230599],[33.739729,-9.417151],[33.940838,-9.693674],[34.280006,-10.16],[34.559989,-11.52002]]]}}, +{"type":"Feature","id":"MYS","properties":{"name":"Malaysia"},"geometry":{"type":"MultiPolygon","coordinates":[[[[101.075516,6.204867],[101.154219,5.691384],[101.814282,5.810808],[102.141187,6.221636],[102.371147,6.128205],[102.961705,5.524495],[103.381215,4.855001],[103.438575,4.181606],[103.332122,3.726698],[103.429429,3.382869],[103.502448,2.791019],[103.854674,2.515454],[104.247932,1.631141],[104.228811,1.293048],[103.519707,1.226334],[102.573615,1.967115],[101.390638,2.760814],[101.27354,3.270292],[100.695435,3.93914],[100.557408,4.76728],[100.196706,5.312493],[100.30626,6.040562],[100.085757,6.464489],[100.259596,6.642825],[101.075516,6.204867]]],[[[118.618321,4.478202],[117.882035,4.137551],[117.015214,4.306094],[115.865517,4.306559],[115.519078,3.169238],[115.134037,2.821482],[114.621355,1.430688],[113.80585,1.217549],[112.859809,1.49779],[112.380252,1.410121],[111.797548,0.904441],[111.159138,0.976478],[110.514061,0.773131],[109.830227,1.338136],[109.66326,2.006467],[110.396135,1.663775],[111.168853,1.850637],[111.370081,2.697303],[111.796928,2.885897],[112.995615,3.102395],[113.712935,3.893509],[114.204017,4.525874],[114.659596,4.007637],[114.869557,4.348314],[115.347461,4.316636],[115.4057,4.955228],[115.45071,5.44773],[116.220741,6.143191],[116.725103,6.924771],[117.129626,6.928053],[117.643393,6.422166],[117.689075,5.98749],[118.347691,5.708696],[119.181904,5.407836],[119.110694,5.016128],[118.439727,4.966519],[118.618321,4.478202]]]]}}, +{"type":"Feature","id":"NAM","properties":{"name":"Namibia"},"geometry":{"type":"Polygon","coordinates":[[[16.344977,-28.576705],[15.601818,-27.821247],[15.210472,-27.090956],[14.989711,-26.117372],[14.743214,-25.39292],[14.408144,-23.853014],[14.385717,-22.656653],[14.257714,-22.111208],[13.868642,-21.699037],[13.352498,-20.872834],[12.826845,-19.673166],[12.608564,-19.045349],[11.794919,-18.069129],[11.734199,-17.301889],[12.215461,-17.111668],[12.814081,-16.941343],[13.462362,-16.971212],[14.058501,-17.423381],[14.209707,-17.353101],[18.263309,-17.309951],[18.956187,-17.789095],[21.377176,-17.930636],[23.215048,-17.523116],[24.033862,-17.295843],[24.682349,-17.353411],[25.07695,-17.578823],[25.084443,-17.661816],[24.520705,-17.887125],[24.217365,-17.889347],[23.579006,-18.281261],[23.196858,-17.869038],[21.65504,-18.219146],[20.910641,-18.252219],[20.881134,-21.814327],[19.895458,-21.849157],[19.895768,-24.76779],[19.894734,-28.461105],[19.002127,-28.972443],[18.464899,-29.045462],[17.836152,-28.856378],[17.387497,-28.783514],[17.218929,-28.355943],[16.824017,-28.082162],[16.344977,-28.576705]]]}}, +{"type":"Feature","id":"NCL","properties":{"name":"New Caledonia"},"geometry":{"type":"Polygon","coordinates":[[[165.77999,-21.080005],[166.599991,-21.700019],[167.120011,-22.159991],[166.740035,-22.399976],[166.189732,-22.129708],[165.474375,-21.679607],[164.829815,-21.14982],[164.167995,-20.444747],[164.029606,-20.105646],[164.459967,-20.120012],[165.020036,-20.459991],[165.460009,-20.800022],[165.77999,-21.080005]]]}}, +{"type":"Feature","id":"NER","properties":{"name":"Niger"},"geometry":{"type":"Polygon","coordinates":[[[2.154474,11.94015],[2.177108,12.625018],[1.024103,12.851826],[0.993046,13.33575],[0.429928,13.988733],[0.295646,14.444235],[0.374892,14.928908],[1.015783,14.968182],[1.385528,15.323561],[2.749993,15.409525],[3.638259,15.56812],[3.723422,16.184284],[4.27021,16.852227],[4.267419,19.155265],[5.677566,19.601207],[8.572893,21.565661],[11.999506,23.471668],[13.581425,23.040506],[14.143871,22.491289],[14.8513,22.86295],[15.096888,21.308519],[15.471077,21.048457],[15.487148,20.730415],[15.903247,20.387619],[15.685741,19.95718],[15.300441,17.92795],[15.247731,16.627306],[13.972202,15.684366],[13.540394,14.367134],[13.956699,13.996691],[13.954477,13.353449],[14.595781,13.330427],[14.495787,12.859396],[14.213531,12.802035],[14.181336,12.483657],[13.995353,12.461565],[13.318702,13.556356],[13.083987,13.596147],[12.302071,13.037189],[11.527803,13.32898],[10.989593,13.387323],[10.701032,13.246918],[10.114814,13.277252],[9.524928,12.851102],[9.014933,12.826659],[7.804671,13.343527],[7.330747,13.098038],[6.820442,13.115091],[6.445426,13.492768],[5.443058,13.865924],[4.368344,13.747482],[4.107946,13.531216],[3.967283,12.956109],[3.680634,12.552903],[3.61118,11.660167],[2.848643,12.235636],[2.490164,12.233052],[2.154474,11.94015]]]}}, +{"type":"Feature","id":"NGA","properties":{"name":"Nigeria"},"geometry":{"type":"Polygon","coordinates":[[[8.500288,4.771983],[7.462108,4.412108],[7.082596,4.464689],[6.698072,4.240594],[5.898173,4.262453],[5.362805,4.887971],[5.033574,5.611802],[4.325607,6.270651],[3.57418,6.2583],[2.691702,6.258817],[2.749063,7.870734],[2.723793,8.506845],[2.912308,9.137608],[3.220352,9.444153],[3.705438,10.06321],[3.60007,10.332186],[3.797112,10.734746],[3.572216,11.327939],[3.61118,11.660167],[3.680634,12.552903],[3.967283,12.956109],[4.107946,13.531216],[4.368344,13.747482],[5.443058,13.865924],[6.445426,13.492768],[6.820442,13.115091],[7.330747,13.098038],[7.804671,13.343527],[9.014933,12.826659],[9.524928,12.851102],[10.114814,13.277252],[10.701032,13.246918],[10.989593,13.387323],[11.527803,13.32898],[12.302071,13.037189],[13.083987,13.596147],[13.318702,13.556356],[13.995353,12.461565],[14.181336,12.483657],[14.577178,12.085361],[14.468192,11.904752],[14.415379,11.572369],[13.57295,10.798566],[13.308676,10.160362],[13.1676,9.640626],[12.955468,9.417772],[12.753672,8.717763],[12.218872,8.305824],[12.063946,7.799808],[11.839309,7.397042],[11.745774,6.981383],[11.058788,6.644427],[10.497375,7.055358],[10.118277,7.03877],[9.522706,6.453482],[9.233163,6.444491],[8.757533,5.479666],[8.500288,4.771983]]]}}, +{"type":"Feature","id":"NIC","properties":{"name":"Nicaragua"},"geometry":{"type":"Polygon","coordinates":[[[-85.71254,11.088445],[-86.058488,11.403439],[-86.52585,11.806877],[-86.745992,12.143962],[-87.167516,12.458258],[-87.668493,12.90991],[-87.557467,13.064552],[-87.392386,12.914018],[-87.316654,12.984686],[-87.005769,13.025794],[-86.880557,13.254204],[-86.733822,13.263093],[-86.755087,13.754845],[-86.520708,13.778487],[-86.312142,13.771356],[-86.096264,14.038187],[-85.801295,13.836055],[-85.698665,13.960078],[-85.514413,14.079012],[-85.165365,14.35437],[-85.148751,14.560197],[-85.052787,14.551541],[-84.924501,14.790493],[-84.820037,14.819587],[-84.649582,14.666805],[-84.449336,14.621614],[-84.228342,14.748764],[-83.975721,14.749436],[-83.628585,14.880074],[-83.489989,15.016267],[-83.147219,14.995829],[-83.233234,14.899866],[-83.284162,14.676624],[-83.182126,14.310703],[-83.4125,13.970078],[-83.519832,13.567699],[-83.552207,13.127054],[-83.498515,12.869292],[-83.473323,12.419087],[-83.626104,12.32085],[-83.719613,11.893124],[-83.650858,11.629032],[-83.85547,11.373311],[-83.808936,11.103044],[-83.655612,10.938764],[-83.895054,10.726839],[-84.190179,10.79345],[-84.355931,10.999226],[-84.673069,11.082657],[-84.903003,10.952303],[-85.561852,11.217119],[-85.71254,11.088445]]]}}, +{"type":"Feature","id":"NLD","properties":{"name":"Netherlands"},"geometry":{"type":"Polygon","coordinates":[[[6.074183,53.510403],[6.90514,53.482162],[7.092053,53.144043],[6.84287,52.22844],[6.589397,51.852029],[5.988658,51.851616],[6.156658,50.803721],[5.606976,51.037298],[4.973991,51.475024],[4.047071,51.267259],[3.314971,51.345755],[3.830289,51.620545],[4.705997,53.091798],[6.074183,53.510403]]]}}, +{"type":"Feature","id":"NOR","properties":{"name":"Norway"},"geometry":{"type":"MultiPolygon","coordinates":[[[[28.165547,71.185474],[31.293418,70.453788],[30.005435,70.186259],[31.101079,69.55808],[29.399581,69.156916],[28.59193,69.064777],[29.015573,69.766491],[27.732292,70.164193],[26.179622,69.825299],[25.689213,69.092114],[24.735679,68.649557],[23.66205,68.891247],[22.356238,68.841741],[21.244936,69.370443],[20.645593,69.106247],[20.025269,69.065139],[19.87856,68.407194],[17.993868,68.567391],[17.729182,68.010552],[16.768879,68.013937],[16.108712,67.302456],[15.108411,66.193867],[13.55569,64.787028],[13.919905,64.445421],[13.571916,64.049114],[12.579935,64.066219],[11.930569,63.128318],[11.992064,61.800362],[12.631147,61.293572],[12.300366,60.117933],[11.468272,59.432393],[11.027369,58.856149],[10.356557,59.469807],[8.382,58.313288],[7.048748,58.078884],[5.665835,58.588155],[5.308234,59.663232],[4.992078,61.970998],[5.9129,62.614473],[8.553411,63.454008],[10.527709,64.486038],[12.358347,65.879726],[14.761146,67.810642],[16.435927,68.563205],[19.184028,69.817444],[21.378416,70.255169],[23.023742,70.202072],[24.546543,71.030497],[26.37005,70.986262],[28.165547,71.185474]]],[[[24.72412,77.85385],[22.49032,77.44493],[20.72601,77.67704],[21.41611,77.93504],[20.8119,78.25463],[22.88426,78.45494],[23.28134,78.07954],[24.72412,77.85385]]],[[[18.25183,79.70175],[21.54383,78.95611],[19.02737,78.5626],[18.47172,77.82669],[17.59441,77.63796],[17.1182,76.80941],[15.91315,76.77045],[13.76259,77.38035],[14.66956,77.73565],[13.1706,78.02493],[11.22231,78.8693],[10.44453,79.65239],[13.17077,80.01046],[13.71852,79.66039],[15.14282,79.67431],[15.52255,80.01608],[16.99085,80.05086],[18.25183,79.70175]]],[[[25.447625,80.40734],[27.407506,80.056406],[25.924651,79.517834],[23.024466,79.400012],[20.075188,79.566823],[19.897266,79.842362],[18.462264,79.85988],[17.368015,80.318896],[20.455992,80.598156],[21.907945,80.357679],[22.919253,80.657144],[25.447625,80.40734]]]]}}, +{"type":"Feature","id":"NPL","properties":{"name":"Nepal"},"geometry":{"type":"Polygon","coordinates":[[[88.120441,27.876542],[88.043133,27.445819],[88.174804,26.810405],[88.060238,26.414615],[87.227472,26.397898],[86.024393,26.630985],[85.251779,26.726198],[84.675018,27.234901],[83.304249,27.364506],[81.999987,27.925479],[81.057203,28.416095],[80.088425,28.79447],[80.476721,29.729865],[81.111256,30.183481],[81.525804,30.422717],[82.327513,30.115268],[83.337115,29.463732],[83.898993,29.320226],[84.23458,28.839894],[85.011638,28.642774],[85.82332,28.203576],[86.954517,27.974262],[88.120441,27.876542]]]}}, +{"type":"Feature","id":"NZL","properties":{"name":"New Zealand"},"geometry":{"type":"MultiPolygon","coordinates":[[[[173.020375,-40.919052],[173.247234,-41.331999],[173.958405,-40.926701],[174.247587,-41.349155],[174.248517,-41.770008],[173.876447,-42.233184],[173.22274,-42.970038],[172.711246,-43.372288],[173.080113,-43.853344],[172.308584,-43.865694],[171.452925,-44.242519],[171.185138,-44.897104],[170.616697,-45.908929],[169.831422,-46.355775],[169.332331,-46.641235],[168.411354,-46.619945],[167.763745,-46.290197],[166.676886,-46.219917],[166.509144,-45.852705],[167.046424,-45.110941],[168.303763,-44.123973],[168.949409,-43.935819],[169.667815,-43.555326],[170.52492,-43.031688],[171.12509,-42.512754],[171.569714,-41.767424],[171.948709,-41.514417],[172.097227,-40.956104],[172.79858,-40.493962],[173.020375,-40.919052]]],[[[174.612009,-36.156397],[175.336616,-37.209098],[175.357596,-36.526194],[175.808887,-36.798942],[175.95849,-37.555382],[176.763195,-37.881253],[177.438813,-37.961248],[178.010354,-37.579825],[178.517094,-37.695373],[178.274731,-38.582813],[177.97046,-39.166343],[177.206993,-39.145776],[176.939981,-39.449736],[177.032946,-39.879943],[176.885824,-40.065978],[176.508017,-40.604808],[176.01244,-41.289624],[175.239567,-41.688308],[175.067898,-41.425895],[174.650973,-41.281821],[175.22763,-40.459236],[174.900157,-39.908933],[173.824047,-39.508854],[173.852262,-39.146602],[174.574802,-38.797683],[174.743474,-38.027808],[174.697017,-37.381129],[174.292028,-36.711092],[174.319004,-36.534824],[173.840997,-36.121981],[173.054171,-35.237125],[172.636005,-34.529107],[173.007042,-34.450662],[173.551298,-35.006183],[174.32939,-35.265496],[174.612009,-36.156397]]]]}}, +{"type":"Feature","id":"OMN","properties":{"name":"Oman"},"geometry":{"type":"MultiPolygon","coordinates":[[[[58.861141,21.114035],[58.487986,20.428986],[58.034318,20.481437],[57.826373,20.243002],[57.665762,19.736005],[57.7887,19.06757],[57.694391,18.94471],[57.234264,18.947991],[56.609651,18.574267],[56.512189,18.087113],[56.283521,17.876067],[55.661492,17.884128],[55.269939,17.632309],[55.2749,17.228354],[54.791002,16.950697],[54.239253,17.044981],[53.570508,16.707663],[53.108573,16.651051],[52.782184,17.349742],[52.00001,19.000003],[54.999982,19.999994],[55.666659,22.000001],[55.208341,22.70833],[55.234489,23.110993],[55.525841,23.524869],[55.528632,23.933604],[55.981214,24.130543],[55.804119,24.269604],[55.886233,24.920831],[56.396847,24.924732],[56.84514,24.241673],[57.403453,23.878594],[58.136948,23.747931],[58.729211,23.565668],[59.180502,22.992395],[59.450098,22.660271],[59.80806,22.533612],[59.806148,22.310525],[59.442191,21.714541],[59.282408,21.433886],[58.861141,21.114035]]],[[[56.391421,25.895991],[56.261042,25.714606],[56.070821,26.055464],[56.362017,26.395934],[56.485679,26.309118],[56.391421,25.895991]]]]}}, +{"type":"Feature","id":"PAK","properties":{"name":"Pakistan"},"geometry":{"type":"Polygon","coordinates":[[[75.158028,37.133031],[75.896897,36.666806],[76.192848,35.898403],[77.837451,35.49401],[76.871722,34.653544],[75.757061,34.504923],[74.240203,34.748887],[73.749948,34.317699],[74.104294,33.441473],[74.451559,32.7649],[75.258642,32.271105],[74.405929,31.692639],[74.42138,30.979815],[73.450638,29.976413],[72.823752,28.961592],[71.777666,27.91318],[70.616496,27.989196],[69.514393,26.940966],[70.168927,26.491872],[70.282873,25.722229],[70.844699,25.215102],[71.04324,24.356524],[68.842599,24.359134],[68.176645,23.691965],[67.443667,23.944844],[67.145442,24.663611],[66.372828,25.425141],[64.530408,25.237039],[62.905701,25.218409],[61.497363,25.078237],[61.874187,26.239975],[63.316632,26.756532],[63.233898,27.217047],[62.755426,27.378923],[62.72783,28.259645],[61.771868,28.699334],[61.369309,29.303276],[60.874248,29.829239],[62.549857,29.318572],[63.550261,29.468331],[64.148002,29.340819],[64.350419,29.560031],[65.046862,29.472181],[66.346473,29.887943],[66.381458,30.738899],[66.938891,31.304911],[67.683394,31.303154],[67.792689,31.58293],[68.556932,31.71331],[68.926677,31.620189],[69.317764,31.901412],[69.262522,32.501944],[69.687147,33.105499],[70.323594,33.358533],[69.930543,34.02012],[70.881803,33.988856],[71.156773,34.348911],[71.115019,34.733126],[71.613076,35.153203],[71.498768,35.650563],[71.262348,36.074388],[71.846292,36.509942],[72.920025,36.720007],[74.067552,36.836176],[74.575893,37.020841],[75.158028,37.133031]]]}}, +{"type":"Feature","id":"PAN","properties":{"name":"Panama"},"geometry":{"type":"Polygon","coordinates":[[[-77.881571,7.223771],[-78.214936,7.512255],[-78.429161,8.052041],[-78.182096,8.319182],[-78.435465,8.387705],[-78.622121,8.718124],[-79.120307,8.996092],[-79.557877,8.932375],[-79.760578,8.584515],[-80.164481,8.333316],[-80.382659,8.298409],[-80.480689,8.090308],[-80.00369,7.547524],[-80.276671,7.419754],[-80.421158,7.271572],[-80.886401,7.220541],[-81.059543,7.817921],[-81.189716,7.647906],[-81.519515,7.70661],[-81.721311,8.108963],[-82.131441,8.175393],[-82.390934,8.292362],[-82.820081,8.290864],[-82.850958,8.073823],[-82.965783,8.225028],[-82.913176,8.423517],[-82.829771,8.626295],[-82.868657,8.807266],[-82.719183,8.925709],[-82.927155,9.07433],[-82.932891,9.476812],[-82.546196,9.566135],[-82.187123,9.207449],[-82.207586,8.995575],[-81.808567,8.950617],[-81.714154,9.031955],[-81.439287,8.786234],[-80.947302,8.858504],[-80.521901,9.111072],[-79.9146,9.312765],[-79.573303,9.61161],[-79.021192,9.552931],[-79.05845,9.454565],[-78.500888,9.420459],[-78.055928,9.24773],[-77.729514,8.946844],[-77.353361,8.670505],[-77.474723,8.524286],[-77.242566,7.935278],[-77.431108,7.638061],[-77.753414,7.70984],[-77.881571,7.223771]]]}}, +{"type":"Feature","id":"PER","properties":{"name":"Peru"},"geometry":{"type":"Polygon","coordinates":[[[-69.590424,-17.580012],[-69.858444,-18.092694],[-70.372572,-18.347975],[-71.37525,-17.773799],[-71.462041,-17.363488],[-73.44453,-16.359363],[-75.237883,-15.265683],[-76.009205,-14.649286],[-76.423469,-13.823187],[-76.259242,-13.535039],[-77.106192,-12.222716],[-78.092153,-10.377712],[-79.036953,-8.386568],[-79.44592,-7.930833],[-79.760578,-7.194341],[-80.537482,-6.541668],[-81.249996,-6.136834],[-80.926347,-5.690557],[-81.410943,-4.736765],[-81.09967,-4.036394],[-80.302561,-3.404856],[-80.184015,-3.821162],[-80.469295,-4.059287],[-80.442242,-4.425724],[-80.028908,-4.346091],[-79.624979,-4.454198],[-79.205289,-4.959129],[-78.639897,-4.547784],[-78.450684,-3.873097],[-77.837905,-3.003021],[-76.635394,-2.608678],[-75.544996,-1.56161],[-75.233723,-0.911417],[-75.373223,-0.152032],[-75.106625,-0.057205],[-74.441601,-0.53082],[-74.122395,-1.002833],[-73.659504,-1.260491],[-73.070392,-2.308954],[-72.325787,-2.434218],[-71.774761,-2.16979],[-71.413646,-2.342802],[-70.813476,-2.256865],[-70.047709,-2.725156],[-70.692682,-3.742872],[-70.394044,-3.766591],[-69.893635,-4.298187],[-70.794769,-4.251265],[-70.928843,-4.401591],[-71.748406,-4.593983],[-72.891928,-5.274561],[-72.964507,-5.741251],[-73.219711,-6.089189],[-73.120027,-6.629931],[-73.724487,-6.918595],[-73.723401,-7.340999],[-73.987235,-7.52383],[-73.571059,-8.424447],[-73.015383,-9.032833],[-73.226713,-9.462213],[-72.563033,-9.520194],[-72.184891,-10.053598],[-71.302412,-10.079436],[-70.481894,-9.490118],[-70.548686,-11.009147],[-70.093752,-11.123972],[-69.529678,-10.951734],[-68.66508,-12.5613],[-68.88008,-12.899729],[-68.929224,-13.602684],[-68.948887,-14.453639],[-69.339535,-14.953195],[-69.160347,-15.323974],[-69.389764,-15.660129],[-68.959635,-16.500698],[-69.590424,-17.580012]]]}}, +{"type":"Feature","id":"PHL","properties":{"name":"Philippines"},"geometry":{"type":"MultiPolygon","coordinates":[[[[126.376814,8.414706],[126.478513,7.750354],[126.537424,7.189381],[126.196773,6.274294],[125.831421,7.293715],[125.363852,6.786485],[125.683161,6.049657],[125.396512,5.581003],[124.219788,6.161355],[123.93872,6.885136],[124.243662,7.36061],[123.610212,7.833527],[123.296071,7.418876],[122.825506,7.457375],[122.085499,6.899424],[121.919928,7.192119],[122.312359,8.034962],[122.942398,8.316237],[123.487688,8.69301],[123.841154,8.240324],[124.60147,8.514158],[124.764612,8.960409],[125.471391,8.986997],[125.412118,9.760335],[126.222714,9.286074],[126.306637,8.782487],[126.376814,8.414706]]],[[[123.982438,10.278779],[123.623183,9.950091],[123.309921,9.318269],[122.995883,9.022189],[122.380055,9.713361],[122.586089,9.981045],[122.837081,10.261157],[122.947411,10.881868],[123.49885,10.940624],[123.337774,10.267384],[124.077936,11.232726],[123.982438,10.278779]]],[[[118.504581,9.316383],[117.174275,8.3675],[117.664477,9.066889],[118.386914,9.6845],[118.987342,10.376292],[119.511496,11.369668],[119.689677,10.554291],[119.029458,10.003653],[118.504581,9.316383]]],[[[121.883548,11.891755],[122.483821,11.582187],[123.120217,11.58366],[123.100838,11.165934],[122.637714,10.741308],[122.00261,10.441017],[121.967367,10.905691],[122.03837,11.415841],[121.883548,11.891755]]],[[[125.502552,12.162695],[125.783465,11.046122],[125.011884,11.311455],[125.032761,10.975816],[125.277449,10.358722],[124.801819,10.134679],[124.760168,10.837995],[124.459101,10.88993],[124.302522,11.495371],[124.891013,11.415583],[124.87799,11.79419],[124.266762,12.557761],[125.227116,12.535721],[125.502552,12.162695]]],[[[121.527394,13.06959],[121.26219,12.20556],[120.833896,12.704496],[120.323436,13.466413],[121.180128,13.429697],[121.527394,13.06959]]],[[[121.321308,18.504065],[121.937601,18.218552],[122.246006,18.47895],[122.336957,18.224883],[122.174279,17.810283],[122.515654,17.093505],[122.252311,16.262444],[121.662786,15.931018],[121.50507,15.124814],[121.728829,14.328376],[122.258925,14.218202],[122.701276,14.336541],[123.950295,13.782131],[123.855107,13.237771],[124.181289,12.997527],[124.077419,12.536677],[123.298035,13.027526],[122.928652,13.55292],[122.671355,13.185836],[122.03465,13.784482],[121.126385,13.636687],[120.628637,13.857656],[120.679384,14.271016],[120.991819,14.525393],[120.693336,14.756671],[120.564145,14.396279],[120.070429,14.970869],[119.920929,15.406347],[119.883773,16.363704],[120.286488,16.034629],[120.390047,17.599081],[120.715867,18.505227],[121.321308,18.504065]]]]}}, +{"type":"Feature","id":"PNG","properties":{"name":"Papua New Guinea"},"geometry":{"type":"MultiPolygon","coordinates":[[[[155.880026,-6.819997],[155.599991,-6.919991],[155.166994,-6.535931],[154.729192,-5.900828],[154.514114,-5.139118],[154.652504,-5.042431],[154.759991,-5.339984],[155.062918,-5.566792],[155.547746,-6.200655],[156.019965,-6.540014],[155.880026,-6.819997]]],[[[151.982796,-5.478063],[151.459107,-5.56028],[151.30139,-5.840728],[150.754447,-6.083763],[150.241197,-6.317754],[149.709963,-6.316513],[148.890065,-6.02604],[148.318937,-5.747142],[148.401826,-5.437756],[149.298412,-5.583742],[149.845562,-5.505503],[149.99625,-5.026101],[150.139756,-5.001348],[150.236908,-5.53222],[150.807467,-5.455842],[151.089672,-5.113693],[151.647881,-4.757074],[151.537862,-4.167807],[152.136792,-4.14879],[152.338743,-4.312966],[152.318693,-4.867661],[151.982796,-5.478063]]],[[[147.191874,-7.388024],[148.084636,-8.044108],[148.734105,-9.104664],[149.306835,-9.071436],[149.266631,-9.514406],[150.038728,-9.684318],[149.738798,-9.872937],[150.801628,-10.293687],[150.690575,-10.582713],[150.028393,-10.652476],[149.78231,-10.393267],[148.923138,-10.280923],[147.913018,-10.130441],[147.135443,-9.492444],[146.567881,-8.942555],[146.048481,-8.067414],[144.744168,-7.630128],[143.897088,-7.91533],[143.286376,-8.245491],[143.413913,-8.983069],[142.628431,-9.326821],[142.068259,-9.159596],[141.033852,-9.117893],[141.017057,-5.859022],[141.00021,-2.600151],[142.735247,-3.289153],[144.583971,-3.861418],[145.27318,-4.373738],[145.829786,-4.876498],[145.981922,-5.465609],[147.648073,-6.083659],[147.891108,-6.614015],[146.970905,-6.721657],[147.191874,-7.388024]]],[[[153.140038,-4.499983],[152.827292,-4.766427],[152.638673,-4.176127],[152.406026,-3.789743],[151.953237,-3.462062],[151.384279,-3.035422],[150.66205,-2.741486],[150.939965,-2.500002],[151.479984,-2.779985],[151.820015,-2.999972],[152.239989,-3.240009],[152.640017,-3.659983],[153.019994,-3.980015],[153.140038,-4.499983]]]]}}, +{"type":"Feature","id":"POL","properties":{"name":"Poland"},"geometry":{"type":"Polygon","coordinates":[[[15.016996,51.106674],[14.607098,51.745188],[14.685026,52.089947],[14.4376,52.62485],[14.074521,52.981263],[14.353315,53.248171],[14.119686,53.757029],[14.8029,54.050706],[16.363477,54.513159],[17.622832,54.851536],[18.620859,54.682606],[18.696255,54.438719],[19.66064,54.426084],[20.892245,54.312525],[22.731099,54.327537],[23.243987,54.220567],[23.484128,53.912498],[23.527536,53.470122],[23.804935,53.089731],[23.799199,52.691099],[23.199494,52.486977],[23.508002,52.023647],[23.527071,51.578454],[24.029986,50.705407],[23.922757,50.424881],[23.426508,50.308506],[22.51845,49.476774],[22.776419,49.027395],[22.558138,49.085738],[21.607808,49.470107],[20.887955,49.328772],[20.415839,49.431453],[19.825023,49.217125],[19.320713,49.571574],[18.909575,49.435846],[18.853144,49.49623],[18.392914,49.988629],[17.649445,50.049038],[17.554567,50.362146],[16.868769,50.473974],[16.719476,50.215747],[16.176253,50.422607],[16.238627,50.697733],[15.490972,50.78473],[15.016996,51.106674]]]}}, +{"type":"Feature","id":"PRI","properties":{"name":"Puerto Rico"},"geometry":{"type":"Polygon","coordinates":[[[-66.282434,18.514762],[-65.771303,18.426679],[-65.591004,18.228035],[-65.847164,17.975906],[-66.599934,17.981823],[-67.184162,17.946553],[-67.242428,18.37446],[-67.100679,18.520601],[-66.282434,18.514762]]]}}, +{"type":"Feature","id":"PRK","properties":{"name":"North Korea"},"geometry":{"type":"Polygon","coordinates":[[[130.640016,42.395009],[130.780007,42.220007],[130.400031,42.280004],[129.965949,41.941368],[129.667362,41.601104],[129.705189,40.882828],[129.188115,40.661808],[129.0104,40.485436],[128.633368,40.189847],[127.967414,40.025413],[127.533436,39.75685],[127.50212,39.323931],[127.385434,39.213472],[127.783343,39.050898],[128.349716,38.612243],[128.205746,38.370397],[127.780035,38.304536],[127.073309,38.256115],[126.68372,37.804773],[126.237339,37.840378],[126.174759,37.749686],[125.689104,37.94001],[125.568439,37.752089],[125.27533,37.669071],[125.240087,37.857224],[124.981033,37.948821],[124.712161,38.108346],[124.985994,38.548474],[125.221949,38.665857],[125.132859,38.848559],[125.38659,39.387958],[125.321116,39.551385],[124.737482,39.660344],[124.265625,39.928493],[125.079942,40.569824],[126.182045,41.107336],[126.869083,41.816569],[127.343783,41.503152],[128.208433,41.466772],[128.052215,41.994285],[129.596669,42.424982],[129.994267,42.985387],[130.640016,42.395009]]]}}, +{"type":"Feature","id":"PRT","properties":{"name":"Portugal"},"geometry":{"type":"Polygon","coordinates":[[[-9.034818,41.880571],[-8.671946,42.134689],[-8.263857,42.280469],[-8.013175,41.790886],[-7.422513,41.792075],[-7.251309,41.918346],[-6.668606,41.883387],[-6.389088,41.381815],[-6.851127,41.111083],[-6.86402,40.330872],[-7.026413,40.184524],[-7.066592,39.711892],[-7.498632,39.629571],[-7.098037,39.030073],[-7.374092,38.373059],[-7.029281,38.075764],[-7.166508,37.803894],[-7.537105,37.428904],[-7.453726,37.097788],[-7.855613,36.838269],[-8.382816,36.97888],[-8.898857,36.868809],[-8.746101,37.651346],[-8.839998,38.266243],[-9.287464,38.358486],[-9.526571,38.737429],[-9.446989,39.392066],[-9.048305,39.755093],[-8.977353,40.159306],[-8.768684,40.760639],[-8.790853,41.184334],[-8.990789,41.543459],[-9.034818,41.880571]]]}}, +{"type":"Feature","id":"PRY","properties":{"name":"Paraguay"},"geometry":{"type":"Polygon","coordinates":[[[-62.685057,-22.249029],[-62.291179,-21.051635],[-62.265961,-20.513735],[-61.786326,-19.633737],[-60.043565,-19.342747],[-59.115042,-19.356906],[-58.183471,-19.868399],[-58.166392,-20.176701],[-57.870674,-20.732688],[-57.937156,-22.090176],[-56.88151,-22.282154],[-56.473317,-22.0863],[-55.797958,-22.35693],[-55.610683,-22.655619],[-55.517639,-23.571998],[-55.400747,-23.956935],[-55.027902,-24.001274],[-54.652834,-23.839578],[-54.29296,-24.021014],[-54.293476,-24.5708],[-54.428946,-25.162185],[-54.625291,-25.739255],[-54.788795,-26.621786],[-55.695846,-27.387837],[-56.486702,-27.548499],[-57.60976,-27.395899],[-58.618174,-27.123719],[-57.63366,-25.603657],[-57.777217,-25.16234],[-58.807128,-24.771459],[-60.028966,-24.032796],[-60.846565,-23.880713],[-62.685057,-22.249029]]]}}, +{"type":"Feature","id":"QAT","properties":{"name":"Qatar"},"geometry":{"type":"Polygon","coordinates":[[[50.810108,24.754743],[50.743911,25.482424],[51.013352,26.006992],[51.286462,26.114582],[51.589079,25.801113],[51.6067,25.21567],[51.389608,24.627386],[51.112415,24.556331],[50.810108,24.754743]]]}}, +{"type":"Feature","id":"ROU","properties":{"name":"Romania"},"geometry":{"type":"Polygon","coordinates":[[[22.710531,47.882194],[23.142236,48.096341],[23.760958,47.985598],[24.402056,47.981878],[24.866317,47.737526],[25.207743,47.891056],[25.945941,47.987149],[26.19745,48.220881],[26.619337,48.220726],[26.924176,48.123264],[27.233873,47.826771],[27.551166,47.405117],[28.12803,46.810476],[28.160018,46.371563],[28.054443,45.944586],[28.233554,45.488283],[28.679779,45.304031],[29.149725,45.464925],[29.603289,45.293308],[29.626543,45.035391],[29.141612,44.82021],[28.837858,44.913874],[28.558081,43.707462],[27.970107,43.812468],[27.2424,44.175986],[26.065159,43.943494],[25.569272,43.688445],[24.100679,43.741051],[23.332302,43.897011],[22.944832,43.823785],[22.65715,44.234923],[22.474008,44.409228],[22.705726,44.578003],[22.459022,44.702517],[22.145088,44.478422],[21.562023,44.768947],[21.483526,45.18117],[20.874313,45.416375],[20.762175,45.734573],[20.220192,46.127469],[21.021952,46.316088],[21.626515,46.994238],[22.099768,47.672439],[22.710531,47.882194]]]}}, +{"type":"Feature","id":"RUS","properties":{"name":"Russia"},"geometry":{"type":"MultiPolygon","coordinates":[[[[143.648007,50.7476],[144.654148,48.976391],[143.173928,49.306551],[142.558668,47.861575],[143.533492,46.836728],[143.505277,46.137908],[142.747701,46.740765],[142.09203,45.966755],[141.906925,46.805929],[142.018443,47.780133],[141.904445,48.859189],[142.1358,49.615163],[142.179983,50.952342],[141.594076,51.935435],[141.682546,53.301966],[142.606934,53.762145],[142.209749,54.225476],[142.654786,54.365881],[142.914616,53.704578],[143.260848,52.74076],[143.235268,51.75666],[143.648007,50.7476]]],[[[22.731099,54.327537],[20.892245,54.312525],[19.66064,54.426084],[19.888481,54.86616],[21.268449,55.190482],[22.315724,55.015299],[22.757764,54.856574],[22.651052,54.582741],[22.731099,54.327537]]],[[[-175.01425,66.58435],[-174.33983,66.33556],[-174.57182,67.06219],[-171.85731,66.91308],[-169.89958,65.97724],[-170.89107,65.54139],[-172.53025,65.43791],[-172.555,64.46079],[-172.95533,64.25269],[-173.89184,64.2826],[-174.65392,64.63125],[-175.98353,64.92288],[-176.20716,65.35667],[-177.22266,65.52024],[-178.35993,65.39052],[-178.90332,65.74044],[-178.68611,66.11211],[-179.88377,65.87456],[-179.43268,65.40411],[-180,64.979709],[-180,68.963636],[-177.55,68.2],[-174.92825,67.20589],[-175.01425,66.58435]]],[[[180,70.832199],[178.903425,70.78114],[178.7253,71.0988],[180,71.515714],[180,70.832199]]],[[[-178.69378,70.89302],[-180,70.832199],[-180,71.515714],[-179.871875,71.55762],[-179.02433,71.55553],[-177.577945,71.26948],[-177.663575,71.13277],[-178.69378,70.89302]]],[[[143.60385,73.21244],[142.08763,73.20544],[140.038155,73.31692],[139.86312,73.36983],[140.81171,73.76506],[142.06207,73.85758],[143.48283,73.47525],[143.60385,73.21244]]],[[[150.73167,75.08406],[149.575925,74.68892],[147.977465,74.778355],[146.11919,75.17298],[146.358485,75.49682],[148.22223,75.345845],[150.73167,75.08406]]],[[[145.086285,75.562625],[144.3,74.82],[140.61381,74.84768],[138.95544,74.61148],[136.97439,75.26167],[137.51176,75.94917],[138.831075,76.13676],[141.471615,76.09289],[145.086285,75.562625]]],[[[57.535693,70.720464],[56.944979,70.632743],[53.677375,70.762658],[53.412017,71.206662],[51.601895,71.474759],[51.455754,72.014881],[52.478275,72.229442],[52.444169,72.774731],[54.427614,73.627548],[53.50829,73.749814],[55.902459,74.627486],[55.631933,75.081412],[57.868644,75.60939],[61.170044,76.251883],[64.498368,76.439055],[66.210977,76.809782],[68.15706,76.939697],[68.852211,76.544811],[68.180573,76.233642],[64.637326,75.737755],[61.583508,75.260885],[58.477082,74.309056],[56.986786,73.333044],[55.419336,72.371268],[55.622838,71.540595],[57.535693,70.720464]]],[[[106.97013,76.97419],[107.24,76.48],[108.1538,76.72335],[111.07726,76.71],[113.33151,76.22224],[114.13417,75.84764],[113.88539,75.32779],[112.77918,75.03186],[110.15125,74.47673],[109.4,74.18],[110.64,74.04],[112.11919,73.78774],[113.01954,73.97693],[113.52958,73.33505],[113.96881,73.59488],[115.56782,73.75285],[118.77633,73.58772],[119.02,73.12],[123.20066,72.97122],[123.25777,73.73503],[125.38,73.56],[126.97644,73.56549],[128.59126,73.03871],[129.05157,72.39872],[128.46,71.98],[129.71599,71.19304],[131.28858,70.78699],[132.2535,71.8363],[133.85766,71.38642],[135.56193,71.65525],[137.49755,71.34763],[138.23409,71.62803],[139.86983,71.48783],[139.14791,72.41619],[140.46817,72.84941],[149.5,72.2],[150.35118,71.60643],[152.9689,70.84222],[157.00688,71.03141],[158.99779,70.86672],[159.83031,70.45324],[159.70866,69.72198],[160.94053,69.43728],[162.27907,69.64204],[164.05248,69.66823],[165.94037,69.47199],[167.83567,69.58269],[169.57763,68.6938],[170.81688,69.01363],[170.0082,69.65276],[170.45345,70.09703],[173.64391,69.81743],[175.72403,69.87725],[178.6,69.4],[180,68.963636],[180,64.979709],[179.99281,64.97433],[178.7072,64.53493],[177.41128,64.60821],[178.313,64.07593],[178.90825,63.25197],[179.37034,62.98262],[179.48636,62.56894],[179.22825,62.3041],[177.3643,62.5219],[174.56929,61.76915],[173.68013,61.65261],[172.15,60.95],[170.6985,60.33618],[170.33085,59.88177],[168.90046,60.57355],[166.29498,59.78855],[165.84,60.16],[164.87674,59.7316],[163.53929,59.86871],[163.21711,59.21101],[162.01733,58.24328],[162.05297,57.83912],[163.19191,57.61503],[163.05794,56.15924],[162.12958,56.12219],[161.70146,55.28568],[162.11749,54.85514],[160.36877,54.34433],[160.02173,53.20257],[158.53094,52.95868],[158.23118,51.94269],[156.78979,51.01105],[156.42,51.7],[155.99182,53.15895],[155.43366,55.38103],[155.91442,56.76792],[156.75815,57.3647],[156.81035,57.83204],[158.36433,58.05575],[160.15064,59.31477],[161.87204,60.343],[163.66969,61.1409],[164.47355,62.55061],[163.25842,62.46627],[162.65791,61.6425],[160.12148,60.54423],[159.30232,61.77396],[156.72068,61.43442],[154.21806,59.75818],[155.04375,59.14495],[152.81185,58.88385],[151.26573,58.78089],[151.33815,59.50396],[149.78371,59.65573],[148.54481,59.16448],[145.48722,59.33637],[142.19782,59.03998],[138.95848,57.08805],[135.12619,54.72959],[136.70171,54.60355],[137.19342,53.97732],[138.1647,53.75501],[138.80463,54.25455],[139.90151,54.18968],[141.34531,53.08957],[141.37923,52.23877],[140.59742,51.23967],[140.51308,50.04553],[140.06193,48.44671],[138.55472,46.99965],[138.21971,46.30795],[136.86232,45.1435],[135.51535,43.989],[134.86939,43.39821],[133.53687,42.81147],[132.90627,42.79849],[132.27807,43.28456],[130.93587,42.55274],[130.78,42.22],[130.64,42.395],[130.633866,42.903015],[131.144688,42.92999],[131.288555,44.11152],[131.02519,44.96796],[131.883454,45.321162],[133.09712,45.14409],[133.769644,46.116927],[134.11235,47.21248],[134.50081,47.57845],[135.026311,48.47823],[133.373596,48.183442],[132.50669,47.78896],[130.98726,47.79013],[130.582293,48.729687],[129.397818,49.4406],[127.6574,49.76027],[127.287456,50.739797],[126.939157,51.353894],[126.564399,51.784255],[125.946349,52.792799],[125.068211,53.161045],[123.57147,53.4588],[122.245748,53.431726],[121.003085,53.251401],[120.177089,52.753886],[120.725789,52.516226],[120.7382,51.96411],[120.18208,51.64355],[119.27939,50.58292],[119.288461,50.142883],[117.879244,49.510983],[116.678801,49.888531],[115.485695,49.805177],[114.96211,50.140247],[114.362456,50.248303],[112.89774,49.543565],[111.581231,49.377968],[110.662011,49.130128],[109.402449,49.292961],[108.475167,49.282548],[107.868176,49.793705],[106.888804,50.274296],[105.886591,50.406019],[104.62158,50.27532],[103.676545,50.089966],[102.25589,50.51056],[102.06521,51.25991],[100.88948,51.516856],[99.981732,51.634006],[98.861491,52.047366],[97.82574,51.010995],[98.231762,50.422401],[97.25976,49.72605],[95.81402,49.97746],[94.815949,50.013433],[94.147566,50.480537],[93.10421,50.49529],[92.234712,50.802171],[90.713667,50.331812],[88.805567,49.470521],[87.751264,49.297198],[87.35997,49.214981],[86.829357,49.826675],[85.54127,49.692859],[85.11556,50.117303],[84.416377,50.3114],[83.935115,50.889246],[83.383004,51.069183],[81.945986,50.812196],[80.568447,51.388336],[80.03556,50.864751],[77.800916,53.404415],[76.525179,54.177003],[76.8911,54.490524],[74.38482,53.54685],[73.425679,53.48981],[73.508516,54.035617],[72.22415,54.376655],[71.180131,54.133285],[70.865267,55.169734],[69.068167,55.38525],[68.1691,54.970392],[65.66687,54.60125],[65.178534,54.354228],[61.4366,54.00625],[60.978066,53.664993],[61.699986,52.979996],[60.739993,52.719986],[60.927269,52.447548],[59.967534,51.96042],[61.588003,51.272659],[61.337424,50.79907],[59.932807,50.842194],[59.642282,50.545442],[58.36332,51.06364],[56.77798,51.04355],[55.71694,50.62171],[54.532878,51.02624],[52.328724,51.718652],[50.766648,51.692762],[48.702382,50.605128],[48.577841,49.87476],[47.54948,50.454698],[46.751596,49.356006],[47.043672,49.152039],[46.466446,48.394152],[47.31524,47.71585],[48.05725,47.74377],[48.694734,47.075628],[48.59325,46.56104],[49.10116,46.39933],[48.64541,45.80629],[47.67591,45.64149],[46.68201,44.6092],[47.59094,43.66016],[47.49252,42.98658],[48.58437,41.80888],[47.987283,41.405819],[47.815666,41.151416],[47.373315,41.219732],[46.686071,41.827137],[46.404951,41.860675],[45.7764,42.09244],[45.470279,42.502781],[44.537623,42.711993],[43.93121,42.55496],[43.75599,42.74083],[42.3944,43.2203],[40.92219,43.38215],[40.076965,43.553104],[39.955009,43.434998],[38.68,44.28],[37.53912,44.65721],[36.67546,45.24469],[37.40317,45.40451],[38.23295,46.24087],[37.67372,46.63657],[39.14767,47.04475],[39.1212,47.26336],[38.223538,47.10219],[38.255112,47.5464],[38.77057,47.82562],[39.738278,47.898937],[39.89562,48.23241],[39.67465,48.78382],[40.080789,49.30743],[40.06904,49.60105],[38.594988,49.926462],[38.010631,49.915662],[37.39346,50.383953],[36.626168,50.225591],[35.356116,50.577197],[35.37791,50.77394],[35.022183,51.207572],[34.224816,51.255993],[34.141978,51.566413],[34.391731,51.768882],[33.7527,52.335075],[32.715761,52.238465],[32.412058,52.288695],[32.15944,52.06125],[31.78597,52.10168],[31.540018,52.742052],[31.305201,53.073996],[31.49764,53.16743],[32.304519,53.132726],[32.693643,53.351421],[32.405599,53.618045],[31.731273,53.794029],[31.791424,53.974639],[31.384472,54.157056],[30.757534,54.811771],[30.971836,55.081548],[30.873909,55.550976],[29.896294,55.789463],[29.371572,55.670091],[29.229513,55.918344],[28.176709,56.16913],[27.855282,56.759326],[27.770016,57.244258],[27.288185,57.474528],[27.716686,57.791899],[27.42015,58.72457],[28.131699,59.300825],[27.98112,59.47537],[29.1177,60.02805],[28.07,60.50352],[30.211107,61.780028],[31.139991,62.357693],[31.516092,62.867687],[30.035872,63.552814],[30.444685,64.204453],[29.54443,64.948672],[30.21765,65.80598],[29.054589,66.944286],[29.977426,67.698297],[28.445944,68.364613],[28.59193,69.064777],[29.39955,69.15692],[31.10108,69.55811],[32.13272,69.90595],[33.77547,69.30142],[36.51396,69.06342],[40.29234,67.9324],[41.05987,67.45713],[41.12595,66.79158],[40.01583,66.26618],[38.38295,65.99953],[33.91871,66.75961],[33.18444,66.63253],[34.81477,65.90015],[34.878574,65.436213],[34.94391,64.41437],[36.23129,64.10945],[37.01273,63.84983],[37.14197,64.33471],[36.539579,64.76446],[37.17604,65.14322],[39.59345,64.52079],[40.4356,64.76446],[39.7626,65.49682],[42.09309,66.47623],[43.01604,66.41858],[43.94975,66.06908],[44.53226,66.75634],[43.69839,67.35245],[44.18795,67.95051],[43.45282,68.57079],[46.25,68.25],[46.82134,67.68997],[45.55517,67.56652],[45.56202,67.01005],[46.34915,66.66767],[47.89416,66.88455],[48.13876,67.52238],[50.22766,67.99867],[53.71743,68.85738],[54.47171,68.80815],[53.48582,68.20131],[54.72628,68.09702],[55.44268,68.43866],[57.31702,68.46628],[58.802,68.88082],[59.94142,68.27844],[61.07784,68.94069],[60.03,69.52],[60.55,69.85],[63.504,69.54739],[64.888115,69.234835],[68.51216,68.09233],[69.18068,68.61563],[68.16444,69.14436],[68.13522,69.35649],[66.93008,69.45461],[67.25976,69.92873],[66.72492,70.70889],[66.69466,71.02897],[68.54006,71.9345],[69.19636,72.84336],[69.94,73.04],[72.58754,72.77629],[72.79603,72.22006],[71.84811,71.40898],[72.47011,71.09019],[72.79188,70.39114],[72.5647,69.02085],[73.66787,68.4079],[73.2387,67.7404],[71.28,66.32],[72.42301,66.17267],[72.82077,66.53267],[73.92099,66.78946],[74.18651,67.28429],[75.052,67.76047],[74.46926,68.32899],[74.93584,68.98918],[73.84236,69.07146],[73.60187,69.62763],[74.3998,70.63175],[73.1011,71.44717],[74.89082,72.12119],[74.65926,72.83227],[75.15801,72.85497],[75.68351,72.30056],[75.28898,71.33556],[76.35911,71.15287],[75.90313,71.87401],[77.57665,72.26717],[79.65202,72.32011],[81.5,71.75],[80.61071,72.58285],[80.51109,73.6482],[82.25,73.85],[84.65526,73.80591],[86.8223,73.93688],[86.00956,74.45967],[87.16682,75.11643],[88.31571,75.14393],[90.26,75.64],[92.90058,75.77333],[93.23421,76.0472],[95.86,76.14],[96.67821,75.91548],[98.92254,76.44689],[100.75967,76.43028],[101.03532,76.86189],[101.99084,77.28754],[104.3516,77.69792],[106.06664,77.37389],[104.705,77.1274],[106.97013,76.97419]]],[[[105.07547,78.30689],[99.43814,77.921],[101.2649,79.23399],[102.08635,79.34641],[102.837815,79.28129],[105.37243,78.71334],[105.07547,78.30689]]],[[[51.136187,80.54728],[49.793685,80.415428],[48.894411,80.339567],[48.754937,80.175468],[47.586119,80.010181],[46.502826,80.247247],[47.072455,80.559424],[44.846958,80.58981],[46.799139,80.771918],[48.318477,80.78401],[48.522806,80.514569],[49.09719,80.753986],[50.039768,80.918885],[51.522933,80.699726],[51.136187,80.54728]]],[[[99.93976,78.88094],[97.75794,78.7562],[94.97259,79.044745],[93.31288,79.4265],[92.5454,80.14379],[91.18107,80.34146],[93.77766,81.0246],[95.940895,81.2504],[97.88385,80.746975],[100.186655,79.780135],[99.93976,78.88094]]]]}}, +{"type":"Feature","id":"RWA","properties":{"name":"Rwanda"},"geometry":{"type":"Polygon","coordinates":[[[30.419105,-1.134659],[30.816135,-1.698914],[30.758309,-2.28725],[30.469696,-2.413858],[29.938359,-2.348487],[29.632176,-2.917858],[29.024926,-2.839258],[29.117479,-2.292211],[29.254835,-2.21511],[29.291887,-1.620056],[29.579466,-1.341313],[29.821519,-1.443322],[30.419105,-1.134659]]]}}, +{"type":"Feature","id":"ESH","properties":{"name":"Western Sahara"},"geometry":{"type":"Polygon","coordinates":[[[-8.794884,27.120696],[-8.817828,27.656426],[-8.66559,27.656426],[-8.665124,27.589479],[-8.6844,27.395744],[-8.687294,25.881056],[-11.969419,25.933353],[-11.937224,23.374594],[-12.874222,23.284832],[-13.118754,22.77122],[-12.929102,21.327071],[-16.845194,21.333323],[-17.063423,20.999752],[-17.020428,21.42231],[-17.002962,21.420734],[-14.750955,21.5006],[-14.630833,21.86094],[-14.221168,22.310163],[-13.89111,23.691009],[-12.500963,24.770116],[-12.030759,26.030866],[-11.71822,26.104092],[-11.392555,26.883424],[-10.551263,26.990808],[-10.189424,26.860945],[-9.735343,26.860945],[-9.413037,27.088476],[-8.794884,27.120696]]]}}, +{"type":"Feature","id":"SAU","properties":{"name":"Saudi Arabia"},"geometry":{"type":"Polygon","coordinates":[[[42.779332,16.347891],[42.649573,16.774635],[42.347989,17.075806],[42.270888,17.474722],[41.754382,17.833046],[41.221391,18.6716],[40.939341,19.486485],[40.247652,20.174635],[39.801685,20.338862],[39.139399,21.291905],[39.023696,21.986875],[39.066329,22.579656],[38.492772,23.688451],[38.02386,24.078686],[37.483635,24.285495],[37.154818,24.858483],[37.209491,25.084542],[36.931627,25.602959],[36.639604,25.826228],[36.249137,26.570136],[35.640182,27.37652],[35.130187,28.063352],[34.632336,28.058546],[34.787779,28.607427],[34.83222,28.957483],[34.956037,29.356555],[36.068941,29.197495],[36.501214,29.505254],[36.740528,29.865283],[37.503582,30.003776],[37.66812,30.338665],[37.998849,30.5085],[37.002166,31.508413],[39.004886,32.010217],[39.195468,32.161009],[40.399994,31.889992],[41.889981,31.190009],[44.709499,29.178891],[46.568713,29.099025],[47.459822,29.002519],[47.708851,28.526063],[48.416094,28.552004],[48.807595,27.689628],[49.299554,27.461218],[49.470914,27.109999],[50.152422,26.689663],[50.212935,26.277027],[50.113303,25.943972],[50.239859,25.60805],[50.527387,25.327808],[50.660557,24.999896],[50.810108,24.754743],[51.112415,24.556331],[51.389608,24.627386],[51.579519,24.245497],[51.617708,24.014219],[52.000733,23.001154],[55.006803,22.496948],[55.208341,22.70833],[55.666659,22.000001],[54.999982,19.999994],[52.00001,19.000003],[49.116672,18.616668],[48.183344,18.166669],[47.466695,17.116682],[47.000005,16.949999],[46.749994,17.283338],[46.366659,17.233315],[45.399999,17.333335],[45.216651,17.433329],[44.062613,17.410359],[43.791519,17.319977],[43.380794,17.579987],[43.115798,17.08844],[43.218375,16.66689],[42.779332,16.347891]]]}}, +{"type":"Feature","id":"SDN","properties":{"name":"Sudan"},"geometry":{"type":"Polygon","coordinates":[[[33.963393,9.464285],[33.824963,9.484061],[33.842131,9.981915],[33.721959,10.325262],[33.206938,10.720112],[33.086766,11.441141],[33.206938,12.179338],[32.743419,12.248008],[32.67475,12.024832],[32.073892,11.97333],[32.314235,11.681484],[32.400072,11.080626],[31.850716,10.531271],[31.352862,9.810241],[30.837841,9.707237],[29.996639,10.290927],[29.618957,10.084919],[29.515953,9.793074],[29.000932,9.604232],[28.966597,9.398224],[27.97089,9.398224],[27.833551,9.604232],[27.112521,9.638567],[26.752006,9.466893],[26.477328,9.55273],[25.962307,10.136421],[25.790633,10.411099],[25.069604,10.27376],[24.794926,9.810241],[24.537415,8.917538],[24.194068,8.728696],[23.88698,8.61973],[23.805813,8.666319],[23.459013,8.954286],[23.394779,9.265068],[23.55725,9.681218],[23.554304,10.089255],[22.977544,10.714463],[22.864165,11.142395],[22.87622,11.38461],[22.50869,11.67936],[22.49762,12.26024],[22.28801,12.64605],[21.93681,12.58818],[22.03759,12.95546],[22.29658,13.37232],[22.18329,13.78648],[22.51202,14.09318],[22.30351,14.32682],[22.56795,14.94429],[23.02459,15.68072],[23.88689,15.61084],[23.83766,19.58047],[23.85,20],[25,20.00304],[25,22],[29.02,22],[32.9,22],[36.86623,22],[37.18872,21.01885],[36.96941,20.83744],[37.1147,19.80796],[37.48179,18.61409],[37.86276,18.36786],[38.41009,17.998307],[37.904,17.42754],[37.16747,17.26314],[36.85253,16.95655],[36.75389,16.29186],[36.32322,14.82249],[36.42951,14.42211],[36.27022,13.56333],[35.86363,12.57828],[35.26049,12.08286],[34.83163,11.31896],[34.73115,10.91017],[34.25745,10.63009],[33.96162,9.58358],[33.963393,9.464285]]]}}, +{"type":"Feature","id":"SDS","properties":{"name":"South Sudan"},"geometry":{"type":"Polygon","coordinates":[[[33.963393,9.464285],[33.97498,8.68456],[33.8255,8.37916],[33.2948,8.35458],[32.95418,7.78497],[33.56829,7.71334],[34.0751,7.22595],[34.25032,6.82607],[34.70702,6.59422],[35.298007,5.506],[34.620196,4.847123],[34.005,4.249885],[33.39,3.79],[32.68642,3.79232],[31.88145,3.55827],[31.24556,3.7819],[30.83385,3.50917],[29.95349,4.1737],[29.715995,4.600805],[29.159078,4.389267],[28.696678,4.455077],[28.428994,4.287155],[27.979977,4.408413],[27.374226,5.233944],[27.213409,5.550953],[26.465909,5.946717],[26.213418,6.546603],[25.796648,6.979316],[25.124131,7.500085],[25.114932,7.825104],[24.567369,8.229188],[23.88698,8.61973],[24.194068,8.728696],[24.537415,8.917538],[24.794926,9.810241],[25.069604,10.27376],[25.790633,10.411099],[25.962307,10.136421],[26.477328,9.55273],[26.752006,9.466893],[27.112521,9.638567],[27.833551,9.604232],[27.97089,9.398224],[28.966597,9.398224],[29.000932,9.604232],[29.515953,9.793074],[29.618957,10.084919],[29.996639,10.290927],[30.837841,9.707237],[31.352862,9.810241],[31.850716,10.531271],[32.400072,11.080626],[32.314235,11.681484],[32.073892,11.97333],[32.67475,12.024832],[32.743419,12.248008],[33.206938,12.179338],[33.086766,11.441141],[33.206938,10.720112],[33.721959,10.325262],[33.842131,9.981915],[33.824963,9.484061],[33.963393,9.464285]]]}}, +{"type":"Feature","id":"SEN","properties":{"name":"Senegal"},"geometry":{"type":"Polygon","coordinates":[[[-16.713729,13.594959],[-17.126107,14.373516],[-17.625043,14.729541],[-17.185173,14.919477],[-16.700706,15.621527],[-16.463098,16.135036],[-16.12069,16.455663],[-15.623666,16.369337],[-15.135737,16.587282],[-14.577348,16.598264],[-14.099521,16.304302],[-13.435738,16.039383],[-12.830658,15.303692],[-12.17075,14.616834],[-12.124887,13.994727],[-11.927716,13.422075],[-11.553398,13.141214],[-11.467899,12.754519],[-11.513943,12.442988],[-11.658301,12.386583],[-12.203565,12.465648],[-12.278599,12.35444],[-12.499051,12.33209],[-13.217818,12.575874],[-13.700476,12.586183],[-15.548477,12.62817],[-15.816574,12.515567],[-16.147717,12.547762],[-16.677452,12.384852],[-16.841525,13.151394],[-15.931296,13.130284],[-15.691001,13.270353],[-15.511813,13.27857],[-15.141163,13.509512],[-14.712197,13.298207],[-14.277702,13.280585],[-13.844963,13.505042],[-14.046992,13.794068],[-14.376714,13.62568],[-14.687031,13.630357],[-15.081735,13.876492],[-15.39877,13.860369],[-15.624596,13.623587],[-16.713729,13.594959]]]}}, +{"type":"Feature","id":"SLB","properties":{"name":"Solomon Islands"},"geometry":{"type":"MultiPolygon","coordinates":[[[[162.119025,-10.482719],[162.398646,-10.826367],[161.700032,-10.820011],[161.319797,-10.204751],[161.917383,-10.446701],[162.119025,-10.482719]]],[[[160.852229,-9.872937],[160.462588,-9.89521],[159.849447,-9.794027],[159.640003,-9.63998],[159.702945,-9.24295],[160.362956,-9.400304],[160.688518,-9.610162],[160.852229,-9.872937]]],[[[161.679982,-9.599982],[161.529397,-9.784312],[160.788253,-8.917543],[160.579997,-8.320009],[160.920028,-8.320009],[161.280006,-9.120011],[161.679982,-9.599982]]],[[[159.875027,-8.33732],[159.917402,-8.53829],[159.133677,-8.114181],[158.586114,-7.754824],[158.21115,-7.421872],[158.359978,-7.320018],[158.820001,-7.560003],[159.640003,-8.020027],[159.875027,-8.33732]]],[[[157.538426,-7.34782],[157.33942,-7.404767],[156.90203,-7.176874],[156.491358,-6.765943],[156.542828,-6.599338],[157.14,-7.021638],[157.538426,-7.34782]]]]}}, +{"type":"Feature","id":"SLE","properties":{"name":"Sierra Leone"},"geometry":{"type":"Polygon","coordinates":[[[-11.438779,6.785917],[-11.708195,6.860098],[-12.428099,7.262942],[-12.949049,7.798646],[-13.124025,8.163946],[-13.24655,8.903049],[-12.711958,9.342712],[-12.596719,9.620188],[-12.425929,9.835834],[-12.150338,9.858572],[-11.917277,10.046984],[-11.117481,10.045873],[-10.839152,9.688246],[-10.622395,9.26791],[-10.65477,8.977178],[-10.494315,8.715541],[-10.505477,8.348896],[-10.230094,8.406206],[-10.695595,7.939464],[-11.146704,7.396706],[-11.199802,7.105846],[-11.438779,6.785917]]]}}, +{"type":"Feature","id":"SLV","properties":{"name":"El Salvador"},"geometry":{"type":"Polygon","coordinates":[[[-87.793111,13.38448],[-87.904112,13.149017],[-88.483302,13.163951],[-88.843228,13.259734],[-89.256743,13.458533],[-89.812394,13.520622],[-90.095555,13.735338],[-90.064678,13.88197],[-89.721934,14.134228],[-89.534219,14.244816],[-89.587343,14.362586],[-89.353326,14.424133],[-89.058512,14.340029],[-88.843073,14.140507],[-88.541231,13.980155],[-88.503998,13.845486],[-88.065343,13.964626],[-87.859515,13.893312],[-87.723503,13.78505],[-87.793111,13.38448]]]}}, +{"type":"Feature","id":"-99","properties":{"name":"Somaliland"},"geometry":{"type":"Polygon","coordinates":[[[48.93813,9.451749],[48.486736,8.837626],[47.78942,8.003],[46.948328,7.996877],[43.67875,9.18358],[43.296975,9.540477],[42.92812,10.02194],[42.55876,10.57258],[42.776852,10.926879],[43.145305,11.46204],[43.47066,11.27771],[43.666668,10.864169],[44.117804,10.445538],[44.614259,10.442205],[45.556941,10.698029],[46.645401,10.816549],[47.525658,11.127228],[48.021596,11.193064],[48.378784,11.375482],[48.948206,11.410622],[48.942005,11.394266],[48.938491,10.982327],[48.938233,9.9735],[48.93813,9.451749]]]}}, +{"type":"Feature","id":"SOM","properties":{"name":"Somalia"},"geometry":{"type":"Polygon","coordinates":[[[49.72862,11.5789],[50.25878,11.67957],[50.73202,12.0219],[51.1112,12.02464],[51.13387,11.74815],[51.04153,11.16651],[51.04531,10.6409],[50.83418,10.27972],[50.55239,9.19874],[50.07092,8.08173],[49.4527,6.80466],[48.59455,5.33911],[47.74079,4.2194],[46.56476,2.85529],[45.56399,2.04576],[44.06815,1.05283],[43.13597,0.2922],[42.04157,-0.91916],[41.81095,-1.44647],[41.58513,-1.68325],[40.993,-0.85829],[40.98105,2.78452],[41.855083,3.918912],[42.12861,4.23413],[42.76967,4.25259],[43.66087,4.95755],[44.9636,5.00162],[47.78942,8.003],[48.486736,8.837626],[48.93813,9.451749],[48.938233,9.9735],[48.938491,10.982327],[48.942005,11.394266],[48.948205,11.410617],[49.26776,11.43033],[49.72862,11.5789]]]}}, +{"type":"Feature","id":"SRB","properties":{"name":"Republic of Serbia"},"geometry":{"type":"Polygon","coordinates":[[[20.874313,45.416375],[21.483526,45.18117],[21.562023,44.768947],[22.145088,44.478422],[22.459022,44.702517],[22.705726,44.578003],[22.474008,44.409228],[22.65715,44.234923],[22.410446,44.008063],[22.500157,43.642814],[22.986019,43.211161],[22.604801,42.898519],[22.436595,42.580321],[22.545012,42.461362],[22.380526,42.32026],[21.91708,42.30364],[21.576636,42.245224],[21.54332,42.32025],[21.66292,42.43922],[21.77505,42.6827],[21.63302,42.67717],[21.43866,42.86255],[21.27421,42.90959],[21.143395,43.068685],[20.95651,43.13094],[20.81448,43.27205],[20.63508,43.21671],[20.49679,42.88469],[20.25758,42.81275],[20.3398,42.89852],[19.95857,43.10604],[19.63,43.21378],[19.48389,43.35229],[19.21852,43.52384],[19.454,43.5681],[19.59976,44.03847],[19.11761,44.42307],[19.36803,44.863],[19.00548,44.86023],[19.390476,45.236516],[19.072769,45.521511],[18.82982,45.90888],[19.596045,46.17173],[20.220192,46.127469],[20.762175,45.734573],[20.874313,45.416375]]]}}, +{"type":"Feature","id":"SUR","properties":{"name":"Suriname"},"geometry":{"type":"Polygon","coordinates":[[[-57.147436,5.97315],[-55.949318,5.772878],[-55.84178,5.953125],[-55.03325,6.025291],[-53.958045,5.756548],[-54.478633,4.896756],[-54.399542,4.212611],[-54.006931,3.620038],[-54.181726,3.18978],[-54.269705,2.732392],[-54.524754,2.311849],[-55.097587,2.523748],[-55.569755,2.421506],[-55.973322,2.510364],[-56.073342,2.220795],[-55.9056,2.021996],[-55.995698,1.817667],[-56.539386,1.899523],[-57.150098,2.768927],[-57.281433,3.333492],[-57.601569,3.334655],[-58.044694,4.060864],[-57.86021,4.576801],[-57.914289,4.812626],[-57.307246,5.073567],[-57.147436,5.97315]]]}}, +{"type":"Feature","id":"SVK","properties":{"name":"Slovakia"},"geometry":{"type":"Polygon","coordinates":[[[18.853144,49.49623],[18.909575,49.435846],[19.320713,49.571574],[19.825023,49.217125],[20.415839,49.431453],[20.887955,49.328772],[21.607808,49.470107],[22.558138,49.085738],[22.280842,48.825392],[22.085608,48.422264],[21.872236,48.319971],[20.801294,48.623854],[20.473562,48.56285],[20.239054,48.327567],[19.769471,48.202691],[19.661364,48.266615],[19.174365,48.111379],[18.777025,48.081768],[18.696513,47.880954],[17.857133,47.758429],[17.488473,47.867466],[16.979667,48.123497],[16.879983,48.470013],[16.960288,48.596982],[17.101985,48.816969],[17.545007,48.800019],[17.886485,48.903475],[17.913512,48.996493],[18.104973,49.043983],[18.170498,49.271515],[18.399994,49.315001],[18.554971,49.495015],[18.853144,49.49623]]]}}, +{"type":"Feature","id":"SVN","properties":{"name":"Slovenia"},"geometry":{"type":"Polygon","coordinates":[[[13.806475,46.509306],[14.632472,46.431817],[15.137092,46.658703],[16.011664,46.683611],[16.202298,46.852386],[16.370505,46.841327],[16.564808,46.503751],[15.768733,46.238108],[15.67153,45.834154],[15.323954,45.731783],[15.327675,45.452316],[14.935244,45.471695],[14.595109,45.634941],[14.411968,45.466166],[13.71506,45.500324],[13.93763,45.591016],[13.69811,46.016778],[13.806475,46.509306]]]}}, +{"type":"Feature","id":"SWE","properties":{"name":"Sweden"},"geometry":{"type":"Polygon","coordinates":[[[22.183173,65.723741],[21.213517,65.026005],[21.369631,64.413588],[19.778876,63.609554],[17.847779,62.7494],[17.119555,61.341166],[17.831346,60.636583],[18.787722,60.081914],[17.869225,58.953766],[16.829185,58.719827],[16.44771,57.041118],[15.879786,56.104302],[14.666681,56.200885],[14.100721,55.407781],[12.942911,55.361737],[12.625101,56.30708],[11.787942,57.441817],[11.027369,58.856149],[11.468272,59.432393],[12.300366,60.117933],[12.631147,61.293572],[11.992064,61.800362],[11.930569,63.128318],[12.579935,64.066219],[13.571916,64.049114],[13.919905,64.445421],[13.55569,64.787028],[15.108411,66.193867],[16.108712,67.302456],[16.768879,68.013937],[17.729182,68.010552],[17.993868,68.567391],[19.87856,68.407194],[20.025269,69.065139],[20.645593,69.106247],[21.978535,68.616846],[23.539473,67.936009],[23.56588,66.396051],[23.903379,66.006927],[22.183173,65.723741]]]}}, +{"type":"Feature","id":"SWZ","properties":{"name":"Swaziland"},"geometry":{"type":"Polygon","coordinates":[[[32.071665,-26.73382],[31.86806,-27.177927],[31.282773,-27.285879],[30.685962,-26.743845],[30.676609,-26.398078],[30.949667,-26.022649],[31.04408,-25.731452],[31.333158,-25.660191],[31.837778,-25.843332],[31.985779,-26.29178],[32.071665,-26.73382]]]}}, +{"type":"Feature","id":"SYR","properties":{"name":"Syria"},"geometry":{"type":"Polygon","coordinates":[[[38.792341,33.378686],[36.834062,32.312938],[35.719918,32.709192],[35.700798,32.716014],[35.836397,32.868123],[35.821101,33.277426],[36.06646,33.824912],[36.61175,34.201789],[36.448194,34.593935],[35.998403,34.644914],[35.905023,35.410009],[36.149763,35.821535],[36.41755,36.040617],[36.685389,36.259699],[36.739494,36.81752],[37.066761,36.623036],[38.167727,36.90121],[38.699891,36.712927],[39.52258,36.716054],[40.673259,37.091276],[41.212089,37.074352],[42.349591,37.229873],[41.837064,36.605854],[41.289707,36.358815],[41.383965,35.628317],[41.006159,34.419372],[38.792341,33.378686]]]}}, +{"type":"Feature","id":"TCD","properties":{"name":"Chad"},"geometry":{"type":"Polygon","coordinates":[[[14.495787,12.859396],[14.595781,13.330427],[13.954477,13.353449],[13.956699,13.996691],[13.540394,14.367134],[13.97217,15.68437],[15.247731,16.627306],[15.300441,17.92795],[15.685741,19.95718],[15.903247,20.387619],[15.487148,20.730415],[15.47106,21.04845],[15.096888,21.308519],[14.8513,22.86295],[15.86085,23.40972],[19.84926,21.49509],[23.83766,19.58047],[23.88689,15.61084],[23.02459,15.68072],[22.56795,14.94429],[22.30351,14.32682],[22.51202,14.09318],[22.18329,13.78648],[22.29658,13.37232],[22.03759,12.95546],[21.93681,12.58818],[22.28801,12.64605],[22.49762,12.26024],[22.50869,11.67936],[22.87622,11.38461],[22.864165,11.142395],[22.231129,10.971889],[21.723822,10.567056],[21.000868,9.475985],[20.059685,9.012706],[19.094008,9.074847],[18.81201,8.982915],[18.911022,8.630895],[18.389555,8.281304],[17.96493,7.890914],[16.705988,7.508328],[16.456185,7.734774],[16.290562,7.754307],[16.106232,7.497088],[15.27946,7.421925],[15.436092,7.692812],[15.120866,8.38215],[14.979996,8.796104],[14.544467,8.965861],[13.954218,9.549495],[14.171466,10.021378],[14.627201,9.920919],[14.909354,9.992129],[15.467873,9.982337],[14.923565,10.891325],[14.960152,11.555574],[14.89336,12.21905],[14.495787,12.859396]]]}}, +{"type":"Feature","id":"TGO","properties":{"name":"Togo"},"geometry":{"type":"Polygon","coordinates":[[[1.865241,6.142158],[1.060122,5.928837],[0.836931,6.279979],[0.570384,6.914359],[0.490957,7.411744],[0.712029,8.312465],[0.461192,8.677223],[0.365901,9.465004],[0.36758,10.191213],[-0.049785,10.706918],[0.023803,11.018682],[0.899563,10.997339],[0.772336,10.470808],[1.077795,10.175607],[1.425061,9.825395],[1.463043,9.334624],[1.664478,9.12859],[1.618951,6.832038],[1.865241,6.142158]]]}}, +{"type":"Feature","id":"THA","properties":{"name":"Thailand"},"geometry":{"type":"Polygon","coordinates":[[[102.584932,12.186595],[101.687158,12.64574],[100.83181,12.627085],[100.978467,13.412722],[100.097797,13.406856],[100.018733,12.307001],[99.478921,10.846367],[99.153772,9.963061],[99.222399,9.239255],[99.873832,9.207862],[100.279647,8.295153],[100.459274,7.429573],[101.017328,6.856869],[101.623079,6.740622],[102.141187,6.221636],[101.814282,5.810808],[101.154219,5.691384],[101.075516,6.204867],[100.259596,6.642825],[100.085757,6.464489],[99.690691,6.848213],[99.519642,7.343454],[98.988253,7.907993],[98.503786,8.382305],[98.339662,7.794512],[98.150009,8.350007],[98.25915,8.973923],[98.553551,9.93296],[99.038121,10.960546],[99.587286,11.892763],[99.196354,12.804748],[99.212012,13.269294],[99.097755,13.827503],[98.430819,14.622028],[98.192074,15.123703],[98.537376,15.308497],[98.903348,16.177824],[98.493761,16.837836],[97.859123,17.567946],[97.375896,18.445438],[97.797783,18.62708],[98.253724,19.708203],[98.959676,19.752981],[99.543309,20.186598],[100.115988,20.41785],[100.548881,20.109238],[100.606294,19.508344],[101.282015,19.462585],[101.035931,18.408928],[101.059548,17.512497],[102.113592,18.109102],[102.413005,17.932782],[102.998706,17.961695],[103.200192,18.309632],[103.956477,18.240954],[104.716947,17.428859],[104.779321,16.441865],[105.589039,15.570316],[105.544338,14.723934],[105.218777,14.273212],[104.281418,14.416743],[102.988422,14.225721],[102.348099,13.394247],[102.584932,12.186595]]]}}, +{"type":"Feature","id":"TJK","properties":{"name":"Tajikistan"},"geometry":{"type":"Polygon","coordinates":[[[71.014198,40.244366],[70.648019,39.935754],[69.55961,40.103211],[69.464887,39.526683],[70.549162,39.604198],[71.784694,39.279463],[73.675379,39.431237],[73.928852,38.505815],[74.257514,38.606507],[74.864816,38.378846],[74.829986,37.990007],[74.980002,37.41999],[73.948696,37.421566],[73.260056,37.495257],[72.63689,37.047558],[72.193041,36.948288],[71.844638,36.738171],[71.448693,37.065645],[71.541918,37.905774],[71.239404,37.953265],[71.348131,38.258905],[70.806821,38.486282],[70.376304,38.138396],[70.270574,37.735165],[70.116578,37.588223],[69.518785,37.608997],[69.196273,37.151144],[68.859446,37.344336],[68.135562,37.023115],[67.83,37.144994],[68.392033,38.157025],[68.176025,38.901553],[67.44222,39.140144],[67.701429,39.580478],[68.536416,39.533453],[69.011633,40.086158],[69.329495,40.727824],[70.666622,40.960213],[70.45816,40.496495],[70.601407,40.218527],[71.014198,40.244366]]]}}, +{"type":"Feature","id":"TKM","properties":{"name":"Turkmenistan"},"geometry":{"type":"Polygon","coordinates":[[[61.210817,35.650072],[61.123071,36.491597],[60.377638,36.527383],[59.234762,37.412988],[58.436154,37.522309],[57.330434,38.029229],[56.619366,38.121394],[56.180375,37.935127],[55.511578,37.964117],[54.800304,37.392421],[53.921598,37.198918],[53.735511,37.906136],[53.880929,38.952093],[53.101028,39.290574],[53.357808,39.975286],[52.693973,40.033629],[52.915251,40.876523],[53.858139,40.631034],[54.736845,40.951015],[54.008311,41.551211],[53.721713,42.123191],[52.91675,41.868117],[52.814689,41.135371],[52.50246,41.783316],[52.944293,42.116034],[54.079418,42.324109],[54.755345,42.043971],[55.455251,41.259859],[55.968191,41.308642],[57.096391,41.32231],[56.932215,41.826026],[57.78653,42.170553],[58.629011,42.751551],[59.976422,42.223082],[60.083341,41.425146],[60.465953,41.220327],[61.547179,41.26637],[61.882714,41.084857],[62.37426,40.053886],[63.518015,39.363257],[64.170223,38.892407],[65.215999,38.402695],[66.54615,37.974685],[66.518607,37.362784],[66.217385,37.39379],[65.745631,37.661164],[65.588948,37.305217],[64.746105,37.111818],[64.546479,36.312073],[63.982896,36.007957],[63.193538,35.857166],[62.984662,35.404041],[62.230651,35.270664],[61.210817,35.650072]]]}}, +{"type":"Feature","id":"TLS","properties":{"name":"East Timor"},"geometry":{"type":"Polygon","coordinates":[[[124.968682,-8.89279],[125.086246,-8.656887],[125.947072,-8.432095],[126.644704,-8.398247],[126.957243,-8.273345],[127.335928,-8.397317],[126.967992,-8.668256],[125.925885,-9.106007],[125.08852,-9.393173],[125.07002,-9.089987],[124.968682,-8.89279]]]}}, +{"type":"Feature","id":"TTO","properties":{"name":"Trinidad and Tobago"},"geometry":{"type":"Polygon","coordinates":[[[-61.68,10.76],[-61.105,10.89],[-60.895,10.855],[-60.935,10.11],[-61.77,10],[-61.95,10.09],[-61.66,10.365],[-61.68,10.76]]]}}, +{"type":"Feature","id":"TUN","properties":{"name":"Tunisia"},"geometry":{"type":"Polygon","coordinates":[[[9.48214,30.307556],[9.055603,32.102692],[8.439103,32.506285],[8.430473,32.748337],[7.612642,33.344115],[7.524482,34.097376],[8.140981,34.655146],[8.376368,35.479876],[8.217824,36.433177],[8.420964,36.946427],[9.509994,37.349994],[10.210002,37.230002],[10.18065,36.724038],[11.028867,37.092103],[11.100026,36.899996],[10.600005,36.41],[10.593287,35.947444],[10.939519,35.698984],[10.807847,34.833507],[10.149593,34.330773],[10.339659,33.785742],[10.856836,33.76874],[11.108501,33.293343],[11.488787,33.136996],[11.432253,32.368903],[10.94479,32.081815],[10.636901,31.761421],[9.950225,31.37607],[10.056575,30.961831],[9.970017,30.539325],[9.48214,30.307556]]]}}, +{"type":"Feature","id":"TUR","properties":{"name":"Turkey"},"geometry":{"type":"MultiPolygon","coordinates":[[[[36.913127,41.335358],[38.347665,40.948586],[39.512607,41.102763],[40.373433,41.013673],[41.554084,41.535656],[42.619549,41.583173],[43.582746,41.092143],[43.752658,40.740201],[43.656436,40.253564],[44.400009,40.005],[44.79399,39.713003],[44.109225,39.428136],[44.421403,38.281281],[44.225756,37.971584],[44.772699,37.170445],[44.293452,37.001514],[43.942259,37.256228],[42.779126,37.385264],[42.349591,37.229873],[41.212089,37.074352],[40.673259,37.091276],[39.52258,36.716054],[38.699891,36.712927],[38.167727,36.90121],[37.066761,36.623036],[36.739494,36.81752],[36.685389,36.259699],[36.41755,36.040617],[36.149763,35.821535],[35.782085,36.274995],[36.160822,36.650606],[35.550936,36.565443],[34.714553,36.795532],[34.026895,36.21996],[32.509158,36.107564],[31.699595,36.644275],[30.621625,36.677865],[30.391096,36.262981],[29.699976,36.144357],[28.732903,36.676831],[27.641187,36.658822],[27.048768,37.653361],[26.318218,38.208133],[26.8047,38.98576],[26.170785,39.463612],[27.28002,40.420014],[28.819978,40.460011],[29.240004,41.219991],[31.145934,41.087622],[32.347979,41.736264],[33.513283,42.01896],[35.167704,42.040225],[36.913127,41.335358]]],[[[27.192377,40.690566],[26.358009,40.151994],[26.043351,40.617754],[26.056942,40.824123],[26.294602,40.936261],[26.604196,41.562115],[26.117042,41.826905],[27.135739,42.141485],[27.99672,42.007359],[28.115525,41.622886],[28.988443,41.299934],[28.806438,41.054962],[27.619017,40.999823],[27.192377,40.690566]]]]}}, +{"type":"Feature","id":"TWN","properties":{"name":"Taiwan"},"geometry":{"type":"Polygon","coordinates":[[[121.777818,24.394274],[121.175632,22.790857],[120.74708,21.970571],[120.220083,22.814861],[120.106189,23.556263],[120.69468,24.538451],[121.495044,25.295459],[121.951244,24.997596],[121.777818,24.394274]]]}}, +{"type":"Feature","id":"TZA","properties":{"name":"United Republic of Tanzania"},"geometry":{"type":"Polygon","coordinates":[[[33.903711,-0.95],[34.07262,-1.05982],[37.69869,-3.09699],[37.7669,-3.67712],[39.20222,-4.67677],[38.74054,-5.90895],[38.79977,-6.47566],[39.44,-6.84],[39.47,-7.1],[39.19469,-7.7039],[39.25203,-8.00781],[39.18652,-8.48551],[39.53574,-9.11237],[39.9496,-10.0984],[40.31659,-10.3171],[39.521,-10.89688],[38.427557,-11.285202],[37.82764,-11.26879],[37.47129,-11.56876],[36.775151,-11.594537],[36.514082,-11.720938],[35.312398,-11.439146],[34.559989,-11.52002],[34.28,-10.16],[33.940838,-9.693674],[33.73972,-9.41715],[32.759375,-9.230599],[32.191865,-8.930359],[31.556348,-8.762049],[31.157751,-8.594579],[30.74,-8.34],[30.2,-7.08],[29.62,-6.52],[29.419993,-5.939999],[29.519987,-5.419979],[29.339998,-4.499983],[29.753512,-4.452389],[30.11632,-4.09012],[30.50554,-3.56858],[30.75224,-3.35931],[30.74301,-3.03431],[30.52766,-2.80762],[30.46967,-2.41383],[30.758309,-2.28725],[30.816135,-1.698914],[30.419105,-1.134659],[30.76986,-1.01455],[31.86617,-1.02736],[33.903711,-0.95]]]}}, +{"type":"Feature","id":"UGA","properties":{"name":"Uganda"},"geometry":{"type":"Polygon","coordinates":[[[31.86617,-1.02736],[30.76986,-1.01455],[30.419105,-1.134659],[29.821519,-1.443322],[29.579466,-1.341313],[29.587838,-0.587406],[29.8195,-0.2053],[29.875779,0.59738],[30.086154,1.062313],[30.468508,1.583805],[30.85267,1.849396],[31.174149,2.204465],[30.77332,2.33989],[30.83385,3.50917],[31.24556,3.7819],[31.88145,3.55827],[32.68642,3.79232],[33.39,3.79],[34.005,4.249885],[34.47913,3.5556],[34.59607,3.05374],[35.03599,1.90584],[34.6721,1.17694],[34.18,0.515],[33.893569,0.109814],[33.903711,-0.95],[31.86617,-1.02736]]]}}, +{"type":"Feature","id":"UKR","properties":{"name":"Ukraine"},"geometry":{"type":"Polygon","coordinates":[[[31.785998,52.101678],[32.159412,52.061267],[32.412058,52.288695],[32.715761,52.238465],[33.7527,52.335075],[34.391731,51.768882],[34.141978,51.566413],[34.224816,51.255993],[35.022183,51.207572],[35.377924,50.773955],[35.356116,50.577197],[36.626168,50.225591],[37.39346,50.383953],[38.010631,49.915662],[38.594988,49.926462],[40.069058,49.601055],[40.080789,49.30743],[39.674664,48.783818],[39.895632,48.232405],[39.738278,47.898937],[38.770585,47.825608],[38.255112,47.5464],[38.223538,47.10219],[37.425137,47.022221],[36.759855,46.6987],[35.823685,46.645964],[34.962342,46.273197],[35.020788,45.651219],[35.510009,45.409993],[36.529998,45.46999],[36.334713,45.113216],[35.239999,44.939996],[33.882511,44.361479],[33.326421,44.564877],[33.546924,45.034771],[32.454174,45.327466],[32.630804,45.519186],[33.588162,45.851569],[33.298567,46.080598],[31.74414,46.333348],[31.675307,46.706245],[30.748749,46.5831],[30.377609,46.03241],[29.603289,45.293308],[29.149725,45.464925],[28.679779,45.304031],[28.233554,45.488283],[28.485269,45.596907],[28.659987,45.939987],[28.933717,46.25883],[28.862972,46.437889],[29.072107,46.517678],[29.170654,46.379262],[29.759972,46.349988],[30.024659,46.423937],[29.83821,46.525326],[29.908852,46.674361],[29.559674,46.928583],[29.415135,47.346645],[29.050868,47.510227],[29.122698,47.849095],[28.670891,48.118149],[28.259547,48.155562],[27.522537,48.467119],[26.857824,48.368211],[26.619337,48.220726],[26.19745,48.220881],[25.945941,47.987149],[25.207743,47.891056],[24.866317,47.737526],[24.402056,47.981878],[23.760958,47.985598],[23.142236,48.096341],[22.710531,47.882194],[22.64082,48.15024],[22.085608,48.422264],[22.280842,48.825392],[22.558138,49.085738],[22.776419,49.027395],[22.51845,49.476774],[23.426508,50.308506],[23.922757,50.424881],[24.029986,50.705407],[23.527071,51.578454],[24.005078,51.617444],[24.553106,51.888461],[25.327788,51.910656],[26.337959,51.832289],[27.454066,51.592303],[28.241615,51.572227],[28.617613,51.427714],[28.992835,51.602044],[29.254938,51.368234],[30.157364,51.416138],[30.555117,51.319503],[30.619454,51.822806],[30.927549,52.042353],[31.785998,52.101678]]]}}, +{"type":"Feature","id":"URY","properties":{"name":"Uruguay"},"geometry":{"type":"Polygon","coordinates":[[[-57.625133,-30.216295],[-56.976026,-30.109686],[-55.973245,-30.883076],[-55.60151,-30.853879],[-54.572452,-31.494511],[-53.787952,-32.047243],[-53.209589,-32.727666],[-53.650544,-33.202004],[-53.373662,-33.768378],[-53.806426,-34.396815],[-54.935866,-34.952647],[-55.67409,-34.752659],[-56.215297,-34.859836],[-57.139685,-34.430456],[-57.817861,-34.462547],[-58.427074,-33.909454],[-58.349611,-33.263189],[-58.132648,-33.040567],[-58.14244,-32.044504],[-57.874937,-31.016556],[-57.625133,-30.216295]]]}}, +{"type":"Feature","id":"USA","properties":{"name":"United States of America"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-155.54211,19.08348],[-155.68817,18.91619],[-155.93665,19.05939],[-155.90806,19.33888],[-156.07347,19.70294],[-156.02368,19.81422],[-155.85008,19.97729],[-155.91907,20.17395],[-155.86108,20.26721],[-155.78505,20.2487],[-155.40214,20.07975],[-155.22452,19.99302],[-155.06226,19.8591],[-154.80741,19.50871],[-154.83147,19.45328],[-155.22217,19.23972],[-155.54211,19.08348]]],[[[-156.07926,20.64397],[-156.41445,20.57241],[-156.58673,20.783],[-156.70167,20.8643],[-156.71055,20.92676],[-156.61258,21.01249],[-156.25711,20.91745],[-155.99566,20.76404],[-156.07926,20.64397]]],[[[-156.75824,21.17684],[-156.78933,21.06873],[-157.32521,21.09777],[-157.25027,21.21958],[-156.75824,21.17684]]],[[[-157.65283,21.32217],[-157.70703,21.26442],[-157.7786,21.27729],[-158.12667,21.31244],[-158.2538,21.53919],[-158.29265,21.57912],[-158.0252,21.71696],[-157.94161,21.65272],[-157.65283,21.32217]]],[[[-159.34512,21.982],[-159.46372,21.88299],[-159.80051,22.06533],[-159.74877,22.1382],[-159.5962,22.23618],[-159.36569,22.21494],[-159.34512,21.982]]],[[[-94.81758,49.38905],[-94.64,48.84],[-94.32914,48.67074],[-93.63087,48.60926],[-92.61,48.45],[-91.64,48.14],[-90.83,48.27],[-89.6,48.01],[-89.272917,48.019808],[-88.378114,48.302918],[-87.439793,47.94],[-86.461991,47.553338],[-85.652363,47.220219],[-84.87608,46.900083],[-84.779238,46.637102],[-84.543749,46.538684],[-84.6049,46.4396],[-84.3367,46.40877],[-84.14212,46.512226],[-84.091851,46.275419],[-83.890765,46.116927],[-83.616131,46.116927],[-83.469551,45.994686],[-83.592851,45.816894],[-82.550925,45.347517],[-82.337763,44.44],[-82.137642,43.571088],[-82.43,42.98],[-82.9,42.43],[-83.12,42.08],[-83.142,41.975681],[-83.02981,41.832796],[-82.690089,41.675105],[-82.439278,41.675105],[-81.277747,42.209026],[-80.247448,42.3662],[-78.939362,42.863611],[-78.92,42.965],[-79.01,43.27],[-79.171674,43.466339],[-78.72028,43.625089],[-77.737885,43.629056],[-76.820034,43.628784],[-76.5,44.018459],[-76.375,44.09631],[-75.31821,44.81645],[-74.867,45.00048],[-73.34783,45.00738],[-71.50506,45.0082],[-71.405,45.255],[-71.08482,45.30524],[-70.66,45.46],[-70.305,45.915],[-69.99997,46.69307],[-69.237216,47.447781],[-68.905,47.185],[-68.23444,47.35486],[-67.79046,47.06636],[-67.79134,45.70281],[-67.13741,45.13753],[-66.96466,44.8097],[-68.03252,44.3252],[-69.06,43.98],[-70.11617,43.68405],[-70.645476,43.090238],[-70.81489,42.8653],[-70.825,42.335],[-70.495,41.805],[-70.08,41.78],[-70.185,42.145],[-69.88497,41.92283],[-69.96503,41.63717],[-70.64,41.475],[-71.12039,41.49445],[-71.86,41.32],[-72.295,41.27],[-72.87643,41.22065],[-73.71,40.931102],[-72.24126,41.11948],[-71.945,40.93],[-73.345,40.63],[-73.982,40.628],[-73.952325,40.75075],[-74.25671,40.47351],[-73.96244,40.42763],[-74.17838,39.70926],[-74.90604,38.93954],[-74.98041,39.1964],[-75.20002,39.24845],[-75.52805,39.4985],[-75.32,38.96],[-75.071835,38.782032],[-75.05673,38.40412],[-75.37747,38.01551],[-75.94023,37.21689],[-76.03127,37.2566],[-75.72205,37.93705],[-76.23287,38.319215],[-76.35,39.15],[-76.542725,38.717615],[-76.32933,38.08326],[-76.989998,38.239992],[-76.30162,37.917945],[-76.25874,36.9664],[-75.9718,36.89726],[-75.86804,36.55125],[-75.72749,35.55074],[-76.36318,34.80854],[-77.397635,34.51201],[-78.05496,33.92547],[-78.55435,33.86133],[-79.06067,33.49395],[-79.20357,33.15839],[-80.301325,32.509355],[-80.86498,32.0333],[-81.33629,31.44049],[-81.49042,30.72999],[-81.31371,30.03552],[-80.98,29.18],[-80.535585,28.47213],[-80.53,28.04],[-80.056539,26.88],[-80.088015,26.205765],[-80.13156,25.816775],[-80.38103,25.20616],[-80.68,25.08],[-81.17213,25.20126],[-81.33,25.64],[-81.71,25.87],[-82.24,26.73],[-82.70515,27.49504],[-82.85526,27.88624],[-82.65,28.55],[-82.93,29.1],[-83.70959,29.93656],[-84.1,30.09],[-85.10882,29.63615],[-85.28784,29.68612],[-85.7731,30.15261],[-86.4,30.4],[-87.53036,30.27433],[-88.41782,30.3849],[-89.18049,30.31598],[-89.593831,30.159994],[-89.413735,29.89419],[-89.43,29.48864],[-89.21767,29.29108],[-89.40823,29.15961],[-89.77928,29.30714],[-90.15463,29.11743],[-90.880225,29.148535],[-91.626785,29.677],[-92.49906,29.5523],[-93.22637,29.78375],[-93.84842,29.71363],[-94.69,29.48],[-95.60026,28.73863],[-96.59404,28.30748],[-97.14,27.83],[-97.37,27.38],[-97.38,26.69],[-97.33,26.21],[-97.14,25.87],[-97.53,25.84],[-98.24,26.06],[-99.02,26.37],[-99.3,26.84],[-99.52,27.54],[-100.11,28.11],[-100.45584,28.69612],[-100.9576,29.38071],[-101.6624,29.7793],[-102.48,29.76],[-103.11,28.97],[-103.94,29.27],[-104.45697,29.57196],[-104.70575,30.12173],[-105.03737,30.64402],[-105.63159,31.08383],[-106.1429,31.39995],[-106.50759,31.75452],[-108.24,31.754854],[-108.24194,31.34222],[-109.035,31.34194],[-111.02361,31.33472],[-113.30498,32.03914],[-114.815,32.52528],[-114.72139,32.72083],[-115.99135,32.61239],[-117.12776,32.53534],[-117.295938,33.046225],[-117.944,33.621236],[-118.410602,33.740909],[-118.519895,34.027782],[-119.081,34.078],[-119.438841,34.348477],[-120.36778,34.44711],[-120.62286,34.60855],[-120.74433,35.15686],[-121.71457,36.16153],[-122.54747,37.55176],[-122.51201,37.78339],[-122.95319,38.11371],[-123.7272,38.95166],[-123.86517,39.76699],[-124.39807,40.3132],[-124.17886,41.14202],[-124.2137,41.99964],[-124.53284,42.76599],[-124.14214,43.70838],[-124.020535,44.615895],[-123.89893,45.52341],[-124.079635,46.86475],[-124.39567,47.72017],[-124.68721,48.184433],[-124.566101,48.379715],[-123.12,48.04],[-122.58736,47.096],[-122.34,47.36],[-122.5,48.18],[-122.84,49],[-120,49],[-117.03121,49],[-116.04818,49],[-113,49],[-110.05,49],[-107.05,49],[-104.04826,48.99986],[-100.65,49],[-97.22872,49.0007],[-95.15907,49],[-95.15609,49.38425],[-94.81758,49.38905]]],[[[-153.006314,57.115842],[-154.00509,56.734677],[-154.516403,56.992749],[-154.670993,57.461196],[-153.76278,57.816575],[-153.228729,57.968968],[-152.564791,57.901427],[-152.141147,57.591059],[-153.006314,57.115842]]],[[[-165.579164,59.909987],[-166.19277,59.754441],[-166.848337,59.941406],[-167.455277,60.213069],[-166.467792,60.38417],[-165.67443,60.293607],[-165.579164,59.909987]]],[[[-171.731657,63.782515],[-171.114434,63.592191],[-170.491112,63.694975],[-169.682505,63.431116],[-168.689439,63.297506],[-168.771941,63.188598],[-169.52944,62.976931],[-170.290556,63.194438],[-170.671386,63.375822],[-171.553063,63.317789],[-171.791111,63.405846],[-171.731657,63.782515]]],[[[-155.06779,71.147776],[-154.344165,70.696409],[-153.900006,70.889989],[-152.210006,70.829992],[-152.270002,70.600006],[-150.739992,70.430017],[-149.720003,70.53001],[-147.613362,70.214035],[-145.68999,70.12001],[-144.920011,69.989992],[-143.589446,70.152514],[-142.07251,69.851938],[-140.985988,69.711998],[-140.985988,69.711998],[-140.992499,66.000029],[-140.99777,60.306397],[-140.012998,60.276838],[-139.039,60.000007],[-138.34089,59.56211],[-137.4525,58.905],[-136.47972,59.46389],[-135.47583,59.78778],[-134.945,59.27056],[-134.27111,58.86111],[-133.355549,58.410285],[-132.73042,57.69289],[-131.70781,56.55212],[-130.00778,55.91583],[-129.979994,55.284998],[-130.53611,54.802753],[-131.085818,55.178906],[-131.967211,55.497776],[-132.250011,56.369996],[-133.539181,57.178887],[-134.078063,58.123068],[-135.038211,58.187715],[-136.628062,58.212209],[-137.800006,58.499995],[-139.867787,59.537762],[-140.825274,59.727517],[-142.574444,60.084447],[-143.958881,59.99918],[-145.925557,60.45861],[-147.114374,60.884656],[-148.224306,60.672989],[-148.018066,59.978329],[-148.570823,59.914173],[-149.727858,59.705658],[-150.608243,59.368211],[-151.716393,59.155821],[-151.859433,59.744984],[-151.409719,60.725803],[-150.346941,61.033588],[-150.621111,61.284425],[-151.895839,60.727198],[-152.57833,60.061657],[-154.019172,59.350279],[-153.287511,58.864728],[-154.232492,58.146374],[-155.307491,57.727795],[-156.308335,57.422774],[-156.556097,56.979985],[-158.117217,56.463608],[-158.433321,55.994154],[-159.603327,55.566686],[-160.28972,55.643581],[-161.223048,55.364735],[-162.237766,55.024187],[-163.069447,54.689737],[-164.785569,54.404173],[-164.942226,54.572225],[-163.84834,55.039431],[-162.870001,55.348043],[-161.804175,55.894986],[-160.563605,56.008055],[-160.07056,56.418055],[-158.684443,57.016675],[-158.461097,57.216921],[-157.72277,57.570001],[-157.550274,58.328326],[-157.041675,58.918885],[-158.194731,58.615802],[-158.517218,58.787781],[-159.058606,58.424186],[-159.711667,58.93139],[-159.981289,58.572549],[-160.355271,59.071123],[-161.355003,58.670838],[-161.968894,58.671665],[-162.054987,59.266925],[-161.874171,59.633621],[-162.518059,59.989724],[-163.818341,59.798056],[-164.662218,60.267484],[-165.346388,60.507496],[-165.350832,61.073895],[-166.121379,61.500019],[-165.734452,62.074997],[-164.919179,62.633076],[-164.562508,63.146378],[-163.753332,63.219449],[-163.067224,63.059459],[-162.260555,63.541936],[-161.53445,63.455817],[-160.772507,63.766108],[-160.958335,64.222799],[-161.518068,64.402788],[-160.777778,64.788604],[-161.391926,64.777235],[-162.45305,64.559445],[-162.757786,64.338605],[-163.546394,64.55916],[-164.96083,64.446945],[-166.425288,64.686672],[-166.845004,65.088896],[-168.11056,65.669997],[-166.705271,66.088318],[-164.47471,66.57666],[-163.652512,66.57666],[-163.788602,66.077207],[-161.677774,66.11612],[-162.489715,66.735565],[-163.719717,67.116395],[-164.430991,67.616338],[-165.390287,68.042772],[-166.764441,68.358877],[-166.204707,68.883031],[-164.430811,68.915535],[-163.168614,69.371115],[-162.930566,69.858062],[-161.908897,70.33333],[-160.934797,70.44769],[-159.039176,70.891642],[-158.119723,70.824721],[-156.580825,71.357764],[-155.06779,71.147776]]]]}}, +{"type":"Feature","id":"UZB","properties":{"name":"Uzbekistan"},"geometry":{"type":"Polygon","coordinates":[[[66.518607,37.362784],[66.54615,37.974685],[65.215999,38.402695],[64.170223,38.892407],[63.518015,39.363257],[62.37426,40.053886],[61.882714,41.084857],[61.547179,41.26637],[60.465953,41.220327],[60.083341,41.425146],[59.976422,42.223082],[58.629011,42.751551],[57.78653,42.170553],[56.932215,41.826026],[57.096391,41.32231],[55.968191,41.308642],[55.928917,44.995858],[58.503127,45.586804],[58.689989,45.500014],[60.239972,44.784037],[61.05832,44.405817],[62.0133,43.504477],[63.185787,43.650075],[64.900824,43.728081],[66.098012,42.99766],[66.023392,41.994646],[66.510649,41.987644],[66.714047,41.168444],[67.985856,41.135991],[68.259896,40.662325],[68.632483,40.668681],[69.070027,41.384244],[70.388965,42.081308],[70.962315,42.266154],[71.259248,42.167711],[70.420022,41.519998],[71.157859,41.143587],[71.870115,41.3929],[73.055417,40.866033],[71.774875,40.145844],[71.014198,40.244366],[70.601407,40.218527],[70.45816,40.496495],[70.666622,40.960213],[69.329495,40.727824],[69.011633,40.086158],[68.536416,39.533453],[67.701429,39.580478],[67.44222,39.140144],[68.176025,38.901553],[68.392033,38.157025],[67.83,37.144994],[67.075782,37.356144],[66.518607,37.362784]]]}}, +{"type":"Feature","id":"VEN","properties":{"name":"Venezuela"},"geometry":{"type":"Polygon","coordinates":[[[-71.331584,11.776284],[-71.360006,11.539994],[-71.94705,11.423282],[-71.620868,10.96946],[-71.633064,10.446494],[-72.074174,9.865651],[-71.695644,9.072263],[-71.264559,9.137195],[-71.039999,9.859993],[-71.350084,10.211935],[-71.400623,10.968969],[-70.155299,11.375482],[-70.293843,11.846822],[-69.943245,12.162307],[-69.5843,11.459611],[-68.882999,11.443385],[-68.233271,10.885744],[-68.194127,10.554653],[-67.296249,10.545868],[-66.227864,10.648627],[-65.655238,10.200799],[-64.890452,10.077215],[-64.329479,10.389599],[-64.318007,10.641418],[-63.079322,10.701724],[-61.880946,10.715625],[-62.730119,10.420269],[-62.388512,9.948204],[-61.588767,9.873067],[-60.830597,9.38134],[-60.671252,8.580174],[-60.150096,8.602757],[-59.758285,8.367035],[-60.550588,7.779603],[-60.637973,7.415],[-60.295668,7.043911],[-60.543999,6.856584],[-61.159336,6.696077],[-61.139415,6.234297],[-61.410303,5.959068],[-60.733574,5.200277],[-60.601179,4.918098],[-60.966893,4.536468],[-62.08543,4.162124],[-62.804533,4.006965],[-63.093198,3.770571],[-63.888343,4.02053],[-64.628659,4.148481],[-64.816064,4.056445],[-64.368494,3.79721],[-64.408828,3.126786],[-64.269999,2.497006],[-63.422867,2.411068],[-63.368788,2.2009],[-64.083085,1.916369],[-64.199306,1.492855],[-64.611012,1.328731],[-65.354713,1.095282],[-65.548267,0.789254],[-66.325765,0.724452],[-66.876326,1.253361],[-67.181294,2.250638],[-67.447092,2.600281],[-67.809938,2.820655],[-67.303173,3.318454],[-67.337564,3.542342],[-67.621836,3.839482],[-67.823012,4.503937],[-67.744697,5.221129],[-67.521532,5.55687],[-67.34144,6.095468],[-67.695087,6.267318],[-68.265052,6.153268],[-68.985319,6.206805],[-69.38948,6.099861],[-70.093313,6.960376],[-70.674234,7.087785],[-71.960176,6.991615],[-72.198352,7.340431],[-72.444487,7.423785],[-72.479679,7.632506],[-72.360901,8.002638],[-72.439862,8.405275],[-72.660495,8.625288],[-72.78873,9.085027],[-73.304952,9.152],[-73.027604,9.73677],[-72.905286,10.450344],[-72.614658,10.821975],[-72.227575,11.108702],[-71.973922,11.608672],[-71.331584,11.776284]]]}}, +{"type":"Feature","id":"VNM","properties":{"name":"Vietnam"},"geometry":{"type":"Polygon","coordinates":[[[108.05018,21.55238],[106.715068,20.696851],[105.881682,19.75205],[105.662006,19.058165],[106.426817,18.004121],[107.361954,16.697457],[108.269495,16.079742],[108.877107,15.276691],[109.33527,13.426028],[109.200136,11.666859],[108.36613,11.008321],[107.220929,10.364484],[106.405113,9.53084],[105.158264,8.59976],[104.795185,9.241038],[105.076202,9.918491],[104.334335,10.486544],[105.199915,10.88931],[106.24967,10.961812],[105.810524,11.567615],[107.491403,12.337206],[107.614548,13.535531],[107.382727,14.202441],[107.564525,15.202173],[107.312706,15.908538],[106.556008,16.604284],[105.925762,17.485315],[105.094598,18.666975],[103.896532,19.265181],[104.183388,19.624668],[104.822574,19.886642],[104.435,20.758733],[103.203861,20.766562],[102.754896,21.675137],[102.170436,22.464753],[102.706992,22.708795],[103.504515,22.703757],[104.476858,22.81915],[105.329209,23.352063],[105.811247,22.976892],[106.725403,22.794268],[106.567273,22.218205],[107.04342,21.811899],[108.05018,21.55238]]]}}, +{"type":"Feature","id":"VUT","properties":{"name":"Vanuatu"},"geometry":{"type":"MultiPolygon","coordinates":[[[[167.844877,-16.466333],[167.515181,-16.59785],[167.180008,-16.159995],[167.216801,-15.891846],[167.844877,-16.466333]]],[[[167.107712,-14.93392],[167.270028,-15.740021],[167.001207,-15.614602],[166.793158,-15.668811],[166.649859,-15.392704],[166.629137,-14.626497],[167.107712,-14.93392]]]]}}, +{"type":"Feature","id":"PSE","properties":{"name":"West Bank"},"geometry":{"type":"Polygon","coordinates":[[[35.545665,32.393992],[35.545252,31.782505],[35.397561,31.489086],[34.927408,31.353435],[34.970507,31.616778],[35.225892,31.754341],[34.974641,31.866582],[35.18393,32.532511],[35.545665,32.393992]]]}}, +{"type":"Feature","id":"YEM","properties":{"name":"Yemen"},"geometry":{"type":"Polygon","coordinates":[[[53.108573,16.651051],[52.385206,16.382411],[52.191729,15.938433],[52.168165,15.59742],[51.172515,15.17525],[49.574576,14.708767],[48.679231,14.003202],[48.238947,13.94809],[47.938914,14.007233],[47.354454,13.59222],[46.717076,13.399699],[45.877593,13.347764],[45.62505,13.290946],[45.406459,13.026905],[45.144356,12.953938],[44.989533,12.699587],[44.494576,12.721653],[44.175113,12.58595],[43.482959,12.6368],[43.222871,13.22095],[43.251448,13.767584],[43.087944,14.06263],[42.892245,14.802249],[42.604873,15.213335],[42.805015,15.261963],[42.702438,15.718886],[42.823671,15.911742],[42.779332,16.347891],[43.218375,16.66689],[43.115798,17.08844],[43.380794,17.579987],[43.791519,17.319977],[44.062613,17.410359],[45.216651,17.433329],[45.399999,17.333335],[46.366659,17.233315],[46.749994,17.283338],[47.000005,16.949999],[47.466695,17.116682],[48.183344,18.166669],[49.116672,18.616668],[52.00001,19.000003],[52.782184,17.349742],[53.108573,16.651051]]]}}, +{"type":"Feature","id":"ZAF","properties":{"name":"South Africa"},"geometry":{"type":"Polygon","coordinates":[[[31.521001,-29.257387],[31.325561,-29.401978],[30.901763,-29.909957],[30.622813,-30.423776],[30.055716,-31.140269],[28.925553,-32.172041],[28.219756,-32.771953],[27.464608,-33.226964],[26.419452,-33.61495],[25.909664,-33.66704],[25.780628,-33.944646],[25.172862,-33.796851],[24.677853,-33.987176],[23.594043,-33.794474],[22.988189,-33.916431],[22.574157,-33.864083],[21.542799,-34.258839],[20.689053,-34.417175],[20.071261,-34.795137],[19.616405,-34.819166],[19.193278,-34.462599],[18.855315,-34.444306],[18.424643,-33.997873],[18.377411,-34.136521],[18.244499,-33.867752],[18.25008,-33.281431],[17.92519,-32.611291],[18.24791,-32.429131],[18.221762,-31.661633],[17.566918,-30.725721],[17.064416,-29.878641],[17.062918,-29.875954],[16.344977,-28.576705],[16.824017,-28.082162],[17.218929,-28.355943],[17.387497,-28.783514],[17.836152,-28.856378],[18.464899,-29.045462],[19.002127,-28.972443],[19.894734,-28.461105],[19.895768,-24.76779],[20.165726,-24.917962],[20.758609,-25.868136],[20.66647,-26.477453],[20.889609,-26.828543],[21.605896,-26.726534],[22.105969,-26.280256],[22.579532,-25.979448],[22.824271,-25.500459],[23.312097,-25.26869],[23.73357,-25.390129],[24.211267,-25.670216],[25.025171,-25.71967],[25.664666,-25.486816],[25.765849,-25.174845],[25.941652,-24.696373],[26.485753,-24.616327],[26.786407,-24.240691],[27.11941,-23.574323],[28.017236,-22.827754],[29.432188,-22.091313],[29.839037,-22.102216],[30.322883,-22.271612],[30.659865,-22.151567],[31.191409,-22.25151],[31.670398,-23.658969],[31.930589,-24.369417],[31.752408,-25.484284],[31.837778,-25.843332],[31.333158,-25.660191],[31.04408,-25.731452],[30.949667,-26.022649],[30.676609,-26.398078],[30.685962,-26.743845],[31.282773,-27.285879],[31.86806,-27.177927],[32.071665,-26.73382],[32.83012,-26.742192],[32.580265,-27.470158],[32.462133,-28.301011],[32.203389,-28.752405],[31.521001,-29.257387]],[[28.978263,-28.955597],[28.5417,-28.647502],[28.074338,-28.851469],[27.532511,-29.242711],[26.999262,-29.875954],[27.749397,-30.645106],[28.107205,-30.545732],[28.291069,-30.226217],[28.8484,-30.070051],[29.018415,-29.743766],[29.325166,-29.257387],[28.978263,-28.955597]]]}}, +{"type":"Feature","id":"ZMB","properties":{"name":"Zambia"},"geometry":{"type":"Polygon","coordinates":[[[32.759375,-9.230599],[33.231388,-9.676722],[33.485688,-10.525559],[33.31531,-10.79655],[33.114289,-11.607198],[33.306422,-12.435778],[32.991764,-12.783871],[32.688165,-13.712858],[33.214025,-13.97186],[30.179481,-14.796099],[30.274256,-15.507787],[29.516834,-15.644678],[28.947463,-16.043051],[28.825869,-16.389749],[28.467906,-16.4684],[27.598243,-17.290831],[27.044427,-17.938026],[26.706773,-17.961229],[26.381935,-17.846042],[25.264226,-17.73654],[25.084443,-17.661816],[25.07695,-17.578823],[24.682349,-17.353411],[24.033862,-17.295843],[23.215048,-17.523116],[22.562478,-16.898451],[21.887843,-16.08031],[21.933886,-12.898437],[24.016137,-12.911046],[23.930922,-12.565848],[24.079905,-12.191297],[23.904154,-11.722282],[24.017894,-11.237298],[23.912215,-10.926826],[24.257155,-10.951993],[24.314516,-11.262826],[24.78317,-11.238694],[25.418118,-11.330936],[25.75231,-11.784965],[26.553088,-11.92444],[27.16442,-11.608748],[27.388799,-12.132747],[28.155109,-12.272481],[28.523562,-12.698604],[28.934286,-13.248958],[29.699614,-13.257227],[29.616001,-12.178895],[29.341548,-12.360744],[28.642417,-11.971569],[28.372253,-11.793647],[28.49607,-10.789884],[28.673682,-9.605925],[28.449871,-9.164918],[28.734867,-8.526559],[29.002912,-8.407032],[30.346086,-8.238257],[30.740015,-8.340007],[31.157751,-8.594579],[31.556348,-8.762049],[32.191865,-8.930359],[32.759375,-9.230599]]]}}, +{"type":"Feature","id":"ZWE","properties":{"name":"Zimbabwe"},"geometry":{"type":"Polygon","coordinates":[[[31.191409,-22.25151],[30.659865,-22.151567],[30.322883,-22.271612],[29.839037,-22.102216],[29.432188,-22.091313],[28.794656,-21.639454],[28.02137,-21.485975],[27.727228,-20.851802],[27.724747,-20.499059],[27.296505,-20.39152],[26.164791,-19.293086],[25.850391,-18.714413],[25.649163,-18.536026],[25.264226,-17.73654],[26.381935,-17.846042],[26.706773,-17.961229],[27.044427,-17.938026],[27.598243,-17.290831],[28.467906,-16.4684],[28.825869,-16.389749],[28.947463,-16.043051],[29.516834,-15.644678],[30.274256,-15.507787],[30.338955,-15.880839],[31.173064,-15.860944],[31.636498,-16.07199],[31.852041,-16.319417],[32.328239,-16.392074],[32.847639,-16.713398],[32.849861,-17.979057],[32.654886,-18.67209],[32.611994,-19.419383],[32.772708,-19.715592],[32.659743,-20.30429],[32.508693,-20.395292],[32.244988,-21.116489],[31.191409,-22.25151]]]}} +]} From d3c4869917bef0041166677f5e627c9706389473 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 3 Mar 2013 16:09:00 +0100 Subject: [PATCH 098/180] Work with new ol.projection package --- examples/vector-layer.js | 4 ++-- test/spec/ol/layer/vectorlayer.test.js | 2 +- test/spec/ol/source/vectorsource.test.js | 4 ++-- test/spec/ol/style/style.test.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/vector-layer.js b/examples/vector-layer.js index 8e33676521..916dc6406b 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -2,13 +2,13 @@ goog.require('ol.Collection'); goog.require('ol.Coordinate'); goog.require('ol.Feature'); goog.require('ol.Map'); -goog.require('ol.Projection'); goog.require('ol.RendererHint'); goog.require('ol.View2D'); goog.require('ol.geom.LineString'); goog.require('ol.geom.Point'); goog.require('ol.layer.TileLayer'); goog.require('ol.layer.Vector'); +goog.require('ol.projection'); goog.require('ol.source.MapQuestOpenAerial'); goog.require('ol.source.Vector'); @@ -18,7 +18,7 @@ var raster = new ol.layer.TileLayer({ }); var source = new ol.source.Vector({ - projection: ol.Projection.getFromCode('EPSG:3857') + projection: ol.projection.getFromCode('EPSG:3857') }); source.addFeatures([ diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js index 1c5ccd4f79..7450ba4ca3 100644 --- a/test/spec/ol/layer/vectorlayer.test.js +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -7,7 +7,7 @@ describe('ol.layer.Vector', function() { it('groups equal symbolizers', function() { var layer = new ol.layer.Vector({ source: new ol.source.Vector({ - projection: ol.Projection.getFromCode('EPSG:4326') + projection: ol.projection.getFromCode('EPSG:4326') }), style: new ol.style.Style({ rules: [ diff --git a/test/spec/ol/source/vectorsource.test.js b/test/spec/ol/source/vectorsource.test.js index b84a1114b4..3ac6685be2 100644 --- a/test/spec/ol/source/vectorsource.test.js +++ b/test/spec/ol/source/vectorsource.test.js @@ -9,7 +9,7 @@ describe('ol.source.Vector', function() { it('works', function() { vectorSource = new ol.source.Vector({ - projection: ol.Projection.getFromCode('EPSG:4326') + projection: ol.projection.getFromCode('EPSG:4326') }); vectorSource.addFeatures([new ol.Feature()]); expect(vectorSource.getFeatures().length).toEqual(1); @@ -48,7 +48,7 @@ describe('ol.source.Vector', function() { }) ]; vectorSource = new ol.source.Vector({ - projection: ol.Projection.getFromCode('EPSG:4326') + projection: ol.projection.getFromCode('EPSG:4326') }); vectorSource.addFeatures(features); }); diff --git a/test/spec/ol/style/style.test.js b/test/spec/ol/style/style.test.js index 3e93398b64..8cbe965bc4 100644 --- a/test/spec/ol/style/style.test.js +++ b/test/spec/ol/style/style.test.js @@ -29,7 +29,7 @@ describe('ol.style.Style', function() { }); }); - + describe('ol.style.Style.applyDefaultStyle()', function() { var feature = new ol.Feature(); From d4ecf53801f982ce19410d76bd6ff59e6b5149c9 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 3 Mar 2013 17:00:20 +0100 Subject: [PATCH 099/180] Adding example for rule based styling For the first time in the history of OpenLayers, we can render features with multiple symbolizers now, which is also shown in this new example. --- examples/style-rules.html | 47 ++++++++++++++++++ examples/style-rules.js | 102 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 examples/style-rules.html create mode 100644 examples/style-rules.js diff --git a/examples/style-rules.html b/examples/style-rules.html new file mode 100644 index 0000000000..aebef4be82 --- /dev/null +++ b/examples/style-rules.html @@ -0,0 +1,47 @@ + + + + + + + + + Vector Layer Example + + +
+
+

Style rules example

+
Draws features with rule based styles.
+
+

See the + style-rules.js source + to see how this is done.

+
+
+
+
vector, feature, canvas
+ + + diff --git a/examples/style-rules.js b/examples/style-rules.js new file mode 100644 index 0000000000..b2c0e1bd01 --- /dev/null +++ b/examples/style-rules.js @@ -0,0 +1,102 @@ +goog.require('ol.Collection'); +goog.require('ol.Coordinate'); +goog.require('ol.Expression'); +goog.require('ol.Feature'); +goog.require('ol.Map'); +goog.require('ol.RendererHint'); +goog.require('ol.View2D'); +goog.require('ol.filter.Filter'); +goog.require('ol.geom.LineString'); +goog.require('ol.layer.Vector'); +goog.require('ol.projection'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Line'); +goog.require('ol.style.Rule'); +goog.require('ol.style.Style'); + + +var source = new ol.source.Vector({ + projection: ol.projection.getFromCode('EPSG:3857') +}); + +source.addFeatures([ + new ol.Feature({ + g: new ol.geom.LineString([[-10000000, -10000000], [10000000, 10000000]]), + 'color': '#BADA55', + 'where': 'inner' + }), + new ol.Feature({ + g: new ol.geom.LineString([[-10000000, 10000000], [10000000, -10000000]]), + 'color': '#BADA55', + 'where': 'inner' + }), + new ol.Feature({ + g: new ol.geom.LineString([[-10000000, -10000000], [-10000000, 10000000]]), + 'color': '#013', + 'where': 'outer' + }), + new ol.Feature({ + g: new ol.geom.LineString([[-10000000, 10000000], [10000000, 10000000]]), + 'color': '#013', + 'where': 'outer' + }), + new ol.Feature({ + g: new ol.geom.LineString([[10000000, 10000000], [10000000, -10000000]]), + 'color': '#013', + 'where': 'outer' + }), + new ol.Feature({ + g: new ol.geom.LineString([[10000000, -10000000], [-10000000, -10000000]]), + 'color': '#013', + 'where': 'outer' + }) +]); + +var style = new ol.style.Style({ + rules: [ + new ol.style.Rule({ + filter: new ol.filter.Filter(function(feature) { + return feature.get('where') == 'outer'; + }), + symbolizers: [ + new ol.style.Line({ + strokeStyle: new ol.Expression('color'), + strokeWidth: 4, + opacity: 1 + }) + ] + }), + new ol.style.Rule({ + filter: new ol.filter.Filter(function(feature) { + return feature.get('where') == 'inner'; + }), + symbolizers: [ + new ol.style.Line({ + strokeStyle: '#013', + strokeWidth: 4, + opacity: 1 + }), + new ol.style.Line({ + strokeStyle: new ol.Expression('color'), + strokeWidth: 2, + opacity: 1 + }) + ] + }) + ] +}); + +var vector = new ol.layer.Vector({ + source: source, + style: style +}); + +var map = new ol.Map({ + layers: new ol.Collection([vector]), + renderer: ol.RendererHint.CANVAS, + target: 'map', + view: new ol.View2D({ + center: new ol.Coordinate(0, 0), + zoom: 2 + }) +}); From 022917e719f642d44d62edbdd918a2b5a937938a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 3 Mar 2013 16:20:49 +0100 Subject: [PATCH 100/180] Lint free externs --- externs/geojson.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/externs/geojson.js b/externs/geojson.js index 6645f8a79e..a67f9a8940 100644 --- a/externs/geojson.js +++ b/externs/geojson.js @@ -6,6 +6,7 @@ */ + /** * @constructor */ @@ -24,6 +25,7 @@ GeoJSONObject.prototype.type; GeoJSONObject.prototype.crs; + /** * @constructor * @extends {GeoJSONObject} @@ -52,6 +54,7 @@ var GeoJSONGeometry = function() {}; GeoJSONGeometry.prototype.coordinates; + /** * @constructor * @extends {GeoJSONObject} @@ -65,6 +68,7 @@ var GeoJSONGeometryCollection = function() {}; GeoJSONGeometryCollection.prototype.geometries; + /** * @constructor * @extends {GeoJSONObject} @@ -102,5 +106,3 @@ GeoJSONFeatureCollection.prototype.features; * @type {!Array.|undefined} */ GeoJSONFeatureCollection.prototype.bbox; - - From f1120287c32d203615747d1a5e62fdd517d73fa5 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 3 Mar 2013 18:55:52 +0100 Subject: [PATCH 101/180] Features can now have symbolizers If specified, feature symbolizers take precedence over rule based styling. --- src/ol/feature.js | 31 +++++++++++ src/ol/layer/vectorlayer.js | 21 +++++--- test/spec/ol/layer/vectorlayer.test.js | 73 ++++++++++++++++++-------- 3 files changed, 96 insertions(+), 29 deletions(-) diff --git a/src/ol/feature.js b/src/ol/feature.js index 162a5d7a3a..3af0f64d80 100644 --- a/src/ol/feature.js +++ b/src/ol/feature.js @@ -20,6 +20,12 @@ ol.Feature = function(opt_values) { */ this.geometryName_; + /** + * @type {Array.} + * @private + */ + this.symbolizers_ = null; + }; goog.inherits(ol.Feature, ol.Object); @@ -50,6 +56,22 @@ ol.Feature.prototype.getGeometry = function() { }; +/** + * @return {Array.} Symbolizer literals. + */ +ol.Feature.prototype.getSymbolizerLiterals = function() { + var symbolizerLiterals = null; + if (!goog.isNull(this.symbolizers_)) { + var numSymbolizers = this.symbolizers_.length; + symbolizerLiterals = new Array(numSymbolizers); + for (var i = 0; i < numSymbolizers; ++i) { + symbolizerLiterals[i] = this.symbolizers_[i].createLiteral(this); + } + } + return symbolizerLiterals; +}; + + /** * @inheritDoc * @param {string} key Key. @@ -74,6 +96,15 @@ ol.Feature.prototype.setGeometry = function(geometry) { }; +/** + * @param {Array.} symbolizers Symbolizers for this + * features. If set, these take precedence over layer style. + */ +ol.Feature.prototype.setSymbolizers = function(symbolizers) { + this.symbolizers_ = symbolizers; +}; + + /** * @const * @type {string} diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 6397544d57..6703ec3e3c 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -42,16 +42,21 @@ ol.layer.Vector.prototype.groupFeaturesBySymbolizerLiteral = var uniqueLiterals = {}, featuresBySymbolizer = [], style = this.style_, - feature, literals, literal, uniqueLiteral, key; - for (var i = 0, ii = features.length; i < ii; ++i) { + numFeatures = features.length, + i, j, l, feature, literals, numLiterals, literal, uniqueLiteral, key; + for (i = 0; i < numFeatures; ++i) { feature = features[i]; - literals = goog.isNull(style) ? - ol.style.Style.applyDefaultStyle(feature) : - style.apply(feature); - for (var j = 0, jj = literals.length; j < jj; ++j) { + literals = feature.getSymbolizerLiterals(); + if (goog.isNull(literals)) { + literals = goog.isNull(style) ? + ol.style.Style.applyDefaultStyle(feature) : + style.apply(feature); + } + numLiterals = literals.length; + for (j = 0; j < numLiterals; ++j) { literal = literals[j]; - for (var l in uniqueLiterals) { - uniqueLiteral = featuresBySymbolizer[uniqueLiterals[key]][1]; + for (l in uniqueLiterals) { + uniqueLiteral = featuresBySymbolizer[uniqueLiterals[l]][1]; if (literal.equals(uniqueLiteral)) { literal = uniqueLiteral; break; diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js index 7450ba4ca3..8cc0f941da 100644 --- a/test/spec/ol/layer/vectorlayer.test.js +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -4,26 +4,28 @@ describe('ol.layer.Vector', function() { describe('#groupFeaturesBySymbolizerLiteral()', function() { + var layer = new ol.layer.Vector({ + source: new ol.source.Vector({ + projection: ol.projection.getFromCode('EPSG:4326') + }), + style: new ol.style.Style({ + rules: [ + new ol.style.Rule({ + symbolizers: [ + new ol.style.Line({ + strokeWidth: 2, + strokeStyle: new ol.Expression('colorProperty'), + opacity: 1 + }) + ] + }) + ] + }) + }); + var features; + it('groups equal symbolizers', function() { - var layer = new ol.layer.Vector({ - source: new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:4326') - }), - style: new ol.style.Style({ - rules: [ - new ol.style.Rule({ - symbolizers: [ - new ol.style.Line({ - strokeWidth: 2, - strokeStyle: new ol.Expression('colorProperty'), - opacity: 1 - }) - ] - }) - ] - }) - }); - var features = [ + features = [ new ol.Feature({ g: new ol.geom.LineString([[-10, -10], [10, 10]]), colorProperty: '#BADA55' @@ -44,10 +46,39 @@ describe('ol.layer.Vector', function() { expect(groups[0][1].strokeStyle).toBe('#BADA55'); expect(groups[1][0].length).toBe(2); expect(groups[1][1].strokeStyle).toBe('#013'); - - layer.dispose(); }); + it('groups equal symbolizers also when defined on features', function() { + var symbolizer = new ol.style.Line({ + strokeWidth: 3, + strokeStyle: new ol.Expression('colorProperty'), + opacity: 1 + }); + var anotherSymbolizer = new ol.style.Line({ + strokeWidth: 3, + strokeStyle: '#BADA55', + opacity: 1 + }); + var featureWithSymbolizers = new ol.Feature({ + g: new ol.geom.LineString([[-10, -10], [-10, 10]]), + colorProperty: '#BADA55' + }); + featureWithSymbolizers.setSymbolizers([symbolizer]); + var anotherFeatureWithSymbolizers = new ol.Feature({ + g: new ol.geom.LineString([[-10, 10], [-10, -10]]) + }); + anotherFeatureWithSymbolizers.setSymbolizers([anotherSymbolizer]); + features.push(featureWithSymbolizers, anotherFeatureWithSymbolizers); + + var groups = layer.groupFeaturesBySymbolizerLiteral(features); + expect(groups.length).toBe(3); + expect(groups[2][0].length).toBe(2); + expect(groups[2][1].strokeWidth).toBe(3); + + }); + + layer.dispose(); + }); }); From 0558cfa3acf0e21f0e17b43dc29764f784bbbf29 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 4 Mar 2013 18:41:04 +0100 Subject: [PATCH 102/180] Add externs for GeoJSON support --- build/ol-simple.json | 2 ++ build/ol-whitespace.json | 2 ++ build/ol.json | 2 ++ 3 files changed, 6 insertions(+) diff --git a/build/ol-simple.json b/build/ol-simple.json index 27a982ee23..ed07e263c8 100644 --- a/build/ol-simple.json +++ b/build/ol-simple.json @@ -3,7 +3,9 @@ "id": "ol-simple", "externs": [ + "//json.js", "externs/bingmaps.js", + "externs/geojson.js", "externs/proj4js.js", "externs/tilejson.js" ], diff --git a/build/ol-whitespace.json b/build/ol-whitespace.json index 83294ee47c..5564f8a11e 100644 --- a/build/ol-whitespace.json +++ b/build/ol-whitespace.json @@ -3,7 +3,9 @@ "id": "ol-whitespace", "externs": [ + "//json.js", "externs/bingmaps.js", + "externs/geojson.js", "externs/proj4js.js", "externs/tilejson.js" ], diff --git a/build/ol.json b/build/ol.json index 33e8c7bde6..7a44e1cba6 100644 --- a/build/ol.json +++ b/build/ol.json @@ -13,8 +13,10 @@ "css-output-file": "build/ol.css", "externs": [ + "//json.js", "build/src/external/externs/types.js", "externs/bingmaps.js", + "externs/geojson.js", "externs/proj4js.js", "externs/tilejson.js" ], From 0b05432faab2bca5804c504dde816c8a7898ba50 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 4 Mar 2013 18:43:46 +0100 Subject: [PATCH 103/180] Vector source isn't a tile source --- src/ol/source/vectorsource.js | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index edbe74546f..1dc6c0a0a1 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -10,9 +10,8 @@ goog.require('ol.filter.Geometry'); goog.require('ol.filter.Logical'); goog.require('ol.filter.LogicalOperator'); goog.require('ol.geom.GeometryType'); -goog.require('ol.source.TileSource'); +goog.require('ol.source.Source'); goog.require('ol.structs.RTree'); -goog.require('ol.tilegrid.TileGrid'); @@ -177,8 +176,7 @@ ol.source.FeatureCache.prototype.getFeaturesByIds_ = function(ids) { /** * @typedef {{attributions: (Array.|undefined), * extent: (ol.Extent|undefined), - * projection: (ol.Projection|undefined), - * tileGrid: (ol.tilegrid.TileGrid|undefined)}} + * projection: (ol.Projection|undefined)}} */ ol.source.VectorOptions; @@ -186,7 +184,7 @@ ol.source.VectorOptions; /** * @constructor - * @extends {ol.source.TileSource} + * @extends {ol.source.Source} * @param {ol.source.VectorOptions} options Source options. */ ol.source.Vector = function(options) { @@ -194,8 +192,7 @@ ol.source.Vector = function(options) { goog.base(this, { attributions: options.attributions, extent: options.extent, - projection: options.projection, - tileGrid: options.tileGrid + projection: options.projection }); /** @@ -205,15 +202,7 @@ ol.source.Vector = function(options) { this.featureCache_ = new ol.source.FeatureCache(); }; -goog.inherits(ol.source.Vector, ol.source.TileSource); - - -/** - * @param {ol.tilegrid.TileGrid} tileGrid tile grid. - */ -ol.source.Vector.prototype.setTileGrid = function(tileGrid) { - this.tileGrid = tileGrid; -}; +goog.inherits(ol.source.Vector, ol.source.Source); /** From fae79dbc0f8ad63c99d703d3e3719fa1382fd9b6 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 4 Mar 2013 18:55:20 +0100 Subject: [PATCH 104/180] Fewer function calls during bounds calculation --- src/ol/geom/linestring.js | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index 561c973a33..79017c7cdc 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -99,23 +99,30 @@ ol.geom.LineString.prototype.getCount = function() { */ ol.geom.LineString.prototype.getBounds = function() { if (goog.isNull(this.bounds_)) { - var minX, - minY = minX = Number.POSITIVE_INFINITY, - maxX, - maxY = maxX = Number.NEGATIVE_INFINITY, + var minX, minY = minX = Number.POSITIVE_INFINITY, + maxX, maxY = maxX = Number.NEGATIVE_INFINITY, + dimension = this.dimension, vertices = this.vertices, id = this.sharedId_, count = vertices.getCount(id), - dimension = this.dimension, + start = vertices.getStart(id), + end = start + (count * dimension), + coordinates = vertices.coordinates, x, y, i; - 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); - maxY = Math.max(maxY, y); + for (i = start; i < end; i += dimension) { + x = coordinates[i]; + y = coordinates[i + 1]; + if (x < minX) { + minX = x; + } else if (x > maxX) { + maxX = x; + } + if (y < minY) { + minY = y; + } else if (y > maxY) { + maxY = y; + } } this.bounds_ = new ol.Extent(minX, minY, maxX, maxY); } From 978527f88009a11c91faa54166557a7b2e5ad05a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 4 Mar 2013 19:31:11 +0100 Subject: [PATCH 105/180] Accept missing properties in symbolizer constructors Still need to have some discussion about when to apply defaults. --- src/ol/style/line.js | 6 +++--- src/ol/style/polygon.js | 8 ++++---- src/ol/style/shape.js | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ol/style/line.js b/src/ol/style/line.js index 7d8519067f..1f8d4bea80 100644 --- a/src/ol/style/line.js +++ b/src/ol/style/line.js @@ -48,9 +48,9 @@ ol.style.LineLiteral.prototype.equals = function(lineLiteral) { /** - * @typedef {{strokeStyle: (string|ol.Expression), - * strokeWidth: (number|ol.Expression), - * opacity: (number|ol.Expression)}} + * @typedef {{strokeStyle: (string|ol.Expression|undefined), + * strokeWidth: (number|ol.Expression|undefined), + * opacity: (number|ol.Expression|undefined)}} */ ol.style.LineOptions; diff --git a/src/ol/style/polygon.js b/src/ol/style/polygon.js index 5886ccf474..7a126abd99 100644 --- a/src/ol/style/polygon.js +++ b/src/ol/style/polygon.js @@ -53,10 +53,10 @@ ol.style.PolygonLiteral.prototype.equals = function(polygonLiteral) { /** - * @typedef {{fillStyle: (string|ol.Expression), - * strokeStyle: (string|ol.Expression), - * strokeWidth: (number|ol.Expression), - * opacity: (number|ol.Expression)}} + * @typedef {{fillStyle: (string|ol.Expression|undefined), + * strokeStyle: (string|ol.Expression|undefined), + * strokeWidth: (number|ol.Expression|undefined), + * opacity: (number|ol.Expression|undefined)}} */ ol.style.PolygonOptions; diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js index 92c407c452..6fe00005e0 100644 --- a/src/ol/style/shape.js +++ b/src/ol/style/shape.js @@ -71,12 +71,12 @@ ol.style.ShapeLiteral.prototype.equals = function(shapeLiteral) { /** - * @typedef {{type: (ol.style.ShapeType), - * size: (number|ol.Expression), - * fillStyle: (string|ol.Expression), - * strokeStyle: (string|ol.Expression), - * strokeWidth: (number|ol.Expression), - * opacity: (number|ol.Expression)}} + * @typedef {{type: (ol.style.ShapeType|undefined), + * size: (number|ol.Expression|undefined), + * fillStyle: (string|ol.Expression|undefined), + * strokeStyle: (string|ol.Expression|undefined), + * strokeWidth: (number|ol.Expression|undefined), + * opacity: (number|ol.Expression|undefined)}} */ ol.style.ShapeOptions; From 9edf63fdb87705259300df5d6839932cc5152dfd Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 4 Mar 2013 20:21:08 +0100 Subject: [PATCH 106/180] Proper bounds --- src/ol/geom/linestring.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index 79017c7cdc..822af57277 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -99,18 +99,20 @@ ol.geom.LineString.prototype.getCount = function() { */ ol.geom.LineString.prototype.getBounds = function() { if (goog.isNull(this.bounds_)) { - var minX, minY = minX = Number.POSITIVE_INFINITY, - maxX, maxY = maxX = Number.NEGATIVE_INFINITY, - dimension = this.dimension, + var dimension = this.dimension, vertices = this.vertices, id = this.sharedId_, count = vertices.getCount(id), start = vertices.getStart(id), end = start + (count * dimension), coordinates = vertices.coordinates, + minX, maxX, + minY, maxY, x, y, i; - for (i = start; i < end; i += dimension) { + minX = maxX = coordinates[start]; + minY = maxY = coordinates[start + 1]; + for (i = start + dimension; i < end; i += dimension) { x = coordinates[i]; y = coordinates[i + 1]; if (x < minX) { From 9c8ec427138e558025803d955043b67830dc55d6 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 4 Mar 2013 20:29:42 +0100 Subject: [PATCH 107/180] Feature parser interfaces Parsers that read features from strings, objects, or dom elements should implement the right interface. Among the read options is a callback that gets called after feature construction and before geometry construction. This callback is called with the feature and the geometry type and returns a shared vertices structure. The geometry is then constructed with this shared vertices structure before being added to the feature. --- src/ol/parser/featureparser.js | 66 ++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/ol/parser/featureparser.js diff --git a/src/ol/parser/featureparser.js b/src/ol/parser/featureparser.js new file mode 100644 index 0000000000..686fcb2443 --- /dev/null +++ b/src/ol/parser/featureparser.js @@ -0,0 +1,66 @@ +goog.provide('ol.parser.DomFeatureParser'); +goog.provide('ol.parser.ObjectFeatureParser'); +goog.provide('ol.parser.ReadFeaturesOptions'); +goog.provide('ol.parser.StringFeatureParser'); + +goog.require('ol.Feature'); + + + +/** + * @interface + */ +ol.parser.DomFeatureParser = function() {}; + + +/** + * @param {Element|Document} node Document or element node. + * @param {ol.parser.ReadFeaturesOptions=} opt_options Feature reading options. + * @return {Array.} Array of features. + */ +ol.parser.DomFeatureParser.prototype.readFeaturesFromNode = + goog.abstractMethod; + + + +/** + * @interface + */ +ol.parser.ObjectFeatureParser = function() {}; + + +/** + * @param {Object} obj Object representing features. + * @param {ol.parser.ReadFeaturesOptions=} opt_options Feature reading options. + * @return {Array.} Array of features. + */ +ol.parser.ObjectFeatureParser.prototype.readFeaturesFromObject = + goog.abstractMethod; + + + +/** + * @interface + */ +ol.parser.StringFeatureParser = function() {}; + + +/** + * @param {string} data String data. + * @param {ol.parser.ReadFeaturesOptions=} opt_options Feature reading options. + * @return {Array.} Array of features. + */ +ol.parser.StringFeatureParser.prototype.readFeaturesFromString = + goog.abstractMethod; + + +/** + * @typedef {function(ol.Feature, ol.geom.GeometryType):ol.geom.SharedVertices} + */ +ol.parser.ReadFeaturesCallback; + + +/** + * @typedef {{callback: ol.parser.ReadFeaturesCallback}} + */ +ol.parser.ReadFeaturesOptions; From 8c8ddce290f016117e63fd77b18258caceee6d8a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 4 Mar 2013 20:31:01 +0100 Subject: [PATCH 108/180] GeoJSON parser implements StringFeatureParser --- src/ol/parser/geojson.js | 155 +++++++++++++++++----------- test/spec/ol/parser/geojson.test.js | 19 +++- 2 files changed, 111 insertions(+), 63 deletions(-) diff --git a/src/ol/parser/geojson.js b/src/ol/parser/geojson.js index cb5863e243..94624e3d7a 100644 --- a/src/ol/parser/geojson.js +++ b/src/ol/parser/geojson.js @@ -2,6 +2,7 @@ goog.provide('ol.parser.GeoJSON'); goog.require('ol.Feature'); goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); goog.require('ol.geom.MultiLineString'); goog.require('ol.geom.MultiPoint'); @@ -10,11 +11,14 @@ goog.require('ol.geom.Point'); goog.require('ol.geom.Polygon'); goog.require('ol.geom.SharedVertices'); goog.require('ol.parser.Parser'); +goog.require('ol.parser.ReadFeaturesOptions'); +goog.require('ol.parser.StringFeatureParser'); /** * @constructor + * @implements {ol.parser.StringFeatureParser} * @extends {ol.parser.Parser} */ ol.parser.GeoJSON = function() {}; @@ -24,63 +28,74 @@ goog.inherits(ol.parser.GeoJSON, ol.parser.Parser); /** * Parse a GeoJSON string. * @param {string} str GeoJSON string. - * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {ol.Feature|Array.| * ol.geom.Geometry|Array.} Parsed geometry or array * of geometries. */ -ol.parser.GeoJSON.prototype.read = function(str, opt_options) { +ol.parser.GeoJSON.prototype.read = function(str) { var json = /** @type {GeoJSONObject} */ (JSON.parse(str)); - return this.parse_(json, opt_options); + return this.parse_(json); +}; + + +/** + * Parse a GeoJSON feature collection. + * @param {string} str GeoJSON feature collection. + * @param {ol.parser.ReadFeaturesOptions=} opt_options Reader options. + * @return {Array.} Array of features. + */ +ol.parser.GeoJSON.prototype.readFeaturesFromString = + function(str, opt_options) { + var json = /** @type {GeoJSONFeatureCollection} */ (JSON.parse(str)); + return this.parseFeatureCollection_(json, opt_options); }; /** * @param {GeoJSONObject} json GeoJSON object. - * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {ol.Feature|Array.| * ol.geom.Geometry|Array.} Parsed geometry or array * of geometries. * @private */ -ol.parser.GeoJSON.prototype.parse_ = function(json, opt_options) { +ol.parser.GeoJSON.prototype.parse_ = function(json) { var result; switch (json.type) { case 'FeatureCollection': result = this.parseFeatureCollection_( - /** @type {GeoJSONFeatureCollection} */ (json), opt_options); + /** @type {GeoJSONFeatureCollection} */ (json)); break; case 'Feature': result = this.parseFeature_( - /** @type {GeoJSONFeature} */ (json), opt_options); + /** @type {GeoJSONFeature} */ (json)); break; case 'GeometryCollection': result = this.parseGeometryCollection_( - /** @type {GeoJSONGeometryCollection} */ (json), opt_options); + /** @type {GeoJSONGeometryCollection} */ (json)); break; case 'Point': result = this.parsePoint_( - /** @type {GeoJSONGeometry} */ (json), opt_options); + /** @type {GeoJSONGeometry} */ (json)); break; case 'LineString': result = this.parseLineString_( - /** @type {GeoJSONGeometry} */ (json), opt_options); + /** @type {GeoJSONGeometry} */ (json)); break; case 'Polygon': result = this.parsePolygon_( - /** @type {GeoJSONGeometry} */ (json), opt_options); + /** @type {GeoJSONGeometry} */ (json)); break; case 'MultiPoint': result = this.parseMultiPoint_( - /** @type {GeoJSONGeometry} */ (json), opt_options); + /** @type {GeoJSONGeometry} */ (json)); break; case 'MultiLineString': result = this.parseMultiLineString_( - /** @type {GeoJSONGeometry} */ (json), opt_options); + /** @type {GeoJSONGeometry} */ (json)); break; case 'MultiPolygon': result = this.parseMultiPolygon_( - /** @type {GeoJSONGeometry} */ (json), opt_options); + /** @type {GeoJSONGeometry} */ (json)); break; default: throw new Error('GeoJSON parsing not implemented for type: ' + json.type); @@ -91,27 +106,55 @@ ol.parser.GeoJSON.prototype.parse_ = function(json, opt_options) { /** * @param {GeoJSONFeature} json GeoJSON feature. - * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. + * @param {ol.parser.ReadFeaturesOptions=} opt_options Read options. * @return {ol.Feature} Parsed feature. * @private */ ol.parser.GeoJSON.prototype.parseFeature_ = function(json, opt_options) { var geomJson = json.geometry, - geometry = null; + geometry = null, + options = opt_options || {}; + var feature = new ol.Feature(json.properties); if (geomJson) { - geometry = /** @type {ol.geom.Geometry} */ (this.parse_( - /** @type {GeoJSONGeometry} */ (geomJson), opt_options)); + var type = geomJson.type; + var callback = options.callback; + var sharedVertices; + if (callback) { + goog.asserts.assert(type in ol.parser.GeoJSON.GeometryType, + 'Bad geometry type: ' + type); + sharedVertices = callback(feature, ol.parser.GeoJSON.GeometryType[type]); + } + switch (type) { + case 'Point': + geometry = this.parsePoint_(geomJson, sharedVertices); + break; + case 'LineString': + geometry = this.parseLineString_(geomJson, sharedVertices); + break; + case 'Polygon': + geometry = this.parsePolygon_(geomJson, sharedVertices); + break; + case 'MultiPoint': + geometry = this.parseMultiPoint_(geomJson, sharedVertices); + break; + case 'MultiLineString': + geometry = this.parseMultiLineString_(geomJson, sharedVertices); + break; + case 'MultiPolygon': + geometry = this.parseMultiPolygon_(geomJson, sharedVertices); + break; + default: + throw new Error('Bad geometry type: ' + type); + } + feature.setGeometry(geometry); } - var feature = new ol.Feature(); - feature.setGeometry(geometry); - feature.setValues(json.properties); return feature; }; /** * @param {GeoJSONFeatureCollection} json GeoJSON feature collection. - * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. + * @param {ol.parser.ReadFeaturesOptions=} opt_options Read options. * @return {Array.} Parsed array of features. * @private */ @@ -123,7 +166,7 @@ ol.parser.GeoJSON.prototype.parseFeatureCollection_ = function( i; for (i = 0; i < len; ++i) { - result[i] = this.parse_( + result[i] = this.parseFeature_( /** @type {GeoJSONFeature} */ (features[i]), opt_options); } return result; @@ -132,20 +175,17 @@ ol.parser.GeoJSON.prototype.parseFeatureCollection_ = function( /** * @param {GeoJSONGeometryCollection} json GeoJSON geometry collection. - * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. * @return {Array.} Parsed array of geometries. * @private */ -ol.parser.GeoJSON.prototype.parseGeometryCollection_ = function( - json, opt_options) { +ol.parser.GeoJSON.prototype.parseGeometryCollection_ = function(json) { var geometries = json.geometries, len = geometries.length, result = new Array(len), i; for (i = 0; i < len; ++i) { - result[i] = this.parse_( - /** @type {GeoJSONGeometry} */ (geometries[i]), opt_options); + result[i] = this.parse_(/** @type {GeoJSONGeometry} */ (geometries[i])); } return result; }; @@ -153,81 +193,80 @@ ol.parser.GeoJSON.prototype.parseGeometryCollection_ = function( /** * @param {GeoJSONGeometry} json GeoJSON linestring. - * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. + * @param {ol.geom.SharedVertices=} opt_vertices Shared vertices. * @return {ol.geom.LineString} Parsed linestring. * @private */ -ol.parser.GeoJSON.prototype.parseLineString_ = function(json, opt_options) { - var vertices = opt_options && opt_options.lineVertices; - return new ol.geom.LineString(json.coordinates, vertices); +ol.parser.GeoJSON.prototype.parseLineString_ = function(json, opt_vertices) { + return new ol.geom.LineString(json.coordinates, opt_vertices); }; /** * @param {GeoJSONGeometry} json GeoJSON multi-linestring. - * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. + * @param {ol.geom.SharedVertices=} opt_vertices Shared vertices. * @return {ol.geom.MultiLineString} Parsed multi-linestring. * @private */ ol.parser.GeoJSON.prototype.parseMultiLineString_ = function( - json, opt_options) { - var vertices = opt_options && opt_options.lineVertices; - return new ol.geom.MultiLineString(json.coordinates, vertices); + json, opt_vertices) { + return new ol.geom.MultiLineString(json.coordinates, opt_vertices); }; /** * @param {GeoJSONGeometry} json GeoJSON multi-point. - * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. + * @param {ol.geom.SharedVertices=} opt_vertices Shared vertices. * @return {ol.geom.MultiPoint} Parsed multi-point. * @private */ -ol.parser.GeoJSON.prototype.parseMultiPoint_ = function(json, opt_options) { - var vertices = opt_options && opt_options.pointVertices; - return new ol.geom.MultiPoint(json.coordinates, vertices); +ol.parser.GeoJSON.prototype.parseMultiPoint_ = function(json, opt_vertices) { + return new ol.geom.MultiPoint(json.coordinates, opt_vertices); }; /** * @param {GeoJSONGeometry} json GeoJSON multi-polygon. - * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. + * @param {ol.geom.SharedVertices=} opt_vertices Shared vertices. * @return {ol.geom.MultiPolygon} Parsed multi-polygon. * @private */ -ol.parser.GeoJSON.prototype.parseMultiPolygon_ = function(json, opt_options) { - var vertices = opt_options && opt_options.polygonVertices; - return new ol.geom.MultiPolygon(json.coordinates, vertices); +ol.parser.GeoJSON.prototype.parseMultiPolygon_ = function(json, opt_vertices) { + return new ol.geom.MultiPolygon(json.coordinates, opt_vertices); }; /** * @param {GeoJSONGeometry} json GeoJSON point. - * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. - * @return {ol.geom.Point} Parsed multi-point. + * @param {ol.geom.SharedVertices=} opt_vertices Shared vertices. + * @return {ol.geom.Point} Parsed point. * @private */ -ol.parser.GeoJSON.prototype.parsePoint_ = function(json, opt_options) { - var vertices = opt_options && opt_options.pointVertices; - return new ol.geom.Point(json.coordinates, vertices); +ol.parser.GeoJSON.prototype.parsePoint_ = function(json, opt_vertices) { + return new ol.geom.Point(json.coordinates, opt_vertices); }; /** * @param {GeoJSONGeometry} json GeoJSON polygon. - * @param {ol.parser.GeoJSON.ReadOptions=} opt_options Read options. + * @param {ol.geom.SharedVertices=} opt_vertices Shared vertices. * @return {ol.geom.Polygon} Parsed polygon. * @private */ -ol.parser.GeoJSON.prototype.parsePolygon_ = function(json, opt_options) { - var vertices = opt_options && opt_options.polygonVertices; - return new ol.geom.Polygon(json.coordinates, vertices); +ol.parser.GeoJSON.prototype.parsePolygon_ = function(json, opt_vertices) { + return new ol.geom.Polygon(json.coordinates, opt_vertices); }; /** - * @typedef {{pointVertices: (ol.geom.SharedVertices|undefined), - * lineVertices: (ol.geom.SharedVertices|undefined), - * polygonVertices: (ol.geom.SharedVertices|undefined)}} - * TODO: add support for toProjection + * @enum {ol.geom.GeometryType} */ -ol.parser.GeoJSON.ReadOptions; +ol.parser.GeoJSON.GeometryType = { + 'Point': ol.geom.GeometryType.POINT, + 'LineString': ol.geom.GeometryType.LINESTRING, + 'Polygon': ol.geom.GeometryType.POLYGON, + 'MultiPoint': ol.geom.GeometryType.MULTIPOINT, + 'MultiLineString': ol.geom.GeometryType.MULTILINESTRING, + 'MultiPolygon': ol.geom.GeometryType.MULTIPOLYGON, + 'GeometryCollection': ol.geom.GeometryType.GEOMETRYCOLLECTION +}; diff --git a/test/spec/ol/parser/geojson.test.js b/test/spec/ol/parser/geojson.test.js index 16a807125e..ceee0b5a6b 100644 --- a/test/spec/ol/parser/geojson.test.js +++ b/test/spec/ol/parser/geojson.test.js @@ -174,11 +174,20 @@ describe('ol.parser.GeoJSON', function() { var lineVertices = new ol.geom.SharedVertices(); var polygonVertices = new ol.geom.SharedVertices(); - var result = parser.read(text, { - pointVertices: pointVertices, - lineVertices: lineVertices, - polygonVertices: polygonVertices - }); + var lookup = { + 'point': pointVertices, + 'linestring': lineVertices, + 'polygon': polygonVertices, + 'multipoint': pointVertices, + 'multilinstring': lineVertices, + 'multipolygon': polygonVertices + }; + + var callback = function(feature, type) { + return lookup[type]; + } + + var result = parser.readFeaturesFromString(text, {callback: callback}); expect(result.length).toBe(179); expect(pointVertices.coordinates.length).toBe(0); From cae925ad1f5b5cbf753694f07c4dfc4d86e58593 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 4 Mar 2013 21:04:59 +0100 Subject: [PATCH 109/180] Correct spelling (thanks @fredj) --- src/ol/geom/geometry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/geom/geometry.js b/src/ol/geom/geometry.js index 4e61d692f6..f4fd967848 100644 --- a/src/ol/geom/geometry.js +++ b/src/ol/geom/geometry.js @@ -28,7 +28,7 @@ ol.geom.Geometry.prototype.dimension; /** - * Get the rectangular 2D evelope for this geoemtry. + * Get the rectangular 2D envelope for this geoemtry. * @return {ol.Extent} The bounding rectangular envelope. */ ol.geom.Geometry.prototype.getBounds = goog.abstractMethod; From dc19948a8560cd571d44bf910ad2850343c57dab Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 4 Mar 2013 21:30:01 +0100 Subject: [PATCH 110/180] Get tile size for zoom level (see #258) --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 38a82edfb7..b4560e2e88 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -180,12 +180,12 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = } // set up transform for the layer canvas to be drawn to the map canvas - var tileSize = tileGrid.getTileSize(), - z = tileGrid.getZForResolution(resolution), + var z = tileGrid.getZForResolution(resolution), tileResolution = tileGrid.getResolution(z), tileRange = tileGrid.getTileRangeForExtentAndResolution( extent, tileResolution), tileRangeExtent = tileGrid.getTileRangeExtent(z, tileRange), + tileSize = tileGrid.getTileSize(z), sketchOrigin = tileRangeExtent.getTopLeft(), transform = this.transform_; From 1d6cd1bd1ba26643c763886fd2cb3c38d01cee2f Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Mon, 4 Mar 2013 21:30:23 +0100 Subject: [PATCH 111/180] Lint-free tests --- test/spec/ol/geom/multilinestring.test.js | 3 ++- test/spec/ol/geom/multipoint.test.js | 1 + test/spec/ol/geom/multipolygon.test.js | 1 + test/spec/ol/geom/polygon.test.js | 1 + test/spec/ol/layer/vectorlayer.test.js | 10 ++++++++++ test/spec/ol/parser/geojson.test.js | 1 + test/spec/ol/source/vectorsource.test.js | 4 ++++ test/spec/ol/style/style.test.js | 3 +++ 8 files changed, 23 insertions(+), 1 deletion(-) diff --git a/test/spec/ol/geom/multilinestring.test.js b/test/spec/ol/geom/multilinestring.test.js index 8bca72fa5c..68dde8b81d 100644 --- a/test/spec/ol/geom/multilinestring.test.js +++ b/test/spec/ol/geom/multilinestring.test.js @@ -14,7 +14,7 @@ describe('ol.geom.MultiLineString', function() { it('throws when given with insufficient dimensions', function() { expect(function() { - var multi = new ol.geom.MultiPoint([1]); + var multi = new ol.geom.MultiLineString([1]); }).toThrow(); }); @@ -84,4 +84,5 @@ describe('ol.geom.MultiLineString', function() { }); goog.require('ol.geom.Geometry'); +goog.require('ol.geom.LineString'); goog.require('ol.geom.MultiLineString'); diff --git a/test/spec/ol/geom/multipoint.test.js b/test/spec/ol/geom/multipoint.test.js index d373e6031d..d3be83bc56 100644 --- a/test/spec/ol/geom/multipoint.test.js +++ b/test/spec/ol/geom/multipoint.test.js @@ -71,3 +71,4 @@ describe('ol.geom.MultiPoint', function() { goog.require('ol.geom.Geometry'); goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.Point'); diff --git a/test/spec/ol/geom/multipolygon.test.js b/test/spec/ol/geom/multipolygon.test.js index cd2ad19793..7a46ca78b5 100644 --- a/test/spec/ol/geom/multipolygon.test.js +++ b/test/spec/ol/geom/multipolygon.test.js @@ -89,3 +89,4 @@ describe('ol.geom.MultiPolygon', function() { goog.require('ol.geom.Geometry'); goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Polygon'); diff --git a/test/spec/ol/geom/polygon.test.js b/test/spec/ol/geom/polygon.test.js index 199c0df9ec..cec6cbe2da 100644 --- a/test/spec/ol/geom/polygon.test.js +++ b/test/spec/ol/geom/polygon.test.js @@ -84,5 +84,6 @@ describe('ol.geom.Polygon', function() { }); goog.require('ol.geom.Geometry'); +goog.require('ol.geom.LinearRing'); goog.require('ol.geom.Polygon'); goog.require('ol.geom.SharedVertices'); diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js index 8cc0f941da..c08053419d 100644 --- a/test/spec/ol/layer/vectorlayer.test.js +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -82,3 +82,13 @@ describe('ol.layer.Vector', function() { }); }); + +goog.require('ol.Expression'); +goog.require('ol.Feature'); +goog.require('ol.geom.LineString'); +goog.require('ol.projection'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Line'); +goog.require('ol.style.Rule'); +goog.require('ol.style.Style'); diff --git a/test/spec/ol/parser/geojson.test.js b/test/spec/ol/parser/geojson.test.js index ceee0b5a6b..f5e750aa4d 100644 --- a/test/spec/ol/parser/geojson.test.js +++ b/test/spec/ol/parser/geojson.test.js @@ -218,6 +218,7 @@ describe('ol.parser.GeoJSON', function() { }); +goog.require('ol.Extent'); goog.require('ol.Feature'); goog.require('ol.geom.LinearRing'); goog.require('ol.geom.LineString'); diff --git a/test/spec/ol/source/vectorsource.test.js b/test/spec/ol/source/vectorsource.test.js index 3ac6685be2..37a6b46113 100644 --- a/test/spec/ol/source/vectorsource.test.js +++ b/test/spec/ol/source/vectorsource.test.js @@ -100,7 +100,10 @@ describe('ol.source.Vector', function() { }); +goog.require('ol.Extent'); +goog.require('ol.Feature'); goog.require('ol.Projection'); +goog.require('ol.filter.Extent'); goog.require('ol.filter.Geometry'); goog.require('ol.filter.Logical'); goog.require('ol.filter.LogicalOperator'); @@ -108,3 +111,4 @@ goog.require('ol.geom.GeometryType'); goog.require('ol.geom.Point'); goog.require('ol.geom.LineString'); goog.require('ol.source.Vector'); +goog.require('ol.projection'); diff --git a/test/spec/ol/style/style.test.js b/test/spec/ol/style/style.test.js index 8cbe965bc4..f5e7530549 100644 --- a/test/spec/ol/style/style.test.js +++ b/test/spec/ol/style/style.test.js @@ -59,8 +59,11 @@ describe('ol.style.Style', function() { }); +goog.require('ol.Feature'); goog.require('ol.geom.LineString'); goog.require('ol.geom.Point'); goog.require('ol.geom.Polygon'); goog.require('ol.filter.Filter'); +goog.require('ol.style.Rule'); +goog.require('ol.style.Shape'); goog.require('ol.style.Style'); From a968a5ca663cc248d9e6827706591cf7395af32d Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 5 Mar 2013 11:40:08 +0100 Subject: [PATCH 112/180] The shared vertices structure now works with integer arrays only Previously, a lookup object containing start indexes was also used. This allowed us to remove arrays of vertices and properly update the start indexes for remaining coordinate values. In order to provide callers with stable identifiers and to work with arrays of integers alone, we cannot support a remove method. I think this is worth revisiting. Even if the array length cannot be changed in WebGL, we don't need to also impose the restriction outside. Instead, the WebGL renderer could be notified when array sizes change and update itself accordingly. I think there is more value in providing geometries with stable identifiers. This common structure is used to store vertices for all geometry types. A slight optimization could be made by creating yet another structure to store point vertices - since these don't need to have a counts array (always 1). Creating a new structure would mean saving memory at the expense of more lib code to transport. The coordinate value lookups are not negatively impacted by using the same structure for points and higher order geometries. Since the first change above goes against my instincts, I'm not making this second change (to add another structure for shared point vertices). --- src/ol/geom/linestring.js | 4 +- src/ol/geom/point.js | 4 +- src/ol/geom/sharedvertices.js | 108 ++++++++------------- test/spec/ol/filter/geometryfilter.test.js | 2 +- test/spec/ol/geom/sharedvertices.test.js | 97 +++++++++--------- 5 files changed, 95 insertions(+), 120 deletions(-) diff --git a/src/ol/geom/linestring.js b/src/ol/geom/linestring.js index 822af57277..1705954163 100644 --- a/src/ol/geom/linestring.js +++ b/src/ol/geom/linestring.js @@ -34,7 +34,7 @@ ol.geom.LineString = function(coordinates, opt_shared) { this.vertices = vertices; /** - * @type {string} + * @type {number} * @private */ this.sharedId_ = vertices.add(coordinates); @@ -142,7 +142,7 @@ ol.geom.LineString.prototype.getType = function() { /** * Get the identifier used to mark this line in the shared vertices structure. - * @return {string} The identifier. + * @return {number} The identifier. */ ol.geom.LineString.prototype.getSharedId = function() { return this.sharedId_; diff --git a/src/ol/geom/point.js b/src/ol/geom/point.js index 621a6e4f13..f593db0cb7 100644 --- a/src/ol/geom/point.js +++ b/src/ol/geom/point.js @@ -32,7 +32,7 @@ ol.geom.Point = function(coordinates, opt_shared) { this.vertices = vertices; /** - * @type {string} + * @type {number} * @private */ this.sharedId_ = vertices.add([coordinates]); @@ -98,7 +98,7 @@ ol.geom.Point.prototype.getType = function() { /** * Get the identifier used to mark this point in the shared vertices structure. - * @return {string} The identifier. + * @return {number} The identifier. */ ol.geom.Point.prototype.getSharedId = function() { return this.sharedId_; diff --git a/src/ol/geom/sharedvertices.js b/src/ol/geom/sharedvertices.js index 39ff922027..3941b8f0e7 100644 --- a/src/ol/geom/sharedvertices.js +++ b/src/ol/geom/sharedvertices.js @@ -20,19 +20,25 @@ ol.geom.SharedVerticesOptions; * @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; + var options = opt_options ? opt_options : {}; /** * @type {Array.} */ this.coordinates = []; + /** + * @type {Array.} + * @private + */ + this.starts_ = []; + + /** + * @type {Array.} + * @private + */ + this.counts_ = []; + /** * Number of dimensions per vertex. Default is 2. * @type {number} @@ -49,25 +55,13 @@ ol.geom.SharedVertices = function(opt_options) { 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. + * @return {number} Index used to reference the added vertex array. */ ol.geom.SharedVertices.prototype.add = function(vertices) { var start = this.coordinates.length; @@ -87,28 +81,23 @@ ol.geom.SharedVertices.prototype.add = function(vertices) { } } } - var id = this.getId_(); - var idIndex = this.ids_.push(id) - 1; - this.lookup_[id] = { - idIndex: idIndex, - start: start, - count: count - }; - return id; + var length = this.starts_.push(start); + this.counts_.push(count); + return length - 1; }; /** - * @param {string} id The vertex array identifier (returned by add). + * @param {number} 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(id < this.starts_.length); 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; + goog.asserts.assert(index < this.counts_[id]); + var start = this.starts_[id]; var value = this.coordinates[start + (index * this.dimension_) + dim]; if (this.offset_) { value += this.offset_[dim]; @@ -118,22 +107,23 @@ ol.geom.SharedVertices.prototype.get = function(id, index, dim) { /** - * @param {string} id The vertex array identifier (returned by add). + * @param {number} 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; + goog.asserts.assert(id < this.counts_.length); + return this.counts_[id]; }; /** - * Gets an identifier that is unique for this instance. - * @return {string} Identifier. - * @private + * Get the array of counts. The index returned by the add method can be used + * to look up the number of vertices. + * + * @return {Array.} The counts array. */ -ol.geom.SharedVertices.prototype.getId_ = function() { - return String(++this.counter_); +ol.geom.SharedVertices.prototype.getCounts = function() { + return this.counts_; }; @@ -154,42 +144,20 @@ ol.geom.SharedVertices.prototype.getOffset = function() { /** - * @param {string} id The vertex array identifier (returned by add). + * @param {number} 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; + goog.asserts.assert(id < this.starts_.length); + return this.starts_[id]; }; /** - * @param {number} id The vertex array identifier (returned by add). - * @return {ol.geom.VertexArray} The removed vertex array. + * Get the array of start indexes. + * @return {Array.} The starts 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; +ol.geom.SharedVertices.prototype.getStarts = function() { + return this.starts_; }; + diff --git a/test/spec/ol/filter/geometryfilter.test.js b/test/spec/ol/filter/geometryfilter.test.js index b9c41ef7ae..dc7bb6dc03 100644 --- a/test/spec/ol/filter/geometryfilter.test.js +++ b/test/spec/ol/filter/geometryfilter.test.js @@ -34,7 +34,7 @@ describe('ol.filter.Geometry', function() { it('works for multi-linestring', function() { var filter = new ol.filter.Geometry( - ol.filter.GeometryType.MULTILINESTRING); + ol.filter.GeometryType.MULTILINESTRING); expect(filter.getType()).toBe(ol.filter.GeometryType.MULTILINESTRING); }); diff --git a/test/spec/ol/geom/sharedvertices.test.js b/test/spec/ol/geom/sharedvertices.test.js index c007c39205..06b15bfea5 100644 --- a/test/spec/ol/geom/sharedvertices.test.js +++ b/test/spec/ol/geom/sharedvertices.test.js @@ -43,8 +43,24 @@ describe('ol.geom.SharedVertices', function() { 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'); + expect(typeof id).toBe('number'); }); + + it('returns the index of the added vertices', 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(first).toBe(0); + expect(second).toBe(1); + expect(third).toBe(2); + }); + }); describe('#get()', function() { @@ -98,6 +114,17 @@ describe('ol.geom.SharedVertices', function() { }); }); + describe('#getCounts()', function() { + it('returns the counts 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]]); + var third = vertices.add([[7, 8]]); + + expect(vertices.getCounts()).toEqual([3, 2, 1]); + }); + }); + describe('#getDimension()', function() { it('returns 2 by default', function() { var vertices = new ol.geom.SharedVertices(); @@ -123,57 +150,43 @@ describe('ol.geom.SharedVertices', function() { }); describe('#getStart()', function() { - it('returns the start of the identified vertex array', function() { + it('returns the start index of an 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]]); + var first = vertices.add([[2, 3], [4, 5], [6, 7]]); + var second = vertices.add([[8, 9], [10, 11]]); + var third = vertices.add([[12, 13]]); expect(vertices.coordinates).toEqual( - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); + // 0 1 2 3 4 5 6 7 8 9 10 11 + expect(vertices.getStart(first)).toBe(0); - expect(vertices.getStart(second)).toBe(2); - expect(vertices.getStart(third)).toBe(6); + expect(vertices.getStart(second)).toBe(6); + expect(vertices.getStart(third)).toBe(10); }); }); - describe('#remove()', function() { - it('removes a vertex array', function() { + describe('#getStarts()', function() { + it('returns the counts 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]]); + var third = vertices.add([[7, 8]]); + + expect(vertices.getStarts()).toEqual([0, 6, 10]); + }); + }); + + describe('#coordinates', function() { + it('is a flat array of all coordinate values', 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([]); + expect(vertices.coordinates).toEqual( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); }); - 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]]); @@ -181,12 +194,6 @@ describe('ol.geom.SharedVertices', function() { 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); }); }); From 12bee3178e94841a6aadf2ff0ebebba5b48ec3ce Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 5 Mar 2013 12:41:44 +0100 Subject: [PATCH 113/180] Moving the feature management to the layer The source will be responsible for fetching vector data. --- examples/style-rules.js | 79 ++++--- examples/vector-layer.js | 11 +- src/ol/layer/vectorlayer.js | 192 ++++++++++++++++++ .../canvas/canvasvectorlayerrenderer.js | 5 +- src/ol/source/vectorsource.js | 185 ----------------- test/spec/ol/layer/vectorlayer.test.js | 101 +++++++++ test/spec/ol/source/vectorsource.test.js | 109 +--------- 7 files changed, 344 insertions(+), 338 deletions(-) diff --git a/examples/style-rules.js b/examples/style-rules.js index b2c0e1bd01..aa34ce8a00 100644 --- a/examples/style-rules.js +++ b/examples/style-rules.js @@ -15,11 +15,46 @@ goog.require('ol.style.Rule'); goog.require('ol.style.Style'); -var source = new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:3857') +var style = new ol.style.Style({rules: [ + new ol.style.Rule({ + filter: new ol.filter.Filter(function(feature) { + return feature.get('where') == 'outer'; + }), + symbolizers: [ + new ol.style.Line({ + strokeStyle: new ol.Expression('color'), + strokeWidth: 4, + opacity: 1 + }) + ] + }), + new ol.style.Rule({ + filter: new ol.filter.Filter(function(feature) { + return feature.get('where') == 'inner'; + }), + symbolizers: [ + new ol.style.Line({ + strokeStyle: '#013', + strokeWidth: 4, + opacity: 1 + }), + new ol.style.Line({ + strokeStyle: new ol.Expression('color'), + strokeWidth: 2, + opacity: 1 + }) + ] + }) +]}); + +var vector = new ol.layer.Vector({ + style: style, + source: new ol.source.Vector({ + projection: ol.projection.getFromCode('EPSG:3857') + }) }); -source.addFeatures([ +vector.addFeatures([ new ol.Feature({ g: new ol.geom.LineString([[-10000000, -10000000], [10000000, 10000000]]), 'color': '#BADA55', @@ -52,44 +87,6 @@ source.addFeatures([ }) ]); -var style = new ol.style.Style({ - rules: [ - new ol.style.Rule({ - filter: new ol.filter.Filter(function(feature) { - return feature.get('where') == 'outer'; - }), - symbolizers: [ - new ol.style.Line({ - strokeStyle: new ol.Expression('color'), - strokeWidth: 4, - opacity: 1 - }) - ] - }), - new ol.style.Rule({ - filter: new ol.filter.Filter(function(feature) { - return feature.get('where') == 'inner'; - }), - symbolizers: [ - new ol.style.Line({ - strokeStyle: '#013', - strokeWidth: 4, - opacity: 1 - }), - new ol.style.Line({ - strokeStyle: new ol.Expression('color'), - strokeWidth: 2, - opacity: 1 - }) - ] - }) - ] -}); - -var vector = new ol.layer.Vector({ - source: source, - style: style -}); var map = new ol.Map({ layers: new ol.Collection([vector]), diff --git a/examples/vector-layer.js b/examples/vector-layer.js index 916dc6406b..e28f0d9cd6 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -17,11 +17,13 @@ var raster = new ol.layer.TileLayer({ source: new ol.source.MapQuestOpenAerial() }); -var source = new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:3857') +var vector = new ol.layer.Vector({ + source: new ol.source.Vector({ + projection: ol.projection.getFromCode('EPSG:3857') + }) }); -source.addFeatures([ +vector.addFeatures([ new ol.Feature({ g: new ol.geom.LineString([[-10000000, -10000000], [10000000, 10000000]]) }), @@ -31,9 +33,6 @@ source.addFeatures([ new ol.Feature({g: new ol.geom.Point([-10000000, 5000000])}) ]); -var vector = new ol.layer.Vector({ - source: source -}); var map = new ol.Map({ layers: new ol.Collection([raster, vector]), diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 6703ec3e3c..77fde89ac2 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -3,10 +3,170 @@ goog.provide('ol.layer.Vector'); goog.require('ol.Feature'); goog.require('ol.layer.Layer'); goog.require('ol.source.Vector'); +goog.require('ol.structs.RTree'); goog.require('ol.style.Style'); +/** + * @constructor + */ +ol.layer.FeatureCache = function() { + + /** + * @type {Object.} + * @private + */ + this.idLookup_; + + /** + * @type {Object.} + * @private + */ + this.geometryTypeIndex_; + + /** + * @type {Object.} + * @private + */ + this.boundsByGeometryType_; + + this.clear(); + +}; + + +/** + * Clear the cache. + */ +ol.layer.FeatureCache.prototype.clear = function() { + this.idLookup_ = {}; + + var geometryTypeIndex = {}, + boundsByGeometryType = {}, + geometryType; + for (var key in ol.geom.GeometryType) { + geometryType = ol.geom.GeometryType[key]; + geometryTypeIndex[geometryType] = {}; + boundsByGeometryType[geometryType] = new ol.structs.RTree(); + } + this.geometryTypeIndex_ = geometryTypeIndex; + this.boundsByGeometryType_ = boundsByGeometryType; +}; + + +/** + * Add a feature to the cache. + * @param {ol.Feature} feature Feature to be cached. + */ +ol.layer.FeatureCache.prototype.add = function(feature) { + var id = goog.getUid(feature).toString(), + geometry = feature.getGeometry(); + + this.idLookup_[id] = feature; + + // index by geometry type and bounding box + if (!goog.isNull(geometry)) { + var geometryType = geometry.getType(); + this.geometryTypeIndex_[geometryType][id] = feature; + this.boundsByGeometryType_[geometryType].put(geometry.getBounds(), + feature); + } +}; + + +/** + * @param {ol.filter.Filter=} opt_filter Optional filter. + * @return {Object.} Object of features, keyed by id. + * @private + */ +ol.layer.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) { + var i, features; + if (!goog.isDef(opt_filter)) { + features = this.idLookup_; + } else { + if (opt_filter instanceof ol.filter.Geometry) { + features = this.geometryTypeIndex_[opt_filter.getType()]; + } else if (opt_filter instanceof ol.filter.Extent) { + var boundsByGeometryType = this.boundsByGeometryType_, + extent = opt_filter.getExtent(); + features = {}; + for (i in boundsByGeometryType) { + goog.object.extend(features, boundsByGeometryType[i].find(extent)); + } + } else if (opt_filter instanceof ol.filter.Logical && + opt_filter.operator === ol.filter.LogicalOperator.AND) { + var filters = opt_filter.getFilters(); + if (filters.length === 2) { + var filter, geometryFilter, extentFilter; + for (i = 0; i <= 1; ++i) { + filter = filters[i]; + if (filter instanceof ol.filter.Geometry) { + geometryFilter = filter; + } else if (filter instanceof ol.filter.Extent) { + extentFilter = filter; + } + } + if (extentFilter && geometryFilter) { + features = this.boundsByGeometryType_[geometryFilter.getType()] + .find(extentFilter.getExtent()); + } + } + } + if (!goog.isDef(features)) { + // TODO: support fast lane for other filter types + var candidates = this.idLookup_, + feature; + features = {}; + for (i in candidates) { + feature = candidates[i]; + if (opt_filter.applies(feature) === true) { + features[i] = feature; + } + } + } + } + return features; +}; + + +/** + * @param {ol.filter.Filter=} opt_filter Optional filter. + * @return {Array.} Array of features. + */ +ol.layer.FeatureCache.prototype.getFeatures = function(opt_filter) { + return goog.object.getValues(this.getFeaturesObject_(opt_filter)); +}; + + +/** + * @param {ol.filter.Geometry} filter Geometry type filter. + * @return {Array.} Array of features. + * @private + */ +ol.layer.FeatureCache.prototype.getFeaturesByGeometryType_ = function(filter) { + return goog.object.getValues(this.geometryTypeIndex_[filter.getType()]); +}; + + +/** + * Get features by ids. + * @param {Array.} ids Array of (internal) identifiers. + * @return {Array.} Array of features. + * @private + */ +ol.layer.FeatureCache.prototype.getFeaturesByIds_ = function(ids) { + var len = ids.length, + features = new Array(len), + i; + for (i = 0; i < len; ++i) { + features[i] = this.idLookup_[ids[i]]; + } + return features; +}; + + + /** * @constructor * @extends {ol.layer.Layer} @@ -21,10 +181,26 @@ ol.layer.Vector = function(layerOptions) { */ this.style_ = goog.isDef(layerOptions.style) ? layerOptions.style : null; + /** + * @type {ol.layer.FeatureCache} + * @private + */ + this.featureCache_ = new ol.layer.FeatureCache(); + }; goog.inherits(ol.layer.Vector, ol.layer.Layer); +/** + * @param {Array.} features Array of features. + */ +ol.layer.Vector.prototype.addFeatures = function(features) { + for (var i = 0, ii = features.length; i < ii; ++i) { + this.featureCache_.add(features[i]); + } +}; + + /** * @return {ol.source.Vector} Source. */ @@ -33,6 +209,15 @@ ol.layer.Vector.prototype.getVectorSource = function() { }; +/** + * @param {ol.filter.Filter=} opt_filter Optional filter. + * @return {Array.} Array of features. + */ +ol.layer.Vector.prototype.getFeatures = function(opt_filter) { + return this.featureCache_.getFeatures(opt_filter); +}; + + /** * @param {Array.} features Features. * @return {Array.} symbolizers for features. @@ -72,3 +257,10 @@ ol.layer.Vector.prototype.groupFeaturesBySymbolizerLiteral = } return featuresBySymbolizer; }; + + +goog.require('ol.filter.Extent'); +goog.require('ol.filter.Geometry'); +goog.require('ol.filter.Logical'); +goog.require('ol.filter.LogicalOperator'); +goog.require('ol.geom.GeometryType'); diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index b4560e2e88..b3a0a191b6 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -167,11 +167,10 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = resolution = view2DState.resolution, extent = frameState.extent, layer = this.getVectorLayer(), - source = layer.getVectorSource(), tileGrid = this.tileGrid_; if (goog.isNull(tileGrid)) { - // lazy tile source creation to match the view projection + // lazy tile grid creation to match the view projection tileGrid = ol.tilegrid.createForProjection( view2DState.projection, 22, // should be no harm in going big here - ideally, it would be ∞ @@ -282,7 +281,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = for (i = 0; i < numFilters; ++i) { geomFilter = filters[i]; type = geomFilter.getType(); - features = source.getFeatures(new ol.filter.Logical( + features = layer.getFeatures(new ol.filter.Logical( [geomFilter, extentFilter], ol.filter.LogicalOperator.AND)); if (features.length) { groups = layer.groupFeaturesBySymbolizerLiteral(features); diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index 1dc6c0a0a1..60ff1f1228 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -11,166 +11,6 @@ goog.require('ol.filter.Logical'); goog.require('ol.filter.LogicalOperator'); goog.require('ol.geom.GeometryType'); goog.require('ol.source.Source'); -goog.require('ol.structs.RTree'); - - - -/** - * @constructor - */ -ol.source.FeatureCache = function() { - - /** - * @type {Object.} - * @private - */ - this.idLookup_; - - /** - * @type {Object.} - * @private - */ - this.geometryTypeIndex_; - - /** - * @type {Object.} - * @private - */ - this.boundsByGeometryType_; - - this.clear(); - -}; - - -/** - * Clear the cache. - */ -ol.source.FeatureCache.prototype.clear = function() { - this.idLookup_ = {}; - - var geometryTypeIndex = {}, - boundsByGeometryType = {}, - geometryType; - for (var key in ol.geom.GeometryType) { - geometryType = ol.geom.GeometryType[key]; - geometryTypeIndex[geometryType] = {}; - boundsByGeometryType[geometryType] = new ol.structs.RTree(); - } - this.geometryTypeIndex_ = geometryTypeIndex; - this.boundsByGeometryType_ = boundsByGeometryType; -}; - - -/** - * Add a feature to the cache. - * @param {ol.Feature} feature Feature to be cached. - */ -ol.source.FeatureCache.prototype.add = function(feature) { - var id = goog.getUid(feature).toString(), - geometry = feature.getGeometry(); - - this.idLookup_[id] = feature; - - // index by geometry type and bounding box - if (!goog.isNull(geometry)) { - var geometryType = geometry.getType(); - this.geometryTypeIndex_[geometryType][id] = feature; - this.boundsByGeometryType_[geometryType].put(geometry.getBounds(), - feature); - } -}; - - -/** - * @param {ol.filter.Filter=} opt_filter Optional filter. - * @return {Object.} Object of features, keyed by id. - * @private - */ -ol.source.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) { - var i, features; - if (!goog.isDef(opt_filter)) { - features = this.idLookup_; - } else { - if (opt_filter instanceof ol.filter.Geometry) { - features = this.geometryTypeIndex_[opt_filter.getType()]; - } else if (opt_filter instanceof ol.filter.Extent) { - var boundsByGeometryType = this.boundsByGeometryType_, - extent = opt_filter.getExtent(); - features = {}; - for (i in boundsByGeometryType) { - goog.object.extend(features, boundsByGeometryType[i].find(extent)); - } - } else if (opt_filter instanceof ol.filter.Logical && - opt_filter.operator === ol.filter.LogicalOperator.AND) { - var filters = opt_filter.getFilters(); - if (filters.length === 2) { - var filter, geometryFilter, extentFilter; - for (i = 0; i <= 1; ++i) { - filter = filters[i]; - if (filter instanceof ol.filter.Geometry) { - geometryFilter = filter; - } else if (filter instanceof ol.filter.Extent) { - extentFilter = filter; - } - } - if (extentFilter && geometryFilter) { - features = this.boundsByGeometryType_[geometryFilter.getType()] - .find(extentFilter.getExtent()); - } - } - } - if (!goog.isDef(features)) { - // TODO: support fast lane for other filter types - var candidates = this.idLookup_, - feature; - features = {}; - for (i in candidates) { - feature = candidates[i]; - if (opt_filter.applies(feature) === true) { - features[i] = feature; - } - } - } - } - return features; -}; - - -/** - * @param {ol.filter.Filter=} opt_filter Optional filter. - * @return {Array.} Array of features. - */ -ol.source.FeatureCache.prototype.getFeatures = function(opt_filter) { - return goog.object.getValues(this.getFeaturesObject_(opt_filter)); -}; - - -/** - * @param {ol.filter.Geometry} filter Geometry type filter. - * @return {Array.} Array of features. - * @private - */ -ol.source.FeatureCache.prototype.getFeaturesByGeometryType_ = function(filter) { - return goog.object.getValues(this.geometryTypeIndex_[filter.getType()]); -}; - - -/** - * Get features by ids. - * @param {Array.} ids Array of (internal) identifiers. - * @return {Array.} Array of features. - * @private - */ -ol.source.FeatureCache.prototype.getFeaturesByIds_ = function(ids) { - var len = ids.length, - features = new Array(len), - i; - for (i = 0; i < len; ++i) { - features[i] = this.idLookup_[ids[i]]; - } - return features; -}; /** @@ -195,30 +35,5 @@ ol.source.Vector = function(options) { projection: options.projection }); - /** - * @type {ol.source.FeatureCache} - * @private - */ - this.featureCache_ = new ol.source.FeatureCache(); - }; goog.inherits(ol.source.Vector, ol.source.Source); - - -/** - * @param {Array.} features Array of features. - */ -ol.source.Vector.prototype.addFeatures = function(features) { - for (var i = 0, ii = features.length; i < ii; ++i) { - this.featureCache_.add(features[i]); - } -}; - - -/** - * @param {ol.filter.Filter=} opt_filter Optional filter. - * @return {Array.} Array of features. - */ -ol.source.Vector.prototype.getFeatures = function(opt_filter) { - return this.featureCache_.getFeatures(opt_filter); -}; diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js index c08053419d..b6e5ff89ec 100644 --- a/test/spec/ol/layer/vectorlayer.test.js +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -2,6 +2,99 @@ goog.provide('ol.test.layer.Vector'); describe('ol.layer.Vector', function() { + describe('#addFeatures()', function() { + + it('allows adding features', function() { + var layer = new ol.layer.Vector({ + new ol.source.Vector({}) + }); + layer.addFeatures([new ol.Feature(), new ol.Feature()]); + expect(layer.getFeatures().length).toEqual(2); + }); + }); + + describe('#getFeatures()', function() { + + var layer, features; + + beforeEach(function() { + features = [ + new ol.Feature({ + g: new ol.geom.Point([16.0, 48.0]) + }), + new ol.Feature({ + g: new ol.geom.Point([16.1, 48.1]) + }), + new ol.Feature({ + g: new ol.geom.Point([16.2, 48.2]) + }), + new ol.Feature({ + g: new ol.geom.Point([16.3, 48.3]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[16.4, 48.4], [16.5, 48.5]]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[16.6, 48.6], [16.7, 48.7]]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[16.8, 48.8], [16.9, 48.9]]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[17.0, 49.0], [17.1, 49.1]]) + }) + ]; + layer = new ol.layer.Vector({ + source: new ol.source.Vector({}) + }); + layer.addFeatures(features); + }); + + var geomFilter = new ol.filter.Geometry(ol.geom.GeometryType.LINESTRING); + var extentFilter = new ol.filter.Extent(new ol.Extent(16, 48, 16.3, 48.3)); + + it('can filter by geometry type using its GeometryType index', function() { + spyOn(geomFilter, 'applies'); + var lineStrings = layer.getFeatures(geomFilter); + expect(geomFilter.applies).not.toHaveBeenCalled(); + expect(lineStrings.length).toEqual(4); + expect(lineStrings).toContain(features[4]); + }); + + it('can filter by extent using its RTree', function() { + spyOn(extentFilter, 'applies'); + var subset = layer.getFeatures(extentFilter); + expect(extentFilter.applies).not.toHaveBeenCalled(); + expect(subset.length).toEqual(4); + expect(subset).not.toContain(features[7]); + }); + + it('can filter by extent and geometry type using its index', function() { + var filter1 = new ol.filter.Logical([geomFilter, extentFilter], + ol.filter.LogicalOperator.AND); + var filter2 = new ol.filter.Logical([extentFilter, geomFilter], + ol.filter.LogicalOperator.AND); + spyOn(filter1, 'applies'); + spyOn(filter2, 'applies'); + var subset1 = layer.getFeatures(filter1); + var subset2 = layer.getFeatures(filter2); + expect(filter1.applies).not.toHaveBeenCalled(); + expect(filter2.applies).not.toHaveBeenCalled(); + expect(subset1.length).toEqual(0); + expect(subset2.length).toEqual(0); + }); + + it('can handle query using the filter\'s applies function', function() { + var filter = new ol.filter.Logical([geomFilter, extentFilter], + ol.filter.LogicalOperator.OR); + spyOn(filter, 'applies').andCallThrough(); + var subset = layer.getFeatures(filter); + expect(filter.applies).toHaveBeenCalled(); + expect(subset.length).toEqual(8); + }); + + }); + describe('#groupFeaturesBySymbolizerLiteral()', function() { var layer = new ol.layer.Vector({ @@ -84,8 +177,16 @@ describe('ol.layer.Vector', function() { }); goog.require('ol.Expression'); +goog.require('ol.Extent'); goog.require('ol.Feature'); +goog.require('ol.Projection'); +goog.require('ol.filter.Extent'); +goog.require('ol.filter.Geometry'); +goog.require('ol.filter.Logical'); +goog.require('ol.filter.LogicalOperator'); +goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); +goog.require('ol.geom.Point'); goog.require('ol.projection'); goog.require('ol.layer.Vector'); goog.require('ol.source.Vector'); diff --git a/test/spec/ol/source/vectorsource.test.js b/test/spec/ol/source/vectorsource.test.js index 37a6b46113..c2cbfdf028 100644 --- a/test/spec/ol/source/vectorsource.test.js +++ b/test/spec/ol/source/vectorsource.test.js @@ -3,112 +3,15 @@ goog.provide('ol.test.source.Vector'); describe('ol.source.Vector', function() { - var vectorSource; - - describe('#addFeatures()', function() { - - it('works', function() { - vectorSource = new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:4326') - }); - vectorSource.addFeatures([new ol.Feature()]); - expect(vectorSource.getFeatures().length).toEqual(1); + describe('constructor', function() { + it('creates an instance', function() { + var source = new ol.source.Vector({}); + expect(source).toBeA(ol.source.Vector); + expect(source).toBeA(ol.source.Source); }); }); - describe('#getFeatures()', function() { - - var features; - - beforeEach(function() { - features = [ - new ol.Feature({ - g: new ol.geom.Point([16.0, 48.0]) - }), - new ol.Feature({ - g: new ol.geom.Point([16.1, 48.1]) - }), - new ol.Feature({ - g: new ol.geom.Point([16.2, 48.2]) - }), - new ol.Feature({ - g: new ol.geom.Point([16.3, 48.3]) - }), - new ol.Feature({ - g: new ol.geom.LineString([[16.4, 48.4], [16.5, 48.5]]) - }), - new ol.Feature({ - g: new ol.geom.LineString([[16.6, 48.6], [16.7, 48.7]]) - }), - new ol.Feature({ - g: new ol.geom.LineString([[16.8, 48.8], [16.9, 48.9]]) - }), - new ol.Feature({ - g: new ol.geom.LineString([[17.0, 49.0], [17.1, 49.1]]) - }) - ]; - vectorSource = new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:4326') - }); - vectorSource.addFeatures(features); - }); - - var geomFilter = new ol.filter.Geometry(ol.geom.GeometryType.LINESTRING); - var extentFilter = new ol.filter.Extent(new ol.Extent(16, 48, 16.3, 48.3)); - - it('can filter by geometry type using its GeometryType index', function() { - spyOn(geomFilter, 'applies'); - var lineStrings = vectorSource.getFeatures(geomFilter); - expect(geomFilter.applies).not.toHaveBeenCalled(); - expect(lineStrings.length).toEqual(4); - expect(lineStrings).toContain(features[4]); - }); - - it('can filter by extent using its RTree', function() { - spyOn(extentFilter, 'applies'); - var subset = vectorSource.getFeatures(extentFilter); - expect(extentFilter.applies).not.toHaveBeenCalled(); - expect(subset.length).toEqual(4); - expect(subset).not.toContain(features[7]); - }); - - it('can filter by extent and geometry type using its index', function() { - var filter1 = new ol.filter.Logical([geomFilter, extentFilter], - ol.filter.LogicalOperator.AND); - var filter2 = new ol.filter.Logical([extentFilter, geomFilter], - ol.filter.LogicalOperator.AND); - spyOn(filter1, 'applies'); - spyOn(filter2, 'applies'); - var subset1 = vectorSource.getFeatures(filter1); - var subset2 = vectorSource.getFeatures(filter2); - expect(filter1.applies).not.toHaveBeenCalled(); - expect(filter2.applies).not.toHaveBeenCalled(); - expect(subset1.length).toEqual(0); - expect(subset2.length).toEqual(0); - }); - - it('can handle query using the filter\'s applies function', function() { - var filter = new ol.filter.Logical([geomFilter, extentFilter], - ol.filter.LogicalOperator.OR); - spyOn(filter, 'applies').andCallThrough(); - var subset = vectorSource.getFeatures(filter); - expect(filter.applies).toHaveBeenCalled(); - expect(subset.length).toEqual(8); - }); - - }); - }); -goog.require('ol.Extent'); -goog.require('ol.Feature'); -goog.require('ol.Projection'); -goog.require('ol.filter.Extent'); -goog.require('ol.filter.Geometry'); -goog.require('ol.filter.Logical'); -goog.require('ol.filter.LogicalOperator'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.LineString'); +goog.require('ol.source.Source'); goog.require('ol.source.Vector'); -goog.require('ol.projection'); From e1c3faa53ecb34c8d315e8d017f61ef3c4ebbc34 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 5 Mar 2013 18:13:10 +0100 Subject: [PATCH 114/180] Vector related exports --- src/objectliterals.exports | 31 ++++++++++++++++++++++++++ src/ol/feature.exports | 6 +++++ src/ol/filter/filter.exports | 7 ++++++ src/ol/geom/expression.exports | 1 + src/ol/geom/geometry.exports | 6 +++++ src/ol/layer/vectorlayer.exports | 3 +++ src/ol/source/vectorsource.exports | 1 + src/ol/source/vectorsource.js | 18 ++------------- src/ol/style.exports | 7 ++++++ src/ol/style/line.js | 11 +++------ src/ol/style/polygon.js | 13 ++++------- src/ol/style/rule.js | 7 ------ src/ol/style/shape.js | 19 ++++++---------- src/ol/style/style.js | 6 ----- test/spec/ol/layer/vectorlayer.test.js | 2 +- test/spec/ol/style/line.test.js | 9 +++++--- test/spec/ol/style/polygon.test.js | 12 +++++++--- test/spec/ol/style/shape.test.js | 19 +++++++++++++--- test/spec/ol/style/style.test.js | 8 +++++-- 19 files changed, 116 insertions(+), 70 deletions(-) create mode 100644 src/ol/feature.exports create mode 100644 src/ol/filter/filter.exports create mode 100644 src/ol/geom/expression.exports create mode 100644 src/ol/geom/geometry.exports create mode 100644 src/ol/layer/vectorlayer.exports create mode 100644 src/ol/source/vectorsource.exports create mode 100644 src/ol/style.exports diff --git a/src/objectliterals.exports b/src/objectliterals.exports index 3f0a1e984d..13f0d5808c 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -113,6 +113,11 @@ @exportObjectLiteralProperty ol.source.SingleImageWMSOptions.url string|undefined @exportObjectLiteralProperty ol.source.SingleImageWMSOptions.version string|undefined +@exportObjectLiteral ol.source.SourceOptions +@exportObjectLiteralProperty ol.source.SourceOptions.attributions Array.|undefined +@exportObjectLiteralProperty ol.source.SourceOptions.extent ol.Extent|undefined +@exportObjectLiteralProperty ol.source.SourceOptions.projection ol.Projection|undefined + @exportObjectLiteral ol.source.StamenOptions @exportObjectLiteralProperty ol.source.StamenOptions.flavor string|undefined @exportObjectLiteralProperty ol.source.StamenOptions.provider string @@ -139,6 +144,32 @@ @exportObjectLiteralProperty ol.source.TiledWMSOptions.url string|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.urls Array.|undefined +@exportObjectLiteral ol.style.LineOptions +@exportObjectLiteralProperty ol.style.LineOptions.strokeStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.LineOptions.strokeWidth number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.LineOptions.opacity number|ol.Expression|undefined + +@exportObjectLiteral ol.style.PolygonOptions +@exportObjectLiteralProperty ol.style.PolygonOptions.fillStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.PolygonOptions.strokeStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.PolygonOptions.strokeWidth number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.PolygonOptions.opacity number|ol.Expression|undefined + +@exportObjectLiteral ol.style.RuleOptions +@exportObjectLiteralProperty ol.style.RuleOptions.filter ol.filter.Filter|undefined +@exportObjectLiteralProperty ol.style.RuleOptions.symbolizers Array.|undefined + +@exportObjectLiteral ol.style.ShapeOptions +@exportObjectLiteralProperty ol.style.ShapeOptions.type ol.style.ShapeType|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.size number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.fillStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.strokeStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.strokeWidth number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.opacity number|ol.Expression|undefined + +@exportObjectLiteral ol.style.StyleOptions +@exportObjectLiteralProperty ol.style.StyleOptions.rules Array. + @exportObjectLiteral ol.tilegrid.TileGridOptions @exportObjectLiteralProperty ol.tilegrid.TileGridOptions.origin ol.Coordinate|undefined @exportObjectLiteralProperty ol.tilegrid.TileGridOptions.origins Array.|undefined diff --git a/src/ol/feature.exports b/src/ol/feature.exports new file mode 100644 index 0000000000..3192a1fd2d --- /dev/null +++ b/src/ol/feature.exports @@ -0,0 +1,6 @@ +@exportSymbol ol.Feature +@exportProperty ol.Feature.prototype.get +@exportProperty ol.Feature.prototype.getAttributes +@exportProperty ol.Feature.prototype.getGeometry +@exportProperty ol.Feature.prototype.set +@exportProperty ol.Feature.prototype.setGeometry diff --git a/src/ol/filter/filter.exports b/src/ol/filter/filter.exports new file mode 100644 index 0000000000..696edee702 --- /dev/null +++ b/src/ol/filter/filter.exports @@ -0,0 +1,7 @@ +@exportSymbol ol.filter.Filter +@exportSymbol ol.filter.Geometry +@exportSymbol ol.filter.Logical + +@exportSymbol ol.filter.LogicalOperator +@exportProperty ol.filter.LogicalOperator.AND +@exportProperty ol.filter.LogicalOperator.OR diff --git a/src/ol/geom/expression.exports b/src/ol/geom/expression.exports new file mode 100644 index 0000000000..911b8a12c6 --- /dev/null +++ b/src/ol/geom/expression.exports @@ -0,0 +1 @@ +@exportSymbol ol.Expression diff --git a/src/ol/geom/geometry.exports b/src/ol/geom/geometry.exports new file mode 100644 index 0000000000..4170284a9e --- /dev/null +++ b/src/ol/geom/geometry.exports @@ -0,0 +1,6 @@ +@exportSymbol ol.geom.Point +@exportSymbol ol.geom.LineString +@exportSymbol ol.geom.Polygon +@exportSymbol ol.geom.MultiPoint +@exportSymbol ol.geom.MultiLineString +@exportSymbol ol.geom.MultiPolygon diff --git a/src/ol/layer/vectorlayer.exports b/src/ol/layer/vectorlayer.exports new file mode 100644 index 0000000000..6abe609b01 --- /dev/null +++ b/src/ol/layer/vectorlayer.exports @@ -0,0 +1,3 @@ +@exportClass ol.layer.Vector ol.layer.LayerOptions + +@exportProperty ol.layer.Vector.prototype.addFeatures diff --git a/src/ol/source/vectorsource.exports b/src/ol/source/vectorsource.exports new file mode 100644 index 0000000000..27cbc2bea0 --- /dev/null +++ b/src/ol/source/vectorsource.exports @@ -0,0 +1 @@ +@exportClass ol.source.Vector ol.source.SourceOptions diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index 60ff1f1228..e45cc25548 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -13,27 +13,13 @@ goog.require('ol.geom.GeometryType'); goog.require('ol.source.Source'); -/** - * @typedef {{attributions: (Array.|undefined), - * extent: (ol.Extent|undefined), - * projection: (ol.Projection|undefined)}} - */ -ol.source.VectorOptions; - - /** * @constructor * @extends {ol.source.Source} - * @param {ol.source.VectorOptions} options Source options. + * @param {ol.source.SourceOptions} options Source options. */ ol.source.Vector = function(options) { - - goog.base(this, { - attributions: options.attributions, - extent: options.extent, - projection: options.projection - }); - + goog.base(this, options); }; goog.inherits(ol.source.Vector, ol.source.Source); diff --git a/src/ol/style.exports b/src/ol/style.exports new file mode 100644 index 0000000000..9b47275dfb --- /dev/null +++ b/src/ol/style.exports @@ -0,0 +1,7 @@ +@exportClass ol.style.Line ol.style.LineOptions +@exportClass ol.style.Polygon ol.style.PolygonOptions +@exportClass ol.style.Rule ol.style.RuleOptions +@exportClass ol.style.Shape ol.style.ShapeOptions +@exportClass ol.style.Style ol.style.StyleOptions +@exportSymbol ol.style.ShapeType +@exportProperty ol.style.ShapeType.CIRCLE diff --git a/src/ol/style/line.js b/src/ol/style/line.js index 1f8d4bea80..59de816f72 100644 --- a/src/ol/style/line.js +++ b/src/ol/style/line.js @@ -24,12 +24,15 @@ ol.style.LineLiteralOptions; ol.style.LineLiteral = function(config) { goog.base(this); + goog.asserts.assertString(config.strokeStyle, 'strokeStyle must be a string'); /** @type {string} */ this.strokeStyle = config.strokeStyle; + goog.asserts.assertNumber(config.strokeWidth, 'strokeWidth must be a number'); /** @type {number} */ this.strokeWidth = config.strokeWidth; + goog.asserts.assertNumber(config.opacity, 'opacity must be a number'); /** @type {number} */ this.opacity = config.opacity; @@ -47,14 +50,6 @@ ol.style.LineLiteral.prototype.equals = function(lineLiteral) { }; -/** - * @typedef {{strokeStyle: (string|ol.Expression|undefined), - * strokeWidth: (number|ol.Expression|undefined), - * opacity: (number|ol.Expression|undefined)}} - */ -ol.style.LineOptions; - - /** * @constructor diff --git a/src/ol/style/polygon.js b/src/ol/style/polygon.js index 7a126abd99..c144ada353 100644 --- a/src/ol/style/polygon.js +++ b/src/ol/style/polygon.js @@ -25,15 +25,19 @@ ol.style.PolygonLiteralOptions; ol.style.PolygonLiteral = function(config) { goog.base(this); + goog.asserts.assertString(config.fillStyle, 'fillStyle must be a string'); /** @type {string} */ this.fillStyle = config.fillStyle; + goog.asserts.assertString(config.strokeStyle, 'strokeStyle must be a string'); /** @type {string} */ this.strokeStyle = config.strokeStyle; + goog.asserts.assertNumber(config.strokeWidth, 'strokeWidth must be a number'); /** @type {number} */ this.strokeWidth = config.strokeWidth; + goog.asserts.assertNumber(config.opacity, 'opacity must be a number'); /** @type {number} */ this.opacity = config.opacity; @@ -52,15 +56,6 @@ ol.style.PolygonLiteral.prototype.equals = function(polygonLiteral) { }; -/** - * @typedef {{fillStyle: (string|ol.Expression|undefined), - * strokeStyle: (string|ol.Expression|undefined), - * strokeWidth: (number|ol.Expression|undefined), - * opacity: (number|ol.Expression|undefined)}} - */ -ol.style.PolygonOptions; - - /** * @constructor diff --git a/src/ol/style/rule.js b/src/ol/style/rule.js index 14b054a3d3..b6f1284fea 100644 --- a/src/ol/style/rule.js +++ b/src/ol/style/rule.js @@ -5,13 +5,6 @@ goog.require('ol.filter.Filter'); goog.require('ol.style.Symbolizer'); -/** - * @typedef {{filter: (ol.filter.Filter), - * symbolizers: (Array.)}} - */ -ol.style.RuleOptions; - - /** * @constructor diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js index 6fe00005e0..83056a6276 100644 --- a/src/ol/style/shape.js +++ b/src/ol/style/shape.js @@ -35,21 +35,27 @@ ol.style.ShapeLiteralOptions; */ ol.style.ShapeLiteral = function(config) { - /** @type {string} */ + goog.asserts.assertString(config.type, 'type must be a string'); + /** @type {ol.style.ShapeType} */ this.type = config.type; + goog.asserts.assertNumber(config.size, 'size must be a number'); /** @type {number} */ this.size = config.size; + goog.asserts.assertString(config.fillStyle, 'fillStyle must be a string'); /** @type {string} */ this.fillStyle = config.fillStyle; + goog.asserts.assertString(config.strokeStyle, 'strokeStyle must be a string'); /** @type {string} */ this.strokeStyle = config.strokeStyle; + goog.asserts.assertNumber(config.strokeWidth, 'strokeWidth must be a number'); /** @type {number} */ this.strokeWidth = config.strokeWidth; + goog.asserts.assertNumber(config.opacity, 'opacity must be a number'); /** @type {number} */ this.opacity = config.opacity; @@ -70,17 +76,6 @@ ol.style.ShapeLiteral.prototype.equals = function(shapeLiteral) { }; -/** - * @typedef {{type: (ol.style.ShapeType|undefined), - * size: (number|ol.Expression|undefined), - * fillStyle: (string|ol.Expression|undefined), - * strokeStyle: (string|ol.Expression|undefined), - * strokeWidth: (number|ol.Expression|undefined), - * opacity: (number|ol.Expression|undefined)}} - */ -ol.style.ShapeOptions; - - /** * @constructor diff --git a/src/ol/style/style.js b/src/ol/style/style.js index e1afc68665..07edb53086 100644 --- a/src/ol/style/style.js +++ b/src/ol/style/style.js @@ -6,12 +6,6 @@ goog.require('ol.style.Rule'); goog.require('ol.style.SymbolizerLiteral'); -/** - * @typedef {{rules: (Array.)}} - */ -ol.style.StyleOptions; - - /** * @constructor diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js index b6e5ff89ec..f63402c935 100644 --- a/test/spec/ol/layer/vectorlayer.test.js +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -6,7 +6,7 @@ describe('ol.layer.Vector', function() { it('allows adding features', function() { var layer = new ol.layer.Vector({ - new ol.source.Vector({}) + source: new ol.source.Vector({}) }); layer.addFeatures([new ol.Feature(), new ol.Feature()]); expect(layer.getFeatures().length).toEqual(2); diff --git a/test/spec/ol/style/line.test.js b/test/spec/ol/style/line.test.js index e9957e036e..95784ab15f 100644 --- a/test/spec/ol/style/line.test.js +++ b/test/spec/ol/style/line.test.js @@ -7,15 +7,18 @@ describe('ol.style.LineLiteral', function() { it('identifies equal literals', function() { var literal = new ol.style.LineLiteral({ strokeWidth: 3, - strokeStyle: '#BADA55' + strokeStyle: '#BADA55', + opacity: 1 }); var equalLiteral = new ol.style.LineLiteral({ strokeStyle: '#BADA55', - strokeWidth: 3 + strokeWidth: 3, + opacity: 1 }); var differentLiteral = new ol.style.LineLiteral({ strokeStyle: '#013', - strokeWidth: 3 + strokeWidth: 3, + opacity: 1 }); expect(literal.equals(equalLiteral)).toBe(true); expect(literal.equals(differentLiteral)).toBe(false); diff --git a/test/spec/ol/style/polygon.test.js b/test/spec/ol/style/polygon.test.js index 3e7693bc85..4c8d443db3 100644 --- a/test/spec/ol/style/polygon.test.js +++ b/test/spec/ol/style/polygon.test.js @@ -7,15 +7,21 @@ describe('ol.style.PolygonLiteral', function() { it('identifies equal literals', function() { var literal = new ol.style.PolygonLiteral({ strokeWidth: 3, - fillStyle: '#BADA55' + strokeStyle: '#013', + fillStyle: '#BADA55', + opacity: 1 }); var equalLiteral = new ol.style.PolygonLiteral({ fillStyle: '#BADA55', - strokeWidth: 3 + strokeStyle: '#013', + strokeWidth: 3, + opacity: 1 }); var differentLiteral = new ol.style.PolygonLiteral({ fillStyle: '#013', - strokeWidth: 3 + strokeStyle: '#013', + strokeWidth: 3, + opacity: 1 }); expect(literal.equals(equalLiteral)).toBe(true); expect(literal.equals(differentLiteral)).toBe(false); diff --git a/test/spec/ol/style/shape.test.js b/test/spec/ol/style/shape.test.js index fec06b87d0..4f06a7a661 100644 --- a/test/spec/ol/style/shape.test.js +++ b/test/spec/ol/style/shape.test.js @@ -6,16 +6,28 @@ describe('ol.style.ShapeLiteral', function() { it('identifies equal literals', function() { var literal = new ol.style.ShapeLiteral({ + type: ol.style.ShapeType.CIRCLE, size: 4, - fillStyle: '#BADA55' + fillStyle: '#BADA55', + strokeStyle: '#013', + strokeWidth: 3, + opacity: 1 }); var equalLiteral = new ol.style.ShapeLiteral({ + type: ol.style.ShapeType.CIRCLE, + size: 4, fillStyle: '#BADA55', - size: 4 + strokeStyle: '#013', + strokeWidth: 3, + opacity: 1 }); var differentLiteral = new ol.style.ShapeLiteral({ + type: ol.style.ShapeType.CIRCLE, + size: 4, fillStyle: '#013', - size: 4 + strokeStyle: '#013', + strokeWidth: 3, + opacity: 1 }); expect(literal.equals(equalLiteral)).toBe(true); expect(literal.equals(differentLiteral)).toBe(false); @@ -74,3 +86,4 @@ goog.require('ol.Expression'); goog.require('ol.Feature'); goog.require('ol.style.Shape'); goog.require('ol.style.ShapeLiteral'); +goog.require('ol.style.ShapeType'); diff --git a/test/spec/ol/style/style.test.js b/test/spec/ol/style/style.test.js index f5e7530549..9b76d23b60 100644 --- a/test/spec/ol/style/style.test.js +++ b/test/spec/ol/style/style.test.js @@ -5,6 +5,7 @@ describe('ol.style.Style', function() { describe('#apply()', function() { it('applies a style to a feature', function() { + var style = new ol.style.Style({ rules: [ new ol.style.Rule({ @@ -39,8 +40,10 @@ describe('ol.style.Style', function() { it('returns an array with the Shape default for points', function() { feature.setGeometry(new ol.geom.Point([0, 0])); - expect(ol.style.Style.applyDefaultStyle(feature)[0] - .equals(ol.style.ShapeDefaults)).toBe(true); + var symbolizers = ol.style.Style.applyDefaultStyle(feature); + expect(symbolizers.length).toBe(1); + expect(symbolizers[0]).toBeA(ol.style.ShapeLiteral); + expect(symbolizers[0].equals(ol.style.ShapeDefaults)).toBe(true); }); it('returns an array with the Line default for lines', function() { @@ -66,4 +69,5 @@ goog.require('ol.geom.Polygon'); goog.require('ol.filter.Filter'); goog.require('ol.style.Rule'); goog.require('ol.style.Shape'); +goog.require('ol.style.ShapeLiteral'); goog.require('ol.style.Style'); From 9a0e7a96fe78f32273f8e07fe9136b2c97e2575c Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 5 Mar 2013 12:41:44 +0100 Subject: [PATCH 115/180] Moving the feature management to the layer The source will be responsible for fetching vector data. --- examples/style-rules.js | 79 ++++--- examples/vector-layer.js | 11 +- src/ol/layer/vectorlayer.js | 192 ++++++++++++++++++ .../canvas/canvasvectorlayerrenderer.js | 5 +- src/ol/source/vectorsource.js | 185 ----------------- test/spec/ol/layer/vectorlayer.test.js | 101 +++++++++ test/spec/ol/source/vectorsource.test.js | 109 +--------- 7 files changed, 344 insertions(+), 338 deletions(-) diff --git a/examples/style-rules.js b/examples/style-rules.js index b2c0e1bd01..aa34ce8a00 100644 --- a/examples/style-rules.js +++ b/examples/style-rules.js @@ -15,11 +15,46 @@ goog.require('ol.style.Rule'); goog.require('ol.style.Style'); -var source = new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:3857') +var style = new ol.style.Style({rules: [ + new ol.style.Rule({ + filter: new ol.filter.Filter(function(feature) { + return feature.get('where') == 'outer'; + }), + symbolizers: [ + new ol.style.Line({ + strokeStyle: new ol.Expression('color'), + strokeWidth: 4, + opacity: 1 + }) + ] + }), + new ol.style.Rule({ + filter: new ol.filter.Filter(function(feature) { + return feature.get('where') == 'inner'; + }), + symbolizers: [ + new ol.style.Line({ + strokeStyle: '#013', + strokeWidth: 4, + opacity: 1 + }), + new ol.style.Line({ + strokeStyle: new ol.Expression('color'), + strokeWidth: 2, + opacity: 1 + }) + ] + }) +]}); + +var vector = new ol.layer.Vector({ + style: style, + source: new ol.source.Vector({ + projection: ol.projection.getFromCode('EPSG:3857') + }) }); -source.addFeatures([ +vector.addFeatures([ new ol.Feature({ g: new ol.geom.LineString([[-10000000, -10000000], [10000000, 10000000]]), 'color': '#BADA55', @@ -52,44 +87,6 @@ source.addFeatures([ }) ]); -var style = new ol.style.Style({ - rules: [ - new ol.style.Rule({ - filter: new ol.filter.Filter(function(feature) { - return feature.get('where') == 'outer'; - }), - symbolizers: [ - new ol.style.Line({ - strokeStyle: new ol.Expression('color'), - strokeWidth: 4, - opacity: 1 - }) - ] - }), - new ol.style.Rule({ - filter: new ol.filter.Filter(function(feature) { - return feature.get('where') == 'inner'; - }), - symbolizers: [ - new ol.style.Line({ - strokeStyle: '#013', - strokeWidth: 4, - opacity: 1 - }), - new ol.style.Line({ - strokeStyle: new ol.Expression('color'), - strokeWidth: 2, - opacity: 1 - }) - ] - }) - ] -}); - -var vector = new ol.layer.Vector({ - source: source, - style: style -}); var map = new ol.Map({ layers: new ol.Collection([vector]), diff --git a/examples/vector-layer.js b/examples/vector-layer.js index 916dc6406b..e28f0d9cd6 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -17,11 +17,13 @@ var raster = new ol.layer.TileLayer({ source: new ol.source.MapQuestOpenAerial() }); -var source = new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:3857') +var vector = new ol.layer.Vector({ + source: new ol.source.Vector({ + projection: ol.projection.getFromCode('EPSG:3857') + }) }); -source.addFeatures([ +vector.addFeatures([ new ol.Feature({ g: new ol.geom.LineString([[-10000000, -10000000], [10000000, 10000000]]) }), @@ -31,9 +33,6 @@ source.addFeatures([ new ol.Feature({g: new ol.geom.Point([-10000000, 5000000])}) ]); -var vector = new ol.layer.Vector({ - source: source -}); var map = new ol.Map({ layers: new ol.Collection([raster, vector]), diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 6703ec3e3c..77fde89ac2 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -3,10 +3,170 @@ goog.provide('ol.layer.Vector'); goog.require('ol.Feature'); goog.require('ol.layer.Layer'); goog.require('ol.source.Vector'); +goog.require('ol.structs.RTree'); goog.require('ol.style.Style'); +/** + * @constructor + */ +ol.layer.FeatureCache = function() { + + /** + * @type {Object.} + * @private + */ + this.idLookup_; + + /** + * @type {Object.} + * @private + */ + this.geometryTypeIndex_; + + /** + * @type {Object.} + * @private + */ + this.boundsByGeometryType_; + + this.clear(); + +}; + + +/** + * Clear the cache. + */ +ol.layer.FeatureCache.prototype.clear = function() { + this.idLookup_ = {}; + + var geometryTypeIndex = {}, + boundsByGeometryType = {}, + geometryType; + for (var key in ol.geom.GeometryType) { + geometryType = ol.geom.GeometryType[key]; + geometryTypeIndex[geometryType] = {}; + boundsByGeometryType[geometryType] = new ol.structs.RTree(); + } + this.geometryTypeIndex_ = geometryTypeIndex; + this.boundsByGeometryType_ = boundsByGeometryType; +}; + + +/** + * Add a feature to the cache. + * @param {ol.Feature} feature Feature to be cached. + */ +ol.layer.FeatureCache.prototype.add = function(feature) { + var id = goog.getUid(feature).toString(), + geometry = feature.getGeometry(); + + this.idLookup_[id] = feature; + + // index by geometry type and bounding box + if (!goog.isNull(geometry)) { + var geometryType = geometry.getType(); + this.geometryTypeIndex_[geometryType][id] = feature; + this.boundsByGeometryType_[geometryType].put(geometry.getBounds(), + feature); + } +}; + + +/** + * @param {ol.filter.Filter=} opt_filter Optional filter. + * @return {Object.} Object of features, keyed by id. + * @private + */ +ol.layer.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) { + var i, features; + if (!goog.isDef(opt_filter)) { + features = this.idLookup_; + } else { + if (opt_filter instanceof ol.filter.Geometry) { + features = this.geometryTypeIndex_[opt_filter.getType()]; + } else if (opt_filter instanceof ol.filter.Extent) { + var boundsByGeometryType = this.boundsByGeometryType_, + extent = opt_filter.getExtent(); + features = {}; + for (i in boundsByGeometryType) { + goog.object.extend(features, boundsByGeometryType[i].find(extent)); + } + } else if (opt_filter instanceof ol.filter.Logical && + opt_filter.operator === ol.filter.LogicalOperator.AND) { + var filters = opt_filter.getFilters(); + if (filters.length === 2) { + var filter, geometryFilter, extentFilter; + for (i = 0; i <= 1; ++i) { + filter = filters[i]; + if (filter instanceof ol.filter.Geometry) { + geometryFilter = filter; + } else if (filter instanceof ol.filter.Extent) { + extentFilter = filter; + } + } + if (extentFilter && geometryFilter) { + features = this.boundsByGeometryType_[geometryFilter.getType()] + .find(extentFilter.getExtent()); + } + } + } + if (!goog.isDef(features)) { + // TODO: support fast lane for other filter types + var candidates = this.idLookup_, + feature; + features = {}; + for (i in candidates) { + feature = candidates[i]; + if (opt_filter.applies(feature) === true) { + features[i] = feature; + } + } + } + } + return features; +}; + + +/** + * @param {ol.filter.Filter=} opt_filter Optional filter. + * @return {Array.} Array of features. + */ +ol.layer.FeatureCache.prototype.getFeatures = function(opt_filter) { + return goog.object.getValues(this.getFeaturesObject_(opt_filter)); +}; + + +/** + * @param {ol.filter.Geometry} filter Geometry type filter. + * @return {Array.} Array of features. + * @private + */ +ol.layer.FeatureCache.prototype.getFeaturesByGeometryType_ = function(filter) { + return goog.object.getValues(this.geometryTypeIndex_[filter.getType()]); +}; + + +/** + * Get features by ids. + * @param {Array.} ids Array of (internal) identifiers. + * @return {Array.} Array of features. + * @private + */ +ol.layer.FeatureCache.prototype.getFeaturesByIds_ = function(ids) { + var len = ids.length, + features = new Array(len), + i; + for (i = 0; i < len; ++i) { + features[i] = this.idLookup_[ids[i]]; + } + return features; +}; + + + /** * @constructor * @extends {ol.layer.Layer} @@ -21,10 +181,26 @@ ol.layer.Vector = function(layerOptions) { */ this.style_ = goog.isDef(layerOptions.style) ? layerOptions.style : null; + /** + * @type {ol.layer.FeatureCache} + * @private + */ + this.featureCache_ = new ol.layer.FeatureCache(); + }; goog.inherits(ol.layer.Vector, ol.layer.Layer); +/** + * @param {Array.} features Array of features. + */ +ol.layer.Vector.prototype.addFeatures = function(features) { + for (var i = 0, ii = features.length; i < ii; ++i) { + this.featureCache_.add(features[i]); + } +}; + + /** * @return {ol.source.Vector} Source. */ @@ -33,6 +209,15 @@ ol.layer.Vector.prototype.getVectorSource = function() { }; +/** + * @param {ol.filter.Filter=} opt_filter Optional filter. + * @return {Array.} Array of features. + */ +ol.layer.Vector.prototype.getFeatures = function(opt_filter) { + return this.featureCache_.getFeatures(opt_filter); +}; + + /** * @param {Array.} features Features. * @return {Array.} symbolizers for features. @@ -72,3 +257,10 @@ ol.layer.Vector.prototype.groupFeaturesBySymbolizerLiteral = } return featuresBySymbolizer; }; + + +goog.require('ol.filter.Extent'); +goog.require('ol.filter.Geometry'); +goog.require('ol.filter.Logical'); +goog.require('ol.filter.LogicalOperator'); +goog.require('ol.geom.GeometryType'); diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index b4560e2e88..b3a0a191b6 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -167,11 +167,10 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = resolution = view2DState.resolution, extent = frameState.extent, layer = this.getVectorLayer(), - source = layer.getVectorSource(), tileGrid = this.tileGrid_; if (goog.isNull(tileGrid)) { - // lazy tile source creation to match the view projection + // lazy tile grid creation to match the view projection tileGrid = ol.tilegrid.createForProjection( view2DState.projection, 22, // should be no harm in going big here - ideally, it would be ∞ @@ -282,7 +281,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = for (i = 0; i < numFilters; ++i) { geomFilter = filters[i]; type = geomFilter.getType(); - features = source.getFeatures(new ol.filter.Logical( + features = layer.getFeatures(new ol.filter.Logical( [geomFilter, extentFilter], ol.filter.LogicalOperator.AND)); if (features.length) { groups = layer.groupFeaturesBySymbolizerLiteral(features); diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index 1dc6c0a0a1..60ff1f1228 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -11,166 +11,6 @@ goog.require('ol.filter.Logical'); goog.require('ol.filter.LogicalOperator'); goog.require('ol.geom.GeometryType'); goog.require('ol.source.Source'); -goog.require('ol.structs.RTree'); - - - -/** - * @constructor - */ -ol.source.FeatureCache = function() { - - /** - * @type {Object.} - * @private - */ - this.idLookup_; - - /** - * @type {Object.} - * @private - */ - this.geometryTypeIndex_; - - /** - * @type {Object.} - * @private - */ - this.boundsByGeometryType_; - - this.clear(); - -}; - - -/** - * Clear the cache. - */ -ol.source.FeatureCache.prototype.clear = function() { - this.idLookup_ = {}; - - var geometryTypeIndex = {}, - boundsByGeometryType = {}, - geometryType; - for (var key in ol.geom.GeometryType) { - geometryType = ol.geom.GeometryType[key]; - geometryTypeIndex[geometryType] = {}; - boundsByGeometryType[geometryType] = new ol.structs.RTree(); - } - this.geometryTypeIndex_ = geometryTypeIndex; - this.boundsByGeometryType_ = boundsByGeometryType; -}; - - -/** - * Add a feature to the cache. - * @param {ol.Feature} feature Feature to be cached. - */ -ol.source.FeatureCache.prototype.add = function(feature) { - var id = goog.getUid(feature).toString(), - geometry = feature.getGeometry(); - - this.idLookup_[id] = feature; - - // index by geometry type and bounding box - if (!goog.isNull(geometry)) { - var geometryType = geometry.getType(); - this.geometryTypeIndex_[geometryType][id] = feature; - this.boundsByGeometryType_[geometryType].put(geometry.getBounds(), - feature); - } -}; - - -/** - * @param {ol.filter.Filter=} opt_filter Optional filter. - * @return {Object.} Object of features, keyed by id. - * @private - */ -ol.source.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) { - var i, features; - if (!goog.isDef(opt_filter)) { - features = this.idLookup_; - } else { - if (opt_filter instanceof ol.filter.Geometry) { - features = this.geometryTypeIndex_[opt_filter.getType()]; - } else if (opt_filter instanceof ol.filter.Extent) { - var boundsByGeometryType = this.boundsByGeometryType_, - extent = opt_filter.getExtent(); - features = {}; - for (i in boundsByGeometryType) { - goog.object.extend(features, boundsByGeometryType[i].find(extent)); - } - } else if (opt_filter instanceof ol.filter.Logical && - opt_filter.operator === ol.filter.LogicalOperator.AND) { - var filters = opt_filter.getFilters(); - if (filters.length === 2) { - var filter, geometryFilter, extentFilter; - for (i = 0; i <= 1; ++i) { - filter = filters[i]; - if (filter instanceof ol.filter.Geometry) { - geometryFilter = filter; - } else if (filter instanceof ol.filter.Extent) { - extentFilter = filter; - } - } - if (extentFilter && geometryFilter) { - features = this.boundsByGeometryType_[geometryFilter.getType()] - .find(extentFilter.getExtent()); - } - } - } - if (!goog.isDef(features)) { - // TODO: support fast lane for other filter types - var candidates = this.idLookup_, - feature; - features = {}; - for (i in candidates) { - feature = candidates[i]; - if (opt_filter.applies(feature) === true) { - features[i] = feature; - } - } - } - } - return features; -}; - - -/** - * @param {ol.filter.Filter=} opt_filter Optional filter. - * @return {Array.} Array of features. - */ -ol.source.FeatureCache.prototype.getFeatures = function(opt_filter) { - return goog.object.getValues(this.getFeaturesObject_(opt_filter)); -}; - - -/** - * @param {ol.filter.Geometry} filter Geometry type filter. - * @return {Array.} Array of features. - * @private - */ -ol.source.FeatureCache.prototype.getFeaturesByGeometryType_ = function(filter) { - return goog.object.getValues(this.geometryTypeIndex_[filter.getType()]); -}; - - -/** - * Get features by ids. - * @param {Array.} ids Array of (internal) identifiers. - * @return {Array.} Array of features. - * @private - */ -ol.source.FeatureCache.prototype.getFeaturesByIds_ = function(ids) { - var len = ids.length, - features = new Array(len), - i; - for (i = 0; i < len; ++i) { - features[i] = this.idLookup_[ids[i]]; - } - return features; -}; /** @@ -195,30 +35,5 @@ ol.source.Vector = function(options) { projection: options.projection }); - /** - * @type {ol.source.FeatureCache} - * @private - */ - this.featureCache_ = new ol.source.FeatureCache(); - }; goog.inherits(ol.source.Vector, ol.source.Source); - - -/** - * @param {Array.} features Array of features. - */ -ol.source.Vector.prototype.addFeatures = function(features) { - for (var i = 0, ii = features.length; i < ii; ++i) { - this.featureCache_.add(features[i]); - } -}; - - -/** - * @param {ol.filter.Filter=} opt_filter Optional filter. - * @return {Array.} Array of features. - */ -ol.source.Vector.prototype.getFeatures = function(opt_filter) { - return this.featureCache_.getFeatures(opt_filter); -}; diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js index c08053419d..b6e5ff89ec 100644 --- a/test/spec/ol/layer/vectorlayer.test.js +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -2,6 +2,99 @@ goog.provide('ol.test.layer.Vector'); describe('ol.layer.Vector', function() { + describe('#addFeatures()', function() { + + it('allows adding features', function() { + var layer = new ol.layer.Vector({ + new ol.source.Vector({}) + }); + layer.addFeatures([new ol.Feature(), new ol.Feature()]); + expect(layer.getFeatures().length).toEqual(2); + }); + }); + + describe('#getFeatures()', function() { + + var layer, features; + + beforeEach(function() { + features = [ + new ol.Feature({ + g: new ol.geom.Point([16.0, 48.0]) + }), + new ol.Feature({ + g: new ol.geom.Point([16.1, 48.1]) + }), + new ol.Feature({ + g: new ol.geom.Point([16.2, 48.2]) + }), + new ol.Feature({ + g: new ol.geom.Point([16.3, 48.3]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[16.4, 48.4], [16.5, 48.5]]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[16.6, 48.6], [16.7, 48.7]]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[16.8, 48.8], [16.9, 48.9]]) + }), + new ol.Feature({ + g: new ol.geom.LineString([[17.0, 49.0], [17.1, 49.1]]) + }) + ]; + layer = new ol.layer.Vector({ + source: new ol.source.Vector({}) + }); + layer.addFeatures(features); + }); + + var geomFilter = new ol.filter.Geometry(ol.geom.GeometryType.LINESTRING); + var extentFilter = new ol.filter.Extent(new ol.Extent(16, 48, 16.3, 48.3)); + + it('can filter by geometry type using its GeometryType index', function() { + spyOn(geomFilter, 'applies'); + var lineStrings = layer.getFeatures(geomFilter); + expect(geomFilter.applies).not.toHaveBeenCalled(); + expect(lineStrings.length).toEqual(4); + expect(lineStrings).toContain(features[4]); + }); + + it('can filter by extent using its RTree', function() { + spyOn(extentFilter, 'applies'); + var subset = layer.getFeatures(extentFilter); + expect(extentFilter.applies).not.toHaveBeenCalled(); + expect(subset.length).toEqual(4); + expect(subset).not.toContain(features[7]); + }); + + it('can filter by extent and geometry type using its index', function() { + var filter1 = new ol.filter.Logical([geomFilter, extentFilter], + ol.filter.LogicalOperator.AND); + var filter2 = new ol.filter.Logical([extentFilter, geomFilter], + ol.filter.LogicalOperator.AND); + spyOn(filter1, 'applies'); + spyOn(filter2, 'applies'); + var subset1 = layer.getFeatures(filter1); + var subset2 = layer.getFeatures(filter2); + expect(filter1.applies).not.toHaveBeenCalled(); + expect(filter2.applies).not.toHaveBeenCalled(); + expect(subset1.length).toEqual(0); + expect(subset2.length).toEqual(0); + }); + + it('can handle query using the filter\'s applies function', function() { + var filter = new ol.filter.Logical([geomFilter, extentFilter], + ol.filter.LogicalOperator.OR); + spyOn(filter, 'applies').andCallThrough(); + var subset = layer.getFeatures(filter); + expect(filter.applies).toHaveBeenCalled(); + expect(subset.length).toEqual(8); + }); + + }); + describe('#groupFeaturesBySymbolizerLiteral()', function() { var layer = new ol.layer.Vector({ @@ -84,8 +177,16 @@ describe('ol.layer.Vector', function() { }); goog.require('ol.Expression'); +goog.require('ol.Extent'); goog.require('ol.Feature'); +goog.require('ol.Projection'); +goog.require('ol.filter.Extent'); +goog.require('ol.filter.Geometry'); +goog.require('ol.filter.Logical'); +goog.require('ol.filter.LogicalOperator'); +goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); +goog.require('ol.geom.Point'); goog.require('ol.projection'); goog.require('ol.layer.Vector'); goog.require('ol.source.Vector'); diff --git a/test/spec/ol/source/vectorsource.test.js b/test/spec/ol/source/vectorsource.test.js index 37a6b46113..c2cbfdf028 100644 --- a/test/spec/ol/source/vectorsource.test.js +++ b/test/spec/ol/source/vectorsource.test.js @@ -3,112 +3,15 @@ goog.provide('ol.test.source.Vector'); describe('ol.source.Vector', function() { - var vectorSource; - - describe('#addFeatures()', function() { - - it('works', function() { - vectorSource = new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:4326') - }); - vectorSource.addFeatures([new ol.Feature()]); - expect(vectorSource.getFeatures().length).toEqual(1); + describe('constructor', function() { + it('creates an instance', function() { + var source = new ol.source.Vector({}); + expect(source).toBeA(ol.source.Vector); + expect(source).toBeA(ol.source.Source); }); }); - describe('#getFeatures()', function() { - - var features; - - beforeEach(function() { - features = [ - new ol.Feature({ - g: new ol.geom.Point([16.0, 48.0]) - }), - new ol.Feature({ - g: new ol.geom.Point([16.1, 48.1]) - }), - new ol.Feature({ - g: new ol.geom.Point([16.2, 48.2]) - }), - new ol.Feature({ - g: new ol.geom.Point([16.3, 48.3]) - }), - new ol.Feature({ - g: new ol.geom.LineString([[16.4, 48.4], [16.5, 48.5]]) - }), - new ol.Feature({ - g: new ol.geom.LineString([[16.6, 48.6], [16.7, 48.7]]) - }), - new ol.Feature({ - g: new ol.geom.LineString([[16.8, 48.8], [16.9, 48.9]]) - }), - new ol.Feature({ - g: new ol.geom.LineString([[17.0, 49.0], [17.1, 49.1]]) - }) - ]; - vectorSource = new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:4326') - }); - vectorSource.addFeatures(features); - }); - - var geomFilter = new ol.filter.Geometry(ol.geom.GeometryType.LINESTRING); - var extentFilter = new ol.filter.Extent(new ol.Extent(16, 48, 16.3, 48.3)); - - it('can filter by geometry type using its GeometryType index', function() { - spyOn(geomFilter, 'applies'); - var lineStrings = vectorSource.getFeatures(geomFilter); - expect(geomFilter.applies).not.toHaveBeenCalled(); - expect(lineStrings.length).toEqual(4); - expect(lineStrings).toContain(features[4]); - }); - - it('can filter by extent using its RTree', function() { - spyOn(extentFilter, 'applies'); - var subset = vectorSource.getFeatures(extentFilter); - expect(extentFilter.applies).not.toHaveBeenCalled(); - expect(subset.length).toEqual(4); - expect(subset).not.toContain(features[7]); - }); - - it('can filter by extent and geometry type using its index', function() { - var filter1 = new ol.filter.Logical([geomFilter, extentFilter], - ol.filter.LogicalOperator.AND); - var filter2 = new ol.filter.Logical([extentFilter, geomFilter], - ol.filter.LogicalOperator.AND); - spyOn(filter1, 'applies'); - spyOn(filter2, 'applies'); - var subset1 = vectorSource.getFeatures(filter1); - var subset2 = vectorSource.getFeatures(filter2); - expect(filter1.applies).not.toHaveBeenCalled(); - expect(filter2.applies).not.toHaveBeenCalled(); - expect(subset1.length).toEqual(0); - expect(subset2.length).toEqual(0); - }); - - it('can handle query using the filter\'s applies function', function() { - var filter = new ol.filter.Logical([geomFilter, extentFilter], - ol.filter.LogicalOperator.OR); - spyOn(filter, 'applies').andCallThrough(); - var subset = vectorSource.getFeatures(filter); - expect(filter.applies).toHaveBeenCalled(); - expect(subset.length).toEqual(8); - }); - - }); - }); -goog.require('ol.Extent'); -goog.require('ol.Feature'); -goog.require('ol.Projection'); -goog.require('ol.filter.Extent'); -goog.require('ol.filter.Geometry'); -goog.require('ol.filter.Logical'); -goog.require('ol.filter.LogicalOperator'); -goog.require('ol.geom.GeometryType'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.LineString'); +goog.require('ol.source.Source'); goog.require('ol.source.Vector'); -goog.require('ol.projection'); From 4f2523fcf2ce019c524a95a14ead5315baab4461 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 5 Mar 2013 18:13:10 +0100 Subject: [PATCH 116/180] Vector related exports --- src/objectliterals.exports | 31 ++++++++++++++++++++++++++ src/ol/feature.exports | 6 +++++ src/ol/filter/filter.exports | 7 ++++++ src/ol/geom/expression.exports | 1 + src/ol/geom/geometry.exports | 6 +++++ src/ol/layer/vectorlayer.exports | 3 +++ src/ol/source/vectorsource.exports | 1 + src/ol/source/vectorsource.js | 18 ++------------- src/ol/style.exports | 7 ++++++ src/ol/style/line.js | 11 +++------ src/ol/style/polygon.js | 13 ++++------- src/ol/style/rule.js | 7 ------ src/ol/style/shape.js | 19 ++++++---------- src/ol/style/style.js | 6 ----- test/spec/ol/layer/vectorlayer.test.js | 2 +- test/spec/ol/style/line.test.js | 9 +++++--- test/spec/ol/style/polygon.test.js | 12 +++++++--- test/spec/ol/style/shape.test.js | 19 +++++++++++++--- test/spec/ol/style/style.test.js | 8 +++++-- 19 files changed, 116 insertions(+), 70 deletions(-) create mode 100644 src/ol/feature.exports create mode 100644 src/ol/filter/filter.exports create mode 100644 src/ol/geom/expression.exports create mode 100644 src/ol/geom/geometry.exports create mode 100644 src/ol/layer/vectorlayer.exports create mode 100644 src/ol/source/vectorsource.exports create mode 100644 src/ol/style.exports diff --git a/src/objectliterals.exports b/src/objectliterals.exports index 87a20a44c6..8df22dc74e 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -111,6 +111,11 @@ @exportObjectLiteralProperty ol.source.SingleImageWMSOptions.resolutions Array.|undefined @exportObjectLiteralProperty ol.source.SingleImageWMSOptions.url string|undefined +@exportObjectLiteral ol.source.SourceOptions +@exportObjectLiteralProperty ol.source.SourceOptions.attributions Array.|undefined +@exportObjectLiteralProperty ol.source.SourceOptions.extent ol.Extent|undefined +@exportObjectLiteralProperty ol.source.SourceOptions.projection ol.Projection|undefined + @exportObjectLiteral ol.source.StamenOptions @exportObjectLiteralProperty ol.source.StamenOptions.layer string @exportObjectLiteralProperty ol.source.StamenOptions.minZoom number|undefined @@ -138,6 +143,32 @@ @exportObjectLiteralProperty ol.source.TiledWMSOptions.url string|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.urls Array.|undefined +@exportObjectLiteral ol.style.LineOptions +@exportObjectLiteralProperty ol.style.LineOptions.strokeStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.LineOptions.strokeWidth number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.LineOptions.opacity number|ol.Expression|undefined + +@exportObjectLiteral ol.style.PolygonOptions +@exportObjectLiteralProperty ol.style.PolygonOptions.fillStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.PolygonOptions.strokeStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.PolygonOptions.strokeWidth number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.PolygonOptions.opacity number|ol.Expression|undefined + +@exportObjectLiteral ol.style.RuleOptions +@exportObjectLiteralProperty ol.style.RuleOptions.filter ol.filter.Filter|undefined +@exportObjectLiteralProperty ol.style.RuleOptions.symbolizers Array.|undefined + +@exportObjectLiteral ol.style.ShapeOptions +@exportObjectLiteralProperty ol.style.ShapeOptions.type ol.style.ShapeType|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.size number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.fillStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.strokeStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.strokeWidth number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.opacity number|ol.Expression|undefined + +@exportObjectLiteral ol.style.StyleOptions +@exportObjectLiteralProperty ol.style.StyleOptions.rules Array. + @exportObjectLiteral ol.tilegrid.TileGridOptions @exportObjectLiteralProperty ol.tilegrid.TileGridOptions.origin ol.Coordinate|undefined @exportObjectLiteralProperty ol.tilegrid.TileGridOptions.origins Array.|undefined diff --git a/src/ol/feature.exports b/src/ol/feature.exports new file mode 100644 index 0000000000..3192a1fd2d --- /dev/null +++ b/src/ol/feature.exports @@ -0,0 +1,6 @@ +@exportSymbol ol.Feature +@exportProperty ol.Feature.prototype.get +@exportProperty ol.Feature.prototype.getAttributes +@exportProperty ol.Feature.prototype.getGeometry +@exportProperty ol.Feature.prototype.set +@exportProperty ol.Feature.prototype.setGeometry diff --git a/src/ol/filter/filter.exports b/src/ol/filter/filter.exports new file mode 100644 index 0000000000..696edee702 --- /dev/null +++ b/src/ol/filter/filter.exports @@ -0,0 +1,7 @@ +@exportSymbol ol.filter.Filter +@exportSymbol ol.filter.Geometry +@exportSymbol ol.filter.Logical + +@exportSymbol ol.filter.LogicalOperator +@exportProperty ol.filter.LogicalOperator.AND +@exportProperty ol.filter.LogicalOperator.OR diff --git a/src/ol/geom/expression.exports b/src/ol/geom/expression.exports new file mode 100644 index 0000000000..911b8a12c6 --- /dev/null +++ b/src/ol/geom/expression.exports @@ -0,0 +1 @@ +@exportSymbol ol.Expression diff --git a/src/ol/geom/geometry.exports b/src/ol/geom/geometry.exports new file mode 100644 index 0000000000..4170284a9e --- /dev/null +++ b/src/ol/geom/geometry.exports @@ -0,0 +1,6 @@ +@exportSymbol ol.geom.Point +@exportSymbol ol.geom.LineString +@exportSymbol ol.geom.Polygon +@exportSymbol ol.geom.MultiPoint +@exportSymbol ol.geom.MultiLineString +@exportSymbol ol.geom.MultiPolygon diff --git a/src/ol/layer/vectorlayer.exports b/src/ol/layer/vectorlayer.exports new file mode 100644 index 0000000000..6abe609b01 --- /dev/null +++ b/src/ol/layer/vectorlayer.exports @@ -0,0 +1,3 @@ +@exportClass ol.layer.Vector ol.layer.LayerOptions + +@exportProperty ol.layer.Vector.prototype.addFeatures diff --git a/src/ol/source/vectorsource.exports b/src/ol/source/vectorsource.exports new file mode 100644 index 0000000000..27cbc2bea0 --- /dev/null +++ b/src/ol/source/vectorsource.exports @@ -0,0 +1 @@ +@exportClass ol.source.Vector ol.source.SourceOptions diff --git a/src/ol/source/vectorsource.js b/src/ol/source/vectorsource.js index 60ff1f1228..e45cc25548 100644 --- a/src/ol/source/vectorsource.js +++ b/src/ol/source/vectorsource.js @@ -13,27 +13,13 @@ goog.require('ol.geom.GeometryType'); goog.require('ol.source.Source'); -/** - * @typedef {{attributions: (Array.|undefined), - * extent: (ol.Extent|undefined), - * projection: (ol.Projection|undefined)}} - */ -ol.source.VectorOptions; - - /** * @constructor * @extends {ol.source.Source} - * @param {ol.source.VectorOptions} options Source options. + * @param {ol.source.SourceOptions} options Source options. */ ol.source.Vector = function(options) { - - goog.base(this, { - attributions: options.attributions, - extent: options.extent, - projection: options.projection - }); - + goog.base(this, options); }; goog.inherits(ol.source.Vector, ol.source.Source); diff --git a/src/ol/style.exports b/src/ol/style.exports new file mode 100644 index 0000000000..9b47275dfb --- /dev/null +++ b/src/ol/style.exports @@ -0,0 +1,7 @@ +@exportClass ol.style.Line ol.style.LineOptions +@exportClass ol.style.Polygon ol.style.PolygonOptions +@exportClass ol.style.Rule ol.style.RuleOptions +@exportClass ol.style.Shape ol.style.ShapeOptions +@exportClass ol.style.Style ol.style.StyleOptions +@exportSymbol ol.style.ShapeType +@exportProperty ol.style.ShapeType.CIRCLE diff --git a/src/ol/style/line.js b/src/ol/style/line.js index 1f8d4bea80..59de816f72 100644 --- a/src/ol/style/line.js +++ b/src/ol/style/line.js @@ -24,12 +24,15 @@ ol.style.LineLiteralOptions; ol.style.LineLiteral = function(config) { goog.base(this); + goog.asserts.assertString(config.strokeStyle, 'strokeStyle must be a string'); /** @type {string} */ this.strokeStyle = config.strokeStyle; + goog.asserts.assertNumber(config.strokeWidth, 'strokeWidth must be a number'); /** @type {number} */ this.strokeWidth = config.strokeWidth; + goog.asserts.assertNumber(config.opacity, 'opacity must be a number'); /** @type {number} */ this.opacity = config.opacity; @@ -47,14 +50,6 @@ ol.style.LineLiteral.prototype.equals = function(lineLiteral) { }; -/** - * @typedef {{strokeStyle: (string|ol.Expression|undefined), - * strokeWidth: (number|ol.Expression|undefined), - * opacity: (number|ol.Expression|undefined)}} - */ -ol.style.LineOptions; - - /** * @constructor diff --git a/src/ol/style/polygon.js b/src/ol/style/polygon.js index 7a126abd99..c144ada353 100644 --- a/src/ol/style/polygon.js +++ b/src/ol/style/polygon.js @@ -25,15 +25,19 @@ ol.style.PolygonLiteralOptions; ol.style.PolygonLiteral = function(config) { goog.base(this); + goog.asserts.assertString(config.fillStyle, 'fillStyle must be a string'); /** @type {string} */ this.fillStyle = config.fillStyle; + goog.asserts.assertString(config.strokeStyle, 'strokeStyle must be a string'); /** @type {string} */ this.strokeStyle = config.strokeStyle; + goog.asserts.assertNumber(config.strokeWidth, 'strokeWidth must be a number'); /** @type {number} */ this.strokeWidth = config.strokeWidth; + goog.asserts.assertNumber(config.opacity, 'opacity must be a number'); /** @type {number} */ this.opacity = config.opacity; @@ -52,15 +56,6 @@ ol.style.PolygonLiteral.prototype.equals = function(polygonLiteral) { }; -/** - * @typedef {{fillStyle: (string|ol.Expression|undefined), - * strokeStyle: (string|ol.Expression|undefined), - * strokeWidth: (number|ol.Expression|undefined), - * opacity: (number|ol.Expression|undefined)}} - */ -ol.style.PolygonOptions; - - /** * @constructor diff --git a/src/ol/style/rule.js b/src/ol/style/rule.js index 14b054a3d3..b6f1284fea 100644 --- a/src/ol/style/rule.js +++ b/src/ol/style/rule.js @@ -5,13 +5,6 @@ goog.require('ol.filter.Filter'); goog.require('ol.style.Symbolizer'); -/** - * @typedef {{filter: (ol.filter.Filter), - * symbolizers: (Array.)}} - */ -ol.style.RuleOptions; - - /** * @constructor diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js index 6fe00005e0..83056a6276 100644 --- a/src/ol/style/shape.js +++ b/src/ol/style/shape.js @@ -35,21 +35,27 @@ ol.style.ShapeLiteralOptions; */ ol.style.ShapeLiteral = function(config) { - /** @type {string} */ + goog.asserts.assertString(config.type, 'type must be a string'); + /** @type {ol.style.ShapeType} */ this.type = config.type; + goog.asserts.assertNumber(config.size, 'size must be a number'); /** @type {number} */ this.size = config.size; + goog.asserts.assertString(config.fillStyle, 'fillStyle must be a string'); /** @type {string} */ this.fillStyle = config.fillStyle; + goog.asserts.assertString(config.strokeStyle, 'strokeStyle must be a string'); /** @type {string} */ this.strokeStyle = config.strokeStyle; + goog.asserts.assertNumber(config.strokeWidth, 'strokeWidth must be a number'); /** @type {number} */ this.strokeWidth = config.strokeWidth; + goog.asserts.assertNumber(config.opacity, 'opacity must be a number'); /** @type {number} */ this.opacity = config.opacity; @@ -70,17 +76,6 @@ ol.style.ShapeLiteral.prototype.equals = function(shapeLiteral) { }; -/** - * @typedef {{type: (ol.style.ShapeType|undefined), - * size: (number|ol.Expression|undefined), - * fillStyle: (string|ol.Expression|undefined), - * strokeStyle: (string|ol.Expression|undefined), - * strokeWidth: (number|ol.Expression|undefined), - * opacity: (number|ol.Expression|undefined)}} - */ -ol.style.ShapeOptions; - - /** * @constructor diff --git a/src/ol/style/style.js b/src/ol/style/style.js index e1afc68665..07edb53086 100644 --- a/src/ol/style/style.js +++ b/src/ol/style/style.js @@ -6,12 +6,6 @@ goog.require('ol.style.Rule'); goog.require('ol.style.SymbolizerLiteral'); -/** - * @typedef {{rules: (Array.)}} - */ -ol.style.StyleOptions; - - /** * @constructor diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js index b6e5ff89ec..f63402c935 100644 --- a/test/spec/ol/layer/vectorlayer.test.js +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -6,7 +6,7 @@ describe('ol.layer.Vector', function() { it('allows adding features', function() { var layer = new ol.layer.Vector({ - new ol.source.Vector({}) + source: new ol.source.Vector({}) }); layer.addFeatures([new ol.Feature(), new ol.Feature()]); expect(layer.getFeatures().length).toEqual(2); diff --git a/test/spec/ol/style/line.test.js b/test/spec/ol/style/line.test.js index e9957e036e..95784ab15f 100644 --- a/test/spec/ol/style/line.test.js +++ b/test/spec/ol/style/line.test.js @@ -7,15 +7,18 @@ describe('ol.style.LineLiteral', function() { it('identifies equal literals', function() { var literal = new ol.style.LineLiteral({ strokeWidth: 3, - strokeStyle: '#BADA55' + strokeStyle: '#BADA55', + opacity: 1 }); var equalLiteral = new ol.style.LineLiteral({ strokeStyle: '#BADA55', - strokeWidth: 3 + strokeWidth: 3, + opacity: 1 }); var differentLiteral = new ol.style.LineLiteral({ strokeStyle: '#013', - strokeWidth: 3 + strokeWidth: 3, + opacity: 1 }); expect(literal.equals(equalLiteral)).toBe(true); expect(literal.equals(differentLiteral)).toBe(false); diff --git a/test/spec/ol/style/polygon.test.js b/test/spec/ol/style/polygon.test.js index 3e7693bc85..4c8d443db3 100644 --- a/test/spec/ol/style/polygon.test.js +++ b/test/spec/ol/style/polygon.test.js @@ -7,15 +7,21 @@ describe('ol.style.PolygonLiteral', function() { it('identifies equal literals', function() { var literal = new ol.style.PolygonLiteral({ strokeWidth: 3, - fillStyle: '#BADA55' + strokeStyle: '#013', + fillStyle: '#BADA55', + opacity: 1 }); var equalLiteral = new ol.style.PolygonLiteral({ fillStyle: '#BADA55', - strokeWidth: 3 + strokeStyle: '#013', + strokeWidth: 3, + opacity: 1 }); var differentLiteral = new ol.style.PolygonLiteral({ fillStyle: '#013', - strokeWidth: 3 + strokeStyle: '#013', + strokeWidth: 3, + opacity: 1 }); expect(literal.equals(equalLiteral)).toBe(true); expect(literal.equals(differentLiteral)).toBe(false); diff --git a/test/spec/ol/style/shape.test.js b/test/spec/ol/style/shape.test.js index fec06b87d0..4f06a7a661 100644 --- a/test/spec/ol/style/shape.test.js +++ b/test/spec/ol/style/shape.test.js @@ -6,16 +6,28 @@ describe('ol.style.ShapeLiteral', function() { it('identifies equal literals', function() { var literal = new ol.style.ShapeLiteral({ + type: ol.style.ShapeType.CIRCLE, size: 4, - fillStyle: '#BADA55' + fillStyle: '#BADA55', + strokeStyle: '#013', + strokeWidth: 3, + opacity: 1 }); var equalLiteral = new ol.style.ShapeLiteral({ + type: ol.style.ShapeType.CIRCLE, + size: 4, fillStyle: '#BADA55', - size: 4 + strokeStyle: '#013', + strokeWidth: 3, + opacity: 1 }); var differentLiteral = new ol.style.ShapeLiteral({ + type: ol.style.ShapeType.CIRCLE, + size: 4, fillStyle: '#013', - size: 4 + strokeStyle: '#013', + strokeWidth: 3, + opacity: 1 }); expect(literal.equals(equalLiteral)).toBe(true); expect(literal.equals(differentLiteral)).toBe(false); @@ -74,3 +86,4 @@ goog.require('ol.Expression'); goog.require('ol.Feature'); goog.require('ol.style.Shape'); goog.require('ol.style.ShapeLiteral'); +goog.require('ol.style.ShapeType'); diff --git a/test/spec/ol/style/style.test.js b/test/spec/ol/style/style.test.js index f5e7530549..9b76d23b60 100644 --- a/test/spec/ol/style/style.test.js +++ b/test/spec/ol/style/style.test.js @@ -5,6 +5,7 @@ describe('ol.style.Style', function() { describe('#apply()', function() { it('applies a style to a feature', function() { + var style = new ol.style.Style({ rules: [ new ol.style.Rule({ @@ -39,8 +40,10 @@ describe('ol.style.Style', function() { it('returns an array with the Shape default for points', function() { feature.setGeometry(new ol.geom.Point([0, 0])); - expect(ol.style.Style.applyDefaultStyle(feature)[0] - .equals(ol.style.ShapeDefaults)).toBe(true); + var symbolizers = ol.style.Style.applyDefaultStyle(feature); + expect(symbolizers.length).toBe(1); + expect(symbolizers[0]).toBeA(ol.style.ShapeLiteral); + expect(symbolizers[0].equals(ol.style.ShapeDefaults)).toBe(true); }); it('returns an array with the Line default for lines', function() { @@ -66,4 +69,5 @@ goog.require('ol.geom.Polygon'); goog.require('ol.filter.Filter'); goog.require('ol.style.Rule'); goog.require('ol.style.Shape'); +goog.require('ol.style.ShapeLiteral'); goog.require('ol.style.Style'); From ab19d255c74b839488d219a90bb94c89fa37ed8a Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 6 Mar 2013 01:57:08 +0100 Subject: [PATCH 117/180] Rendering icons from external graphic urls --- src/ol/renderer/canvas/canvasrenderer.js | 128 +++++++++++-- .../canvas/canvasvectorlayerrenderer.js | 22 ++- src/ol/style/icon.js | 170 ++++++++++++++++++ test/spec/ol/parser/geojson.test.js | 2 +- 4 files changed, 306 insertions(+), 16 deletions(-) create mode 100644 src/ol/style/icon.js diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index 4f0d0b7fb4..3c2197ec85 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -2,6 +2,7 @@ goog.provide('ol.renderer.canvas.Renderer'); goog.provide('ol.renderer.canvas.SUPPORTED'); goog.require('goog.asserts'); +goog.require('goog.net.ImageLoader'); goog.require('goog.vec.Mat4'); goog.require('ol.Feature'); goog.require('ol.Pixel'); @@ -11,6 +12,7 @@ goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); goog.require('ol.geom.Point'); goog.require('ol.geom.Polygon'); +goog.require('ol.style.IconLiteral'); goog.require('ol.style.LineLiteral'); goog.require('ol.style.PointLiteral'); goog.require('ol.style.PolygonLiteral'); @@ -32,10 +34,13 @@ ol.renderer.canvas.SUPPORTED = ol.canvas.SUPPORTED; * @param {HTMLCanvasElement} canvas Target canvas. * @param {goog.vec.Mat4.Number} transform Transform. * @param {ol.Pixel=} opt_offset Pixel offset for top-left corner. This is - * provided as an optional argument as a convenience in cases where the - * transform applies to a separate canvas. + * provided as an optional argument as a convenience in cases where the + * transform applies to a separate canvas. + * @param {function()=} opt_iconLoadedCallback Callback for deferred rendering + * when images need to be loaded before rendering. */ -ol.renderer.canvas.Renderer = function(canvas, transform, opt_offset) { +ol.renderer.canvas.Renderer = + function(canvas, transform, opt_offset, opt_iconLoadedCallback) { var context = /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')), @@ -70,6 +75,12 @@ ol.renderer.canvas.Renderer = function(canvas, transform, opt_offset) { */ this.context_ = context; + /** + * @type {function()|undefined} + * @private + */ + this.iconLoadedCallback_ = opt_iconLoadedCallback; + }; @@ -77,13 +88,15 @@ ol.renderer.canvas.Renderer = function(canvas, transform, opt_offset) { * @param {ol.geom.GeometryType} type Geometry type. * @param {Array.} features Array of features. * @param {ol.style.SymbolizerLiteral} symbolizer Symbolizer. + * @return {boolean} true if deferred, false if rendered. */ ol.renderer.canvas.Renderer.prototype.renderFeaturesByGeometryType = function(type, features, symbolizer) { + var deferred = false; switch (type) { case ol.geom.GeometryType.POINT: goog.asserts.assert(symbolizer instanceof ol.style.PointLiteral); - this.renderPointFeatures_( + deferred = this.renderPointFeatures_( features, /** @type {ol.style.PointLiteral} */ (symbolizer)); break; case ol.geom.GeometryType.LINESTRING: @@ -99,6 +112,7 @@ ol.renderer.canvas.Renderer.prototype.renderFeaturesByGeometryType = default: throw new Error('Rendering not implemented for geometry type: ' + type); } + return deferred; }; @@ -138,31 +152,44 @@ ol.renderer.canvas.Renderer.prototype.renderLineStringFeatures_ = /** * @param {Array.} features Array of point features. * @param {ol.style.PointLiteral} symbolizer Point symbolizer. + * @return {boolean} true if deferred, false if rendered. * @private */ ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = function(features, symbolizer) { var context = this.context_, - canvas, i, ii, point, vec; + content, alpha, i, ii, point, vec; if (symbolizer instanceof ol.style.ShapeLiteral) { - canvas = ol.renderer.canvas.Renderer.renderShape(symbolizer); + content = ol.renderer.canvas.Renderer.renderShape(symbolizer); + alpha = 1; + } else if (symbolizer instanceof ol.style.IconLiteral) { + content = ol.renderer.canvas.Renderer.renderIcon( + symbolizer, this.iconLoadedCallback_); + alpha = symbolizer.opacity; } else { throw new Error('Unsupported symbolizer: ' + symbolizer); } - var mid = canvas.width / 2; + if (goog.isNull(content)) { + return true; + } + + var midWidth = content.width / 2; + var midHeight = content.height / 2; context.save(); - context.setTransform(1, 0, 0, 1, -mid, -mid); - context.globalAlpha = 1; + context.setTransform(1, 0, 0, 1, -midWidth, -midHeight); + context.globalAlpha = alpha; for (i = 0, ii = features.length; i < ii; ++i) { point = /** @type {ol.geom.Point} */ features[i].getGeometry(); vec = goog.vec.Mat4.multVec3( this.transform_, [point.get(0), point.get(1), 0], []); - context.drawImage(canvas, vec[0], vec[1]); + context.drawImage(content, vec[0], vec[1]); } context.restore(); + + return false; }; @@ -293,3 +320,84 @@ ol.renderer.canvas.Renderer.renderShape = function(shape) { } return canvas; }; + + +/** + * @param {ol.style.IconLiteral} icon Icon literal. + * @param {function()=} opt_callback Callback which will be called when + * the icon is loaded and rendering will work without deferring. + * @return {HTMLImageElement} image element of null if deferred. + */ +ol.renderer.canvas.Renderer.renderIcon = function(icon, opt_callback) { + var url = icon.url; + var image = ol.renderer.canvas.Renderer.icons_[url]; + var deferred = false; + if (!goog.isDef(image)) { + deferred = true; + image = /** @type {HTMLImageElement} */ + (goog.dom.createElement(goog.dom.TagName.IMG)); + goog.events.listenOnce(image, goog.events.EventType.ERROR, + goog.bind(ol.renderer.canvas.Renderer.handleIconError_, null, + opt_callback), + false, ol.renderer.canvas.Renderer.renderIcon); + goog.events.listenOnce(image, goog.events.EventType.LOAD, + goog.bind(ol.renderer.canvas.Renderer.handleIconLoad_, null, + opt_callback), + false, ol.renderer.canvas.Renderer.renderIcon); + image.setAttribute('src', url); + ol.renderer.canvas.Renderer.icons_[url] = image; + } else if (!goog.isNull(image)) { + var width = icon.width, + height = icon.height; + if (goog.isDef(width) && goog.isDef(height)) { + image.width = width; + image.height = height; + } else if (goog.isDef(width)) { + image.height = width / image.width * image.height; + } else if (goog.isDef(height)) { + image.width = height / image.height * image.width; + } + } + return deferred ? null : image; +}; + + +/** + * @type {Object.} + * @private + */ +ol.renderer.canvas.Renderer.icons_ = {}; + + +/** + * @param {function()=} opt_callback Callback. + * @param {Event=} opt_event Event. + * @private + */ +ol.renderer.canvas.Renderer.handleIconError_ = + function(opt_callback, opt_event) { + if (goog.isDef(opt_event)) { + var url = opt_event.target.getAttribute('src'); + ol.renderer.canvas.Renderer.icons_[url] = null; + ol.renderer.canvas.Renderer.handleIconLoad_(opt_callback, opt_event); + } +}; + + +/** + * @param {function()=} opt_callback Callback. + * @param {Event=} opt_event Event. + * @private + */ +ol.renderer.canvas.Renderer.handleIconLoad_ = + function(opt_callback, opt_event) { + if (goog.isDef(opt_event)) { + var url = opt_event.target.getAttribute('src'); + ol.renderer.canvas.Renderer.icons_[url] = + /** @type {HTMLImageElement} */ (opt_event.target); + } + if (goog.isDef(opt_callback)) { + opt_callback(); + } +}; + diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index b3a0a191b6..a81f18d951 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -126,6 +126,15 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { */ this.tileGrid_ = null; + /** + * @private + * @type {function()} + */ + this.requestMapRenderFrame_ = goog.bind(function() { + this.dirty_ = true; + mapRenderer.getMap().requestRenderFrame(); + }, this); + }; goog.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); @@ -252,7 +261,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = sketchCanvas.height = sketchSize.height; var sketchCanvasRenderer = new ol.renderer.canvas.Renderer( - sketchCanvas, sketchTransform); + sketchCanvas, sketchTransform, undefined, this.requestMapRenderFrame_); // clear/resize final canvas var finalCanvas = this.canvas_; @@ -267,15 +276,15 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var filters = this.geometryFilters_, numFilters = filters.length, i, geomFilter, extentFilter, type, features, - groups, group, j, numGroups; + groups, group, j, numGroups, deferred; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + deferred = false; tileCoord = new ol.TileCoord(z, x, y); key = tileCoord.toString(); if (this.tileCache_.containsKey(key)) { tilesToRender[key] = tileCoord; } else if (!frameState.viewHints[ol.ViewHint.ANIMATING]) { - tilesToRender[key] = tileCoord; extentFilter = new ol.filter.Extent( tileGrid.getTileCoordExtent(tileCoord)); for (i = 0; i < numFilters; ++i) { @@ -288,11 +297,14 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = numGroups = groups.length; for (j = 0; j < numGroups; ++j) { group = groups[j]; - sketchCanvasRenderer.renderFeaturesByGeometryType(type, - group[0], group[1]); + deferred = sketchCanvasRenderer.renderFeaturesByGeometryType( + type, group[0], group[1]) || deferred; } } } + if (!deferred) { + tilesToRender[key] = tileCoord; + } } } } diff --git a/src/ol/style/icon.js b/src/ol/style/icon.js new file mode 100644 index 0000000000..251981b6d3 --- /dev/null +++ b/src/ol/style/icon.js @@ -0,0 +1,170 @@ +goog.provide('ol.style.Icon'); +goog.provide('ol.style.IconLiteral'); +goog.provide('ol.style.IconType'); + +goog.require('ol.Expression'); +goog.require('ol.ExpressionLiteral'); +goog.require('ol.style.Point'); +goog.require('ol.style.PointLiteral'); + + +/** + * @typedef {{url: (string), + * width: (number|undefined), + * height: (number|undefined), + * opacity: (number), + * rotation: (number)}} + */ +ol.style.IconLiteralOptions; + + + +/** + * @constructor + * @extends {ol.style.PointLiteral} + * @param {ol.style.IconLiteralOptions} config Symbolizer properties. + */ +ol.style.IconLiteral = function(config) { + + /** @type {string} */ + this.url = config.url; + + /** @type {number|undefined} */ + this.width = config.width; + + /** @type {number|undefined} */ + this.height = config.height; + + /** @type {number} */ + this.opacity = config.opacity; + + /** @type {number} */ + this.rotation = config.rotation; + +}; +goog.inherits(ol.style.IconLiteral, ol.style.PointLiteral); + + +/** + * @inheritDoc + */ +ol.style.IconLiteral.prototype.equals = function(iconLiteral) { + return this.url == iconLiteral.type && + this.width == iconLiteral.width && + this.height == iconLiteral.height && + this.opacity == iconLiteral.opacity && + this.rotation == iconLiteral.rotation; +}; + + +/** + * @typedef {{url: (string|ol.Expression), + * width: (number|ol.Expression|undefined), + * height: (number|ol.Expression|undefined), + * opacity: (number|ol.Expression|undefined), + * rotation: (number|ol.Expression|undefined)}} + */ +ol.style.IconOptions; + + + +/** + * @constructor + * @extends {ol.style.Point} + * @param {ol.style.IconOptions} options Symbolizer properties. + */ +ol.style.Icon = function(options) { + + goog.asserts.assert(options.url, 'url must be set'); + + /** + * @type {ol.Expression} + * @private + */ + this.url_ = (options.url instanceof ol.Expression) ? + options.url : new ol.ExpressionLiteral(options.url); + + /** + * @type {ol.Expression} + * @private + */ + this.width_ = !goog.isDef(options.width) ? + null : + (options.width instanceof ol.Expression) ? + options.width : new ol.ExpressionLiteral(options.width); + + /** + * @type {ol.Expression} + * @private + */ + this.height_ = !goog.isDef(options.height) ? + null : + (options.height instanceof ol.Expression) ? + options.height : new ol.ExpressionLiteral(options.height); + + /** + * @type {ol.Expression} + * @private + */ + this.opacity_ = !goog.isDef(options.opacity) ? + new ol.ExpressionLiteral(ol.style.IconDefaults.opacity) : + (options.opacity instanceof ol.Expression) ? + options.opacity : new ol.ExpressionLiteral(options.opacity); + + /** + * @type {ol.Expression} + * @private + */ + this.rotation_ = !goog.isDef(options.rotation) ? + new ol.ExpressionLiteral(ol.style.IconDefaults.rotation) : + (options.rotation instanceof ol.Expression) ? + options.rotation : new ol.ExpressionLiteral(options.rotation); + +}; + + +/** + * @inheritDoc + * @return {ol.style.IconLiteral} Literal shape symbolizer. + */ +ol.style.Icon.prototype.createLiteral = function(feature) { + var attrs = feature.getAttributes(); + + var url = /** @type {string} */ (this.url_.evaluate(feature, attrs)); + goog.asserts.assert(goog.isString(url) && url != '#', 'url must be a string'); + + var width = /** @type {number|undefined} */ (goog.isNull(this.width_) ? + undefined : this.width_.evaluate(feature, attrs)); + goog.asserts.assert(!goog.isDef(width) || goog.isNumber(width), + 'width must be undefined or a number'); + + var height = /** @type {number|undefined} */ (goog.isNull(this.height_) ? + undefined : this.height_.evaluate(feature, attrs)); + goog.asserts.assert(!goog.isDef(height) || goog.isNumber(height), + 'height must be undefined or a number'); + + var opacity = /** {@type {number} */ (this.opacity_.evaluate(feature, attrs)); + goog.asserts.assertNumber(opacity, 'opacity must be a number'); + + var rotation = + /** {@type {number} */ (this.opacity_.evaluate(feature, attrs)); + goog.asserts.assertNumber(rotation, 'rotation must be a number'); + + return new ol.style.IconLiteral({ + url: url, + width: width, + height: height, + opacity: opacity, + rotation: rotation + }); +}; + + +/** + * @type {ol.style.IconLiteral} + */ +ol.style.IconDefaults = new ol.style.IconLiteral({ + url: '#', + opacity: 1, + rotation: 0 +}); diff --git a/test/spec/ol/parser/geojson.test.js b/test/spec/ol/parser/geojson.test.js index f5e750aa4d..0153e4ee2f 100644 --- a/test/spec/ol/parser/geojson.test.js +++ b/test/spec/ol/parser/geojson.test.js @@ -185,7 +185,7 @@ describe('ol.parser.GeoJSON', function() { var callback = function(feature, type) { return lookup[type]; - } + }; var result = parser.readFeaturesFromString(text, {callback: callback}); expect(result.length).toBe(179); From 29d3878ea3c1b9048c686aee5940e68fa630669d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 20 Feb 2013 16:47:22 +0100 Subject: [PATCH 118/180] zoomFactor defaults to 2 --- examples/wms-custom-proj.js | 14 -------------- src/ol/map.js | 4 ++-- src/ol/view2d.js | 6 ++---- test/spec/ol/map.test.js | 2 +- 4 files changed, 5 insertions(+), 21 deletions(-) diff --git a/examples/wms-custom-proj.js b/examples/wms-custom-proj.js index 19d03ca493..6aae577534 100644 --- a/examples/wms-custom-proj.js +++ b/examples/wms-custom-proj.js @@ -19,19 +19,6 @@ var epsg21781 = new ol.Projection('EPSG:21781', ol.ProjectionUnits.METERS, new ol.Extent(485869.5728, 76443.1884, 837076.5648, 299941.7864)); ol.projection.addProjection(epsg21781); -// We could give the single image source a set of resolutions. This prevents the -// source from requesting images of arbitrary resolutions. To try it, uncomment -// the block below and the resolutions option in the SingleImageWMS config. -/* -var projectionExtent = epsg21781.getExtent(); -var maxResolution = Math.max(projectionExtent.getWidth(), - projectionExtent.getHeight()) / 256; -var resolutions = new Array(10); -for (var i = 0; i < 10; ++i) { - resolutions[i] = maxResolution / Math.pow(2.0, i); -} -*/ - var extent = new ol.Extent(420000, 30000, 900000, 350000); var layers = new ol.Collection([ new ol.layer.TileLayer({ @@ -50,7 +37,6 @@ var layers = new ol.Collection([ }), new ol.layer.ImageLayer({ source: new ol.source.SingleImageWMS({ - //resolutions: resolutions, url: 'http://wms.geo.admin.ch/', attributions: [new ol.Attribution( '© ' + diff --git a/src/ol/map.js b/src/ol/map.js index 9dfcee6d75..771122e531 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -941,7 +941,7 @@ ol.Map.createControls_ = function(mapOptions) { mapOptions.zoomControl : true; if (zoomControl) { var zoomDelta = goog.isDef(mapOptions.zoomDelta) ? - mapOptions.zoomDelta : 4; + mapOptions.zoomDelta : 1; controls.push(new ol.control.Zoom({ delta: zoomDelta })); @@ -971,7 +971,7 @@ ol.Map.createInteractions_ = function(mapOptions) { mapOptions.doubleClickZoom : true; if (doubleClickZoom) { var zoomDelta = goog.isDef(mapOptions.zoomDelta) ? - mapOptions.zoomDelta : 4; + mapOptions.zoomDelta : 1; interactions.push(new ol.interaction.DblClickZoom(zoomDelta)); } diff --git a/src/ol/view2d.js b/src/ol/view2d.js index f80d3cf25c..9f8a82db1c 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -359,10 +359,8 @@ ol.View2D.createConstraints_ = function(view2DOptions) { maxResolution = Math.max( projectionExtent.maxX - projectionExtent.minX, projectionExtent.maxY - projectionExtent.minY) / ol.DEFAULT_TILE_SIZE; - // number of steps we want between two data resolutions - var numSteps = 4; - numZoomLevels = 29 * numSteps; - zoomFactor = Math.exp(Math.log(2) / numSteps); + numZoomLevels = 29; + zoomFactor = 2; } resolutionConstraint = ol.ResolutionConstraint.createSnapToPower( zoomFactor, maxResolution, numZoomLevels - 1); diff --git a/test/spec/ol/map.test.js b/test/spec/ol/map.test.js index 776d268400..c5e0ff2b30 100644 --- a/test/spec/ol/map.test.js +++ b/test/spec/ol/map.test.js @@ -121,7 +121,7 @@ describe('ol.Map', function() { var interactions = ol.Map.createInteractions_(options); expect(interactions.getLength()).toEqual(1); expect(interactions.getAt(0)).toBeA(ol.interaction.DblClickZoom); - expect(interactions.getAt(0).delta_).toEqual(4); + expect(interactions.getAt(0).delta_).toEqual(1); }); }); From be5967a94864bb2aadbd68445b514390e356e0bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 20 Feb 2013 16:51:48 +0100 Subject: [PATCH 119/180] View2D zoom may animate pan --- src/ol/view2d.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ol/view2d.js b/src/ol/view2d.js index 9f8a82db1c..2fcc1f9016 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -4,6 +4,7 @@ goog.provide('ol.View2D'); goog.provide('ol.View2DProperty'); +goog.require('goog.fx.easing'); goog.require('ol.Constraints'); goog.require('ol.Coordinate'); goog.require('ol.Extent'); @@ -312,12 +313,22 @@ ol.View2D.prototype.zoom_ = function(map, resolution, opt_anchor) { */ ol.View2D.prototype.zoom = function(map, delta, opt_anchor, opt_duration) { var currentResolution = this.getResolution(); - if (goog.isDef(currentResolution) && goog.isDef(opt_duration)) { + var currentCenter = this.getCenter(); + if (goog.isDef(currentResolution) && goog.isDef(currentCenter) && + goog.isDef(opt_duration)) { map.requestRenderFrame(); map.addPreRenderFunction(ol.animation.zoom({ resolution: currentResolution, - duration: opt_duration + duration: opt_duration, + easing: goog.fx.easing.easeOut })); + if (goog.isDef(opt_anchor)) { + map.addPreRenderFunction(ol.animation.pan({ + source: currentCenter, + duration: opt_duration, + easing: goog.fx.easing.easeOut + })); + } } var resolution = this.constraints_.resolution(currentResolution, delta); this.zoom_(map, resolution, opt_anchor); From 673ea079f875d0b0d38109a0836dfc5c1465fe5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 20 Feb 2013 16:52:20 +0100 Subject: [PATCH 120/180] MouseWheelZoom interaction animates zoom --- src/ol/interaction/mousewheelzoominteraction.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ol/interaction/mousewheelzoominteraction.js b/src/ol/interaction/mousewheelzoominteraction.js index a0e8f52cc9..9b49e01024 100644 --- a/src/ol/interaction/mousewheelzoominteraction.js +++ b/src/ol/interaction/mousewheelzoominteraction.js @@ -8,6 +8,12 @@ goog.require('ol.View2D'); goog.require('ol.interaction.Interaction'); +/** + * @define {number} Zoom duration. + */ +ol.interaction.ZOOM_DURATION = 200; + + /** * @constructor @@ -43,7 +49,7 @@ ol.interaction.MouseWheelZoom.prototype.handleMapBrowserEvent = var view = map.getView(); goog.asserts.assert(view instanceof ol.View2D); map.requestRenderFrame(); - view.zoom(map, delta, anchor); + view.zoom(map, delta, anchor, ol.interaction.ZOOM_DURATION); mapBrowserEvent.preventDefault(); mouseWheelEvent.preventDefault(); } From ca31cf590ce895b6c0292140469c83d0d95648ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 21 Feb 2013 14:52:15 +0100 Subject: [PATCH 121/180] Debounce mousewheel zoom events --- src/objectliterals.exports | 1 - .../interaction/mousewheelzoominteraction.js | 91 ++++++++++++++++--- src/ol/map.js | 5 +- test/spec/ol/map.test.js | 27 +----- 4 files changed, 84 insertions(+), 40 deletions(-) diff --git a/src/objectliterals.exports b/src/objectliterals.exports index 13f0d5808c..fc82542a8b 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -7,7 +7,6 @@ @exportObjectLiteralProperty ol.MapOptions.keyboardPanOffset number|undefined @exportObjectLiteralProperty ol.MapOptions.layers ol.Collection|undefined @exportObjectLiteralProperty ol.MapOptions.mouseWheelZoom boolean|undefined -@exportObjectLiteralProperty ol.MapOptions.mouseWheelZoomDelta number|undefined @exportObjectLiteralProperty ol.MapOptions.renderer ol.RendererHint|undefined @exportObjectLiteralProperty ol.MapOptions.renderers Array.|undefined @exportObjectLiteralProperty ol.MapOptions.scaleLineControl boolean|undefined diff --git a/src/ol/interaction/mousewheelzoominteraction.js b/src/ol/interaction/mousewheelzoominteraction.js index 9b49e01024..b274dd7e35 100644 --- a/src/ol/interaction/mousewheelzoominteraction.js +++ b/src/ol/interaction/mousewheelzoominteraction.js @@ -4,30 +4,63 @@ goog.provide('ol.interaction.MouseWheelZoom'); goog.require('goog.events.MouseWheelEvent'); goog.require('goog.events.MouseWheelHandler.EventType'); +goog.require('goog.math'); +goog.require('ol.Coordinate'); goog.require('ol.View2D'); goog.require('ol.interaction.Interaction'); /** - * @define {number} Zoom duration. + * @define {number} Animation duration. */ -ol.interaction.ZOOM_DURATION = 200; +ol.interaction.MOUSEWHEELZOOM_ANIMATION_DURATION = 250; + + +/** + * @define {number} Maximum delta. + */ +ol.interaction.MOUSEWHEELZOOM_MAXDELTA = 1; + + +/** + * @define {number} Timeout duration. + */ +ol.interaction.MOUSEWHEELZOOM_TIMEOUT_DURATION = 80; /** * @constructor * @extends {ol.interaction.Interaction} - * @param {number} delta The zoom delta applied on each mousewheel. */ -ol.interaction.MouseWheelZoom = function(delta) { +ol.interaction.MouseWheelZoom = function() { + + goog.base(this); + /** * @private * @type {number} */ - this.delta_ = delta; + this.delta_ = 0; + + /** + * @private + * @type {?ol.Coordinate} + */ + this.lastAnchor_ = null; + + /** + * @private + * @type {number|undefined} + */ + this.startTime_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.timeoutId_ = undefined; - goog.base(this); }; goog.inherits(ol.interaction.MouseWheelZoom, ol.interaction.Interaction); @@ -37,20 +70,52 @@ goog.inherits(ol.interaction.MouseWheelZoom, ol.interaction.Interaction); */ ol.interaction.MouseWheelZoom.prototype.handleMapBrowserEvent = function(mapBrowserEvent) { + if (mapBrowserEvent.type == goog.events.MouseWheelHandler.EventType.MOUSEWHEEL) { var map = mapBrowserEvent.map; var mouseWheelEvent = /** @type {goog.events.MouseWheelEvent} */ (mapBrowserEvent.browserEvent); goog.asserts.assert(mouseWheelEvent instanceof goog.events.MouseWheelEvent); - var anchor = mapBrowserEvent.getCoordinate(); - var delta = mouseWheelEvent.deltaY < 0 ? this.delta_ : -this.delta_; - // FIXME works for View2D only - var view = map.getView(); - goog.asserts.assert(view instanceof ol.View2D); - map.requestRenderFrame(); - view.zoom(map, delta, anchor, ol.interaction.ZOOM_DURATION); + + this.lastAnchor_ = mapBrowserEvent.getCoordinate(); + this.delta_ += mouseWheelEvent.deltaY / 3; + + if (!goog.isDef(this.startTime_)) { + this.startTime_ = goog.now(); + } + + var duration = ol.interaction.MOUSEWHEELZOOM_TIMEOUT_DURATION; + var timeLeft = Math.max(duration - (goog.now() - this.startTime_), 0); + + goog.global.clearTimeout(this.timeoutId_); + this.timeoutId_ = goog.global.setTimeout( + goog.bind(this.doZoom_, this, map), timeLeft); + mapBrowserEvent.preventDefault(); mouseWheelEvent.preventDefault(); } }; + + +/** + * @private + * @param {ol.Map} map Map. + */ +ol.interaction.MouseWheelZoom.prototype.doZoom_ = function(map) { + var maxDelta = ol.interaction.MOUSEWHEELZOOM_MAXDELTA; + var delta = goog.math.clamp(this.delta_, -maxDelta, maxDelta); + + // FIXME works for View2D only + var view = map.getView(); + goog.asserts.assert(view instanceof ol.View2D); + + map.requestRenderFrame(); + view.zoom(map, -delta, this.lastAnchor_, + ol.interaction.MOUSEWHEELZOOM_ANIMATION_DURATION); + + this.delta_ = 0; + this.lastAnchor_ = null; + this.startTime_ = undefined; + this.timeoutId_ = undefined; +}; diff --git a/src/ol/map.js b/src/ol/map.js index 771122e531..0650f66a76 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -1014,10 +1014,7 @@ ol.Map.createInteractions_ = function(mapOptions) { var mouseWheelZoom = goog.isDef(mapOptions.mouseWheelZoom) ? mapOptions.mouseWheelZoom : true; if (mouseWheelZoom) { - var mouseWheelZoomDelta = - goog.isDef(mapOptions.mouseWheelZoomDelta) ? - mapOptions.mouseWheelZoomDelta : 1; - interactions.push(new ol.interaction.MouseWheelZoom(mouseWheelZoomDelta)); + interactions.push(new ol.interaction.MouseWheelZoom()); } var shiftDragZoom = goog.isDef(mapOptions.shiftDragZoom) ? diff --git a/test/spec/ol/map.test.js b/test/spec/ol/map.test.js index c5e0ff2b30..38e5758437 100644 --- a/test/spec/ol/map.test.js +++ b/test/spec/ol/map.test.js @@ -85,28 +85,11 @@ describe('ol.Map', function() { }); describe('create mousewheel interaction', function() { - - beforeEach(function() { + it('creates mousewheel interaction', function() { options.mouseWheelZoom = true; - }); - - describe('default mouseWheelZoomDelta', function() { - it('create mousewheel interaction with default delta', function() { - var interactions = ol.Map.createInteractions_(options); - expect(interactions.getLength()).toEqual(1); - expect(interactions.getAt(0)).toBeA(ol.interaction.MouseWheelZoom); - expect(interactions.getAt(0).delta_).toEqual(1); - }); - }); - - describe('set mouseWheelZoomDelta', function() { - it('create mousewheel interaction with set delta', function() { - options.mouseWheelZoomDelta = 7; - var interactions = ol.Map.createInteractions_(options); - expect(interactions.getLength()).toEqual(1); - expect(interactions.getAt(0)).toBeA(ol.interaction.MouseWheelZoom); - expect(interactions.getAt(0).delta_).toEqual(7); - }); + var interactions = ol.Map.createInteractions_(options); + expect(interactions.getLength()).toEqual(1); + expect(interactions.getAt(0)).toBeA(ol.interaction.MouseWheelZoom); }); }); @@ -125,7 +108,7 @@ describe('ol.Map', function() { }); }); - describe('set mouseWheelZoomDelta', function() { + describe('set zoomDelta', function() { it('create double click interaction with set delta', function() { options.zoomDelta = 7; var interactions = ol.Map.createInteractions_(options); From 2c551b0445cd9f62be8bb26ba94c633fe44113b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 4 Mar 2013 17:59:43 +0100 Subject: [PATCH 122/180] Change View2D function names Also make the TouchZoom interaction no longer use a private View2D function. --- src/ol/control/zoomcontrol.js | 6 ++- src/ol/interaction/dblclickzoominteraction.js | 2 +- .../dragrotateandzoominteraction.js | 2 +- src/ol/interaction/keyboardzoominteraction.js | 3 +- .../interaction/mousewheelzoominteraction.js | 2 +- src/ol/interaction/touchzoominteraction.js | 4 +- src/ol/view2d.js | 42 +++++++++---------- 7 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/ol/control/zoomcontrol.js b/src/ol/control/zoomcontrol.js index ffacacf55f..63e3345cbf 100644 --- a/src/ol/control/zoomcontrol.js +++ b/src/ol/control/zoomcontrol.js @@ -70,7 +70,8 @@ ol.control.Zoom.prototype.handleIn_ = function(browserEvent) { var map = this.getMap(); map.requestRenderFrame(); // FIXME works for View2D only - map.getView().zoom(map, this.delta_, undefined, ol.control.ZOOM_DURATION); + map.getView().zoomByDelta(map, this.delta_, undefined, + ol.control.ZOOM_DURATION); }; @@ -84,5 +85,6 @@ ol.control.Zoom.prototype.handleOut_ = function(browserEvent) { var map = this.getMap(); map.requestRenderFrame(); // FIXME works for View2D only - map.getView().zoom(map, -this.delta_, undefined, ol.control.ZOOM_DURATION); + map.getView().zoomByDelta(map, -this.delta_, undefined, + ol.control.ZOOM_DURATION); }; diff --git a/src/ol/interaction/dblclickzoominteraction.js b/src/ol/interaction/dblclickzoominteraction.js index b5e3e1e589..c8f63a37c7 100644 --- a/src/ol/interaction/dblclickzoominteraction.js +++ b/src/ol/interaction/dblclickzoominteraction.js @@ -41,7 +41,7 @@ ol.interaction.DblClickZoom.prototype.handleMapBrowserEvent = // FIXME works for View2D only var view = map.getView(); goog.asserts.assert(view instanceof ol.View2D); - view.zoom(map, delta, anchor); + view.zoomByDelta(map, delta, anchor); mapBrowserEvent.preventDefault(); browserEvent.preventDefault(); } diff --git a/src/ol/interaction/dragrotateandzoominteraction.js b/src/ol/interaction/dragrotateandzoominteraction.js index 7cf2f7ffa1..cd8425b37a 100644 --- a/src/ol/interaction/dragrotateandzoominteraction.js +++ b/src/ol/interaction/dragrotateandzoominteraction.js @@ -66,7 +66,7 @@ ol.interaction.DragRotateAndZoom.prototype.handleDrag = this.lastAngle_ = theta; if (goog.isDef(this.lastMagnitude_)) { var resolution = this.lastMagnitude_ * (view.getResolution() / magnitude); - view.zoomToResolution(map, resolution); + view.zoom(map, resolution); } this.lastMagnitude_ = magnitude; }; diff --git a/src/ol/interaction/keyboardzoominteraction.js b/src/ol/interaction/keyboardzoominteraction.js index 563827ab45..cbfe06c51e 100644 --- a/src/ol/interaction/keyboardzoominteraction.js +++ b/src/ol/interaction/keyboardzoominteraction.js @@ -40,7 +40,8 @@ ol.interaction.KeyboardZoom.prototype.handleMapBrowserEvent = // FIXME works for View2D only var view = map.getView(); goog.asserts.assert(view instanceof ol.View2D); - view.zoom(map, delta, undefined, ol.interaction.KEYBOARD_ZOOM_DURATION); + view.zoomByDelta(map, delta, undefined, + ol.interaction.KEYBOARD_ZOOM_DURATION); keyEvent.preventDefault(); mapBrowserEvent.preventDefault(); } diff --git a/src/ol/interaction/mousewheelzoominteraction.js b/src/ol/interaction/mousewheelzoominteraction.js index b274dd7e35..125dba9713 100644 --- a/src/ol/interaction/mousewheelzoominteraction.js +++ b/src/ol/interaction/mousewheelzoominteraction.js @@ -111,7 +111,7 @@ ol.interaction.MouseWheelZoom.prototype.doZoom_ = function(map) { goog.asserts.assert(view instanceof ol.View2D); map.requestRenderFrame(); - view.zoom(map, -delta, this.lastAnchor_, + view.zoomByDelta(map, -delta, this.lastAnchor_, ol.interaction.MOUSEWHEELZOOM_ANIMATION_DURATION); this.delta_ = 0; diff --git a/src/ol/interaction/touchzoominteraction.js b/src/ol/interaction/touchzoominteraction.js index 9087c5f3b4..ed14b714cf 100644 --- a/src/ol/interaction/touchzoominteraction.js +++ b/src/ol/interaction/touchzoominteraction.js @@ -59,7 +59,7 @@ ol.interaction.TouchZoom.prototype.handleTouchMove = var anchor = map.getCoordinateFromPixel(centroid); // scale, bypass the resolution constraint - view.zoom_(map, view.getResolution() * scaleDelta, anchor); + view.zoomNoConstraint(map, view.getResolution() * scaleDelta, anchor); }; @@ -73,7 +73,7 @@ ol.interaction.TouchZoom.prototype.handleTouchEnd = var map = mapBrowserEvent.map; var view = map.getView(); // take the resolution constraint into account - view.zoomToResolution(map, view.getResolution()); + view.zoom(map, view.getResolution()); view.setHint(ol.ViewHint.INTERACTING, -1); return false; } else { diff --git a/src/ol/view2d.js b/src/ol/view2d.js index 2fcc1f9016..0f3d3474c9 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -282,26 +282,13 @@ ol.View2D.prototype.rotate = function(map, rotation, opt_anchor) { /** - * @private * @param {ol.Map} map Map. * @param {number|undefined} resolution Resolution to go to. * @param {ol.Coordinate=} opt_anchor Anchor coordinate. */ -ol.View2D.prototype.zoom_ = function(map, resolution, opt_anchor) { - if (goog.isDefAndNotNull(resolution) && goog.isDefAndNotNull(opt_anchor)) { - var anchor = opt_anchor; - var oldCenter = /** @type {!ol.Coordinate} */ (this.getCenter()); - var oldResolution = this.getResolution(); - var x = anchor.x - resolution * (anchor.x - oldCenter.x) / oldResolution; - var y = anchor.y - resolution * (anchor.y - oldCenter.y) / oldResolution; - var center = new ol.Coordinate(x, y); - map.withFrozenRendering(function() { - this.setCenter(center); - this.setResolution(resolution); - }, this); - } else { - this.setResolution(resolution); - } +ol.View2D.prototype.zoom = function(map, resolution, opt_anchor) { + resolution = this.constraints_.resolution(resolution, 0); + this.zoomNoConstraint(map, resolution, opt_anchor); }; @@ -311,7 +298,8 @@ ol.View2D.prototype.zoom_ = function(map, resolution, opt_anchor) { * @param {ol.Coordinate=} opt_anchor Anchor coordinate. * @param {number=} opt_duration Duration. */ -ol.View2D.prototype.zoom = function(map, delta, opt_anchor, opt_duration) { +ol.View2D.prototype.zoomByDelta = + function(map, delta, opt_anchor, opt_duration) { var currentResolution = this.getResolution(); var currentCenter = this.getCenter(); if (goog.isDef(currentResolution) && goog.isDef(currentCenter) && @@ -331,7 +319,7 @@ ol.View2D.prototype.zoom = function(map, delta, opt_anchor, opt_duration) { } } var resolution = this.constraints_.resolution(currentResolution, delta); - this.zoom_(map, resolution, opt_anchor); + this.zoomNoConstraint(map, resolution, opt_anchor); }; @@ -340,9 +328,21 @@ ol.View2D.prototype.zoom = function(map, delta, opt_anchor, opt_duration) { * @param {number|undefined} resolution Resolution to go to. * @param {ol.Coordinate=} opt_anchor Anchor coordinate. */ -ol.View2D.prototype.zoomToResolution = function(map, resolution, opt_anchor) { - resolution = this.constraints_.resolution(resolution, 0); - this.zoom_(map, resolution, opt_anchor); +ol.View2D.prototype.zoomNoConstraint = function(map, resolution, opt_anchor) { + if (goog.isDefAndNotNull(resolution) && goog.isDefAndNotNull(opt_anchor)) { + var anchor = opt_anchor; + var oldCenter = /** @type {!ol.Coordinate} */ (this.getCenter()); + var oldResolution = this.getResolution(); + var x = anchor.x - resolution * (anchor.x - oldCenter.x) / oldResolution; + var y = anchor.y - resolution * (anchor.y - oldCenter.y) / oldResolution; + var center = new ol.Coordinate(x, y); + map.withFrozenRendering(function() { + this.setCenter(center); + this.setResolution(resolution); + }, this); + } else { + this.setResolution(resolution); + } }; From 303520a5e0b89a1ece3061e03161a9c65673f3ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 4 Mar 2013 18:07:45 +0100 Subject: [PATCH 123/180] Add a rotateNoConstraint View2D method --- src/ol/view2d.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ol/view2d.js b/src/ol/view2d.js index 0f3d3474c9..8772f363ca 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -262,6 +262,16 @@ goog.exportProperty( */ ol.View2D.prototype.rotate = function(map, rotation, opt_anchor) { rotation = this.constraints_.rotation(rotation, 0); + this.rotateNoConstraint(map, rotation, opt_anchor); +}; + + +/** + * @param {ol.Map} map Map. + * @param {number|undefined} rotation Rotation. + * @param {ol.Coordinate=} opt_anchor Anchor coordinate. + */ +ol.View2D.prototype.rotateNoConstraint = function(map, rotation, opt_anchor) { if (goog.isDefAndNotNull(opt_anchor)) { var anchor = opt_anchor; var oldCenter = /** @type {!ol.Coordinate} */ (this.getCenter()); From cae2340343d9f0bc0facd6f21e837b5bd96a9956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 4 Mar 2013 18:07:56 +0100 Subject: [PATCH 124/180] Make TouchRotate interaction use rotateNoConstraint --- src/ol/interaction/touchrotateinteraction.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ol/interaction/touchrotateinteraction.js b/src/ol/interaction/touchrotateinteraction.js index 1ce5d02a77..be084f7db0 100644 --- a/src/ol/interaction/touchrotateinteraction.js +++ b/src/ol/interaction/touchrotateinteraction.js @@ -90,7 +90,7 @@ ol.interaction.TouchRotate.prototype.handleTouchMove = // rotate if (this.rotating_) { - view.rotate(map, view.getRotation() + rotationDelta, anchor); + view.rotateNoConstraint(map, view.getRotation() + rotationDelta, anchor); } }; @@ -103,6 +103,9 @@ ol.interaction.TouchRotate.prototype.handleTouchEnd = if (this.targetTouches.length < 2) { var map = mapBrowserEvent.map; var view = map.getView(); + if (this.rotating_) { + view.rotate(map, view.getRotation()); + } view.setHint(ol.ViewHint.INTERACTING, -1); return false; } else { From 78780fa09538c5e4103edfc965f8e601519d5408 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 5 Mar 2013 00:23:09 +0100 Subject: [PATCH 125/180] Correct Stamen source --- src/objectliterals.exports | 7 +- src/ol/source/stamen.exports | 20 ------ src/ol/source/stamensource.js | 132 ++++++++++++++++++++-------------- 3 files changed, 82 insertions(+), 77 deletions(-) diff --git a/src/objectliterals.exports b/src/objectliterals.exports index fc82542a8b..e082c7e264 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -118,8 +118,11 @@ @exportObjectLiteralProperty ol.source.SourceOptions.projection ol.Projection|undefined @exportObjectLiteral ol.source.StamenOptions -@exportObjectLiteralProperty ol.source.StamenOptions.flavor string|undefined -@exportObjectLiteralProperty ol.source.StamenOptions.provider string +@exportObjectLiteralProperty ol.source.StamenOptions.layer string +@exportObjectLiteralProperty ol.source.StamenOptions.minZoom number|undefined +@exportObjectLiteralProperty ol.source.StamenOptions.maxZoom number|undefined +@exportObjectLiteralProperty ol.source.StamenOptions.opaque boolean|undefined +@exportObjectLiteralProperty ol.source.StamenOptions.url string|undefined @exportObjectLiteral ol.source.StaticImageOptions @exportObjectLiteralProperty ol.source.StaticImageOptions.attributions Array.|undefined diff --git a/src/ol/source/stamen.exports b/src/ol/source/stamen.exports index 40b2c8b0e7..697330485e 100644 --- a/src/ol/source/stamen.exports +++ b/src/ol/source/stamen.exports @@ -1,21 +1 @@ @exportSymbol ol.source.Stamen - -@exportSymbol ol.source.StamenFlavor -@exportProperty ol.source.StamenFlavor.TERRAIN_BACKGROUND -@exportProperty ol.source.StamenFlavor.TERRAIN_LABELS -@exportProperty ol.source.StamenFlavor.TERRAIN_LINES -@exportProperty ol.source.StamenFlavor.TONER_2010 -@exportProperty ol.source.StamenFlavor.TONER_2011 -@exportProperty ol.source.StamenFlavor.TONER_2011_LABELS -@exportProperty ol.source.StamenFlavor.TONER_2011_LINES -@exportProperty ol.source.StamenFlavor.TONER_2011_LITE -@exportProperty ol.source.StamenFlavor.TONER_BACKGROUND -@exportProperty ol.source.StamenFlavor.TONER_HYBRID -@exportProperty ol.source.StamenFlavor.TONER_LABELS -@exportProperty ol.source.StamenFlavor.TONER_LINES -@exportProperty ol.source.StamenFlavor.TONER_LITE - -@exportSymbol ol.source.StamenProvider -@exportProperty ol.source.StamenProvider.TERRAIN -@exportProperty ol.source.StamenProvider.TONER -@exportProperty ol.source.StamenProvider.WATERCOLOR diff --git a/src/ol/source/stamensource.js b/src/ol/source/stamensource.js index 90355bf1e5..6ea87b6ab4 100644 --- a/src/ol/source/stamensource.js +++ b/src/ol/source/stamensource.js @@ -1,61 +1,76 @@ -// FIXME Configure minZoom when supported by TileGrid - goog.provide('ol.source.Stamen'); -goog.provide('ol.source.StamenFlavor'); -goog.provide('ol.source.StamenProvider'); goog.require('ol.Attribution'); goog.require('ol.source.XYZ'); /** - * @enum {string} + * @type {Object.} */ -ol.source.StamenFlavor = { - TERRAIN_BACKGROUND: 'background', - TERRAIN_LABELS: 'labels', - TERRAIN_LINES: 'lines', - TONER_2010: '2010', - TONER_2011: '2011', - TONER_2011_LABELS: '2011-labels', - TONER_2011_LINES: '2011-lines', - TONER_2011_LITE: '2011-lite', - TONER_BACKGROUND: 'background', - TONER_HYBRID: 'hybrid', - TONER_LABELS: 'labels', - TONER_LINES: 'lines', - TONER_LITE: 'lite' +ol.source.StamenLayerConfig = { + 'terrain': { + extension: 'jpg', + opaque: true + }, + 'terrain-background': { + extension: 'jpg', + opaque: true + }, + 'terrain-labels': { + extension: 'png', + opaque: false + }, + 'terrain-lines': { + extension: 'png', + opaque: false + }, + 'toner-background': { + extension: 'png', + opaque: true + }, + 'toner': { + extension: 'png', + opaque: true + }, + 'toner-hybrid': { + extension: 'png', + opaque: false + }, + 'toner-labels': { + extension: 'png', + opaque: false + }, + 'toner-lines': { + extension: 'png', + opaque: false + }, + 'toner-lite': { + extension: 'png', + opaque: true + }, + 'watercolor': { + extension: 'jpg', + opaque: true + } }; /** - * @enum {string} + * @type {Object.} */ -ol.source.StamenProvider = { - TERRAIN: 'terrain', - TONER: 'toner', - WATERCOLOR: 'watercolor' -}; - - -/** - * @type {Object.} - */ -ol.source.StamenProviderConfig = {}; -ol.source.StamenProviderConfig[ol.source.StamenProvider.TERRAIN] = { - type: 'jpg', - minZoom: 4, - maxZoom: 18 -}; -ol.source.StamenProviderConfig[ol.source.StamenProvider.TONER] = { - type: 'png', - minZoom: 0, - maxZoom: 20 -}; -ol.source.StamenProviderConfig[ol.source.StamenProvider.WATERCOLOR] = { - type: 'jpg', - minZoom: 3, - maxZoom: 16 +ol.source.StamenProviderConfig = { + 'terrain': { + minZoom: 4, + maxZoom: 18 + }, + 'toner': { + minZoom: 0, + maxZoom: 20 + }, + 'watercolor': { + minZoom: 3, + maxZoom: 16 + } }; @@ -63,9 +78,9 @@ ol.source.StamenProviderConfig[ol.source.StamenProvider.WATERCOLOR] = { /** * @constructor * @extends {ol.source.XYZ} - * @param {ol.source.StamenOptions} stamenOptions Stamen options. + * @param {ol.source.StamenOptions} options Options. */ -ol.source.Stamen = function(stamenOptions) { +ol.source.Stamen = function(options) { var attribution = new ol.Attribution( 'Map tiles by Stamen Design, ' + @@ -75,18 +90,25 @@ ol.source.Stamen = function(stamenOptions) { 'under ' + 'CC BY SA.'); - var layer = stamenOptions.provider; - if (goog.isDef(stamenOptions.flavor)) { - layer += '-' + stamenOptions.flavor; - } + var i = options.layer.indexOf('-'); + var provider = i == -1 ? options.layer : options.layer.slice(0, i); + goog.asserts.assert(provider in ol.source.StamenProviderConfig); + var providerConfig = ol.source.StamenProviderConfig[provider]; - var config = ol.source.StamenProviderConfig[stamenOptions.provider]; + goog.asserts.assert(options.layer in ol.source.StamenLayerConfig); + var layerConfig = ol.source.StamenLayerConfig[options.layer]; + + var url = goog.isDef(options.url) ? options.url : + 'http://{a-d}.tile.stamen.com/' + options.layer + '/{z}/{x}/{y}.' + + layerConfig.extension; goog.base(this, { attributions: [attribution], - maxZoom: config.maxZoom, - opaque: false, - url: 'http://{a-d}.tile.stamen.com/' + layer + '/{z}/{x}/{y}.' + config.type + maxZoom: providerConfig.maxZoom, + // FIXME uncomment the following when tilegrid supports minZoom + //minZoom: providerConfig.minZoom, + opaque: layerConfig.opaque, + url: url }); }; From 83225916c1f0021702a44fa4a612bf375cee4e42 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 5 Mar 2013 00:41:46 +0100 Subject: [PATCH 126/180] Add Stamen example --- examples/stamen.html | 42 ++++++++++++++++++++++++++++++++++++++++++ examples/stamen.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 examples/stamen.html create mode 100644 examples/stamen.js diff --git a/examples/stamen.html b/examples/stamen.html new file mode 100644 index 0000000000..0b0f9ef71d --- /dev/null +++ b/examples/stamen.html @@ -0,0 +1,42 @@ + + + + + + + + + Stamen example + + +
+
+

Stamen example

+
Example of a Stamen tile source.
+
+

See the + stamen.js source + to see how this is done.

+
+
+
+
fullscreen, stamen, tilelayer
+ + + diff --git a/examples/stamen.js b/examples/stamen.js new file mode 100644 index 0000000000..3c947baf84 --- /dev/null +++ b/examples/stamen.js @@ -0,0 +1,31 @@ +goog.require('ol.Collection'); +goog.require('ol.Coordinate'); +goog.require('ol.Map'); +goog.require('ol.RendererHints'); +goog.require('ol.View2D'); +goog.require('ol.layer.TileLayer'); +goog.require('ol.source.Stamen'); + + +var layers = new ol.Collection([ + new ol.layer.TileLayer({ + source: new ol.source.Stamen({ + layer: 'watercolor' + }) + }), + new ol.layer.TileLayer({ + source: new ol.source.Stamen({ + layer: 'terrain-labels' + }) + }) +]); +var map = new ol.Map({ + layers: layers, + renderers: ol.RendererHints.createFromQueryData(), + scaleLineControl: true, + target: 'map', + view: new ol.View2D({ + center: new ol.Coordinate(0, 0), + zoom: 3 + }) +}); From f20cf83c9467c3dd890d699c51e5dab4452ce65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 4 Mar 2013 19:21:41 +0100 Subject: [PATCH 127/180] Add a snapToZero rotation constraint --- src/ol/rotationconstraint.js | 20 ++++++++++++++ src/ol/view2d.js | 2 +- test/spec/ol/rotationconstraint.test.js | 36 +++++++++++++++++++++++++ test/spec/ol/view2d.test.js | 10 +++++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 test/spec/ol/rotationconstraint.test.js diff --git a/src/ol/rotationconstraint.js b/src/ol/rotationconstraint.js index 72500f9ec7..c297e13608 100644 --- a/src/ol/rotationconstraint.js +++ b/src/ol/rotationconstraint.js @@ -37,3 +37,23 @@ ol.RotationConstraint.createSnapToN = function(n) { } }; }; + + +/** + * @param {number=} opt_tolerance Tolerance. + * @return {ol.RotationConstraintType} Rotation constraint. + */ +ol.RotationConstraint.createSnapToZero = function(opt_tolerance) { + var tolerance = opt_tolerance || 0.1; + return function(rotation, delta) { + if (goog.isDef(rotation)) { + if (Math.abs(rotation + delta) <= tolerance) { + return 0; + } else { + return rotation + delta; + } + } else { + return undefined; + } + }; +}; diff --git a/src/ol/view2d.js b/src/ol/view2d.js index 8772f363ca..77062aad97 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -387,6 +387,6 @@ ol.View2D.createConstraints_ = function(view2DOptions) { zoomFactor, maxResolution, numZoomLevels - 1); } // FIXME rotation constraint is not configurable at the moment - var rotationConstraint = ol.RotationConstraint.none; + var rotationConstraint = ol.RotationConstraint.createSnapToZero(); return new ol.Constraints(resolutionConstraint, rotationConstraint); }; diff --git a/test/spec/ol/rotationconstraint.test.js b/test/spec/ol/rotationconstraint.test.js new file mode 100644 index 0000000000..950a8892e4 --- /dev/null +++ b/test/spec/ol/rotationconstraint.test.js @@ -0,0 +1,36 @@ +goog.provide('ol.test.RotationConstraint'); + +describe('ol.RotationConstraint', function() { + + describe('SnapToZero', function() { + + it('returns expected rotation value', function() { + var rotationConstraint = ol.RotationConstraint.createSnapToZero(0.3); + + expect(rotationConstraint(0.1, 0)).toEqual(0); + expect(rotationConstraint(0.2, 0)).toEqual(0); + expect(rotationConstraint(0.3, 0)).toEqual(0); + expect(rotationConstraint(0.4, 0)).toEqual(0.4); + + expect(rotationConstraint(-0.1, 0)).toEqual(0); + expect(rotationConstraint(-0.2, 0)).toEqual(0); + expect(rotationConstraint(-0.3, 0)).toEqual(0); + expect(rotationConstraint(-0.4, 0)).toEqual(-0.4); + + expect(rotationConstraint(1, -0.9)).toEqual(0); + expect(rotationConstraint(1, -0.8)).toEqual(0); + // floating-point arithmetic + expect(rotationConstraint(1, -0.7)).not.toEqual(0); + expect(rotationConstraint(1, -0.6)).toEqual(0.4); + + expect(rotationConstraint(-1, 0.9)).toEqual(0); + expect(rotationConstraint(-1, 0.8)).toEqual(0); + // floating-point arithmetic + expect(rotationConstraint(-1, 0.7)).not.toEqual(0); + expect(rotationConstraint(-1, 0.6)).toEqual(-0.4); + }); + + }); +}); + +goog.require('ol.RotationConstraint'); diff --git a/test/spec/ol/view2d.test.js b/test/spec/ol/view2d.test.js index d78e17dad4..efed46163b 100644 --- a/test/spec/ol/view2d.test.js +++ b/test/spec/ol/view2d.test.js @@ -49,6 +49,16 @@ describe('ol.View2D', function() { }); }); + + describe('create rotation constraint', function() { + it('gives a correct rotation constraint function', function() { + var options = {}; + var fn = ol.View2D.createConstraints_(options).rotation; + expect(fn(0.01, 0)).toEqual(0); + expect(fn(0.15, 0)).toEqual(0.15); + }); + }); + }); }); From de8db176ab14c2feb08a6fd1966ba1335bbf1c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 4 Mar 2013 20:04:51 +0100 Subject: [PATCH 128/180] Do not apply rotation constraint while rotating This is on par to what the TouchRotate interaction does. --- src/ol/interaction/dragrotateinteraction.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/ol/interaction/dragrotateinteraction.js b/src/ol/interaction/dragrotateinteraction.js index 276fb15c56..90b92c0be5 100644 --- a/src/ol/interaction/dragrotateinteraction.js +++ b/src/ol/interaction/dragrotateinteraction.js @@ -1,6 +1,7 @@ goog.provide('ol.interaction.DragRotate'); goog.require('ol.View2D'); +goog.require('ol.ViewHint'); goog.require('ol.interaction.ConditionType'); goog.require('ol.interaction.Drag'); @@ -46,12 +47,26 @@ ol.interaction.DragRotate.prototype.handleDrag = function(mapBrowserEvent) { // FIXME supports View2D only goog.asserts.assert(view instanceof ol.View2D); map.requestRenderFrame(); - view.rotate(map, view.getRotation() - delta); + view.rotateNoConstraint(map, view.getRotation() - delta); } this.lastAngle_ = theta; }; +/** + * @inheritDoc + */ +ol.interaction.DragRotate.prototype.handleDragEnd = function(mapBrowserEvent) { + var browserEvent = mapBrowserEvent.browserEvent; + var map = mapBrowserEvent.map; + // FIXME supports View2D only + var view = map.getView(); + goog.asserts.assert(view instanceof ol.View2D); + view.rotate(map, view.getRotation()); + view.setHint(ol.ViewHint.INTERACTING, -1); +}; + + /** * @inheritDoc */ @@ -65,6 +80,7 @@ ol.interaction.DragRotate.prototype.handleDragStart = goog.asserts.assert(view instanceof ol.View2D); map.requestRenderFrame(); this.lastAngle_ = undefined; + view.setHint(ol.ViewHint.INTERACTING, 1); return true; } else { return false; From 33eb13d73b80d630e371efbe78ba99d90a9d0f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 4 Mar 2013 20:29:18 +0100 Subject: [PATCH 129/180] Make animating rotation possible --- src/ol/view2d.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/ol/view2d.js b/src/ol/view2d.js index 77062aad97..4662678d3b 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -259,8 +259,28 @@ goog.exportProperty( * @param {ol.Map} map Map. * @param {number|undefined} rotation Rotation. * @param {ol.Coordinate=} opt_anchor Anchor coordinate. + * @param {number=} opt_duration Duration. */ -ol.View2D.prototype.rotate = function(map, rotation, opt_anchor) { +ol.View2D.prototype.rotate = + function(map, rotation, opt_anchor, opt_duration) { + var currentRotation = this.getRotation(); + var currentCenter = this.getCenter(); + if (goog.isDef(currentRotation) && goog.isDef(currentCenter) && + goog.isDef(opt_duration)) { + map.requestRenderFrame(); + map.addPreRenderFunction(ol.animation.rotate({ + rotation: currentRotation, + duration: opt_duration, + easing: goog.fx.easing.easeOut + })); + if (goog.isDef(opt_anchor)) { + map.addPreRenderFunction(ol.animation.pan({ + source: currentCenter, + duration: opt_duration, + easing: goog.fx.easing.easeOut + })); + } + } rotation = this.constraints_.rotation(rotation, 0); this.rotateNoConstraint(map, rotation, opt_anchor); }; From b9a286fb7ad9895e1c70726e61e1574df6f4aa09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 4 Mar 2013 20:30:15 +0100 Subject: [PATCH 130/180] Animate rotation when releasing mouse or fingers --- src/ol/interaction/dragrotateinteraction.js | 9 ++++++++- src/ol/interaction/touchrotateinteraction.js | 11 ++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ol/interaction/dragrotateinteraction.js b/src/ol/interaction/dragrotateinteraction.js index 90b92c0be5..d4036be9c1 100644 --- a/src/ol/interaction/dragrotateinteraction.js +++ b/src/ol/interaction/dragrotateinteraction.js @@ -6,6 +6,12 @@ goog.require('ol.interaction.ConditionType'); goog.require('ol.interaction.Drag'); +/** + * @define {number} Animation duration. + */ +ol.interaction.DRAGROTATE_ANIMATION_DURATION = 250; + + /** * @constructor @@ -62,7 +68,8 @@ ol.interaction.DragRotate.prototype.handleDragEnd = function(mapBrowserEvent) { // FIXME supports View2D only var view = map.getView(); goog.asserts.assert(view instanceof ol.View2D); - view.rotate(map, view.getRotation()); + view.rotate(map, view.getRotation(), undefined, + ol.interaction.DRAGROTATE_ANIMATION_DURATION); view.setHint(ol.ViewHint.INTERACTING, -1); }; diff --git a/src/ol/interaction/touchrotateinteraction.js b/src/ol/interaction/touchrotateinteraction.js index be084f7db0..a3a6e14dfe 100644 --- a/src/ol/interaction/touchrotateinteraction.js +++ b/src/ol/interaction/touchrotateinteraction.js @@ -8,6 +8,14 @@ goog.require('ol.ViewHint'); goog.require('ol.interaction.Touch'); +/** + * @define {number} Animation duration. + */ +ol.interaction.TOUCHROTATE_ANIMATION_DURATION = 250; + + + +/** /** * @constructor @@ -104,7 +112,8 @@ ol.interaction.TouchRotate.prototype.handleTouchEnd = var map = mapBrowserEvent.map; var view = map.getView(); if (this.rotating_) { - view.rotate(map, view.getRotation()); + view.rotate(map, view.getRotation(), undefined, + ol.interaction.TOUCHROTATE_ANIMATION_DURATION); } view.setHint(ol.ViewHint.INTERACTING, -1); return false; From a4c47edd9d78025ae77d2181162c6dda2d5a11b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 4 Mar 2013 20:51:45 +0100 Subject: [PATCH 131/180] 2DView refactoring This refactoring makes animating zoom, as opposed to zoomByDelta, possible. --- src/ol/view2d.js | 145 +++++++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 67 deletions(-) diff --git a/src/ol/view2d.js b/src/ol/view2d.js index 4662678d3b..f022a1bf25 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -263,26 +263,8 @@ goog.exportProperty( */ ol.View2D.prototype.rotate = function(map, rotation, opt_anchor, opt_duration) { - var currentRotation = this.getRotation(); - var currentCenter = this.getCenter(); - if (goog.isDef(currentRotation) && goog.isDef(currentCenter) && - goog.isDef(opt_duration)) { - map.requestRenderFrame(); - map.addPreRenderFunction(ol.animation.rotate({ - rotation: currentRotation, - duration: opt_duration, - easing: goog.fx.easing.easeOut - })); - if (goog.isDef(opt_anchor)) { - map.addPreRenderFunction(ol.animation.pan({ - source: currentCenter, - duration: opt_duration, - easing: goog.fx.easing.easeOut - })); - } - } rotation = this.constraints_.rotation(rotation, 0); - this.rotateNoConstraint(map, rotation, opt_anchor); + this.rotateNoConstraint(map, rotation, opt_anchor, opt_duration); }; @@ -290,23 +272,45 @@ ol.View2D.prototype.rotate = * @param {ol.Map} map Map. * @param {number|undefined} rotation Rotation. * @param {ol.Coordinate=} opt_anchor Anchor coordinate. + * @param {number=} opt_duration Duration. */ -ol.View2D.prototype.rotateNoConstraint = function(map, rotation, opt_anchor) { - if (goog.isDefAndNotNull(opt_anchor)) { - var anchor = opt_anchor; - var oldCenter = /** @type {!ol.Coordinate} */ (this.getCenter()); - var center = new ol.Coordinate( - oldCenter.x - anchor.x, - oldCenter.y - anchor.y); - center.rotate(rotation - this.getRotation()); - center.x += anchor.x; - center.y += anchor.y; - map.withFrozenRendering(function() { - this.setCenter(center); +ol.View2D.prototype.rotateNoConstraint = + function(map, rotation, opt_anchor, opt_duration) { + if (goog.isDefAndNotNull(rotation)) { + var currentRotation = this.getRotation(); + var currentCenter = this.getCenter(); + if (goog.isDef(currentRotation) && goog.isDef(currentCenter) && + goog.isDef(opt_duration)) { + map.requestRenderFrame(); + map.addPreRenderFunction(ol.animation.rotate({ + rotation: currentRotation, + duration: opt_duration, + easing: goog.fx.easing.easeOut + })); + if (goog.isDef(opt_anchor)) { + map.addPreRenderFunction(ol.animation.pan({ + source: currentCenter, + duration: opt_duration, + easing: goog.fx.easing.easeOut + })); + } + } + if (goog.isDefAndNotNull(opt_anchor)) { + var anchor = opt_anchor; + var oldCenter = /** @type {!ol.Coordinate} */ (this.getCenter()); + var center = new ol.Coordinate( + oldCenter.x - anchor.x, + oldCenter.y - anchor.y); + center.rotate(rotation - this.getRotation()); + center.x += anchor.x; + center.y += anchor.y; + map.withFrozenRendering(function() { + this.setCenter(center); + this.setRotation(rotation); + }, this); + } else { this.setRotation(rotation); - }, this); - } else { - this.setRotation(rotation); + } } }; @@ -315,10 +319,12 @@ ol.View2D.prototype.rotateNoConstraint = function(map, rotation, opt_anchor) { * @param {ol.Map} map Map. * @param {number|undefined} resolution Resolution to go to. * @param {ol.Coordinate=} opt_anchor Anchor coordinate. + * @param {number=} opt_duration Duration. */ -ol.View2D.prototype.zoom = function(map, resolution, opt_anchor) { +ol.View2D.prototype.zoom = + function(map, resolution, opt_anchor, opt_duration) { resolution = this.constraints_.resolution(resolution, 0); - this.zoomNoConstraint(map, resolution, opt_anchor); + this.zoomNoConstraint(map, resolution, opt_anchor, opt_duration); }; @@ -331,25 +337,8 @@ ol.View2D.prototype.zoom = function(map, resolution, opt_anchor) { ol.View2D.prototype.zoomByDelta = function(map, delta, opt_anchor, opt_duration) { var currentResolution = this.getResolution(); - var currentCenter = this.getCenter(); - if (goog.isDef(currentResolution) && goog.isDef(currentCenter) && - goog.isDef(opt_duration)) { - map.requestRenderFrame(); - map.addPreRenderFunction(ol.animation.zoom({ - resolution: currentResolution, - duration: opt_duration, - easing: goog.fx.easing.easeOut - })); - if (goog.isDef(opt_anchor)) { - map.addPreRenderFunction(ol.animation.pan({ - source: currentCenter, - duration: opt_duration, - easing: goog.fx.easing.easeOut - })); - } - } var resolution = this.constraints_.resolution(currentResolution, delta); - this.zoomNoConstraint(map, resolution, opt_anchor); + this.zoomNoConstraint(map, resolution, opt_anchor, opt_duration); }; @@ -357,21 +346,43 @@ ol.View2D.prototype.zoomByDelta = * @param {ol.Map} map Map. * @param {number|undefined} resolution Resolution to go to. * @param {ol.Coordinate=} opt_anchor Anchor coordinate. + * @param {number=} opt_duration Duration. */ -ol.View2D.prototype.zoomNoConstraint = function(map, resolution, opt_anchor) { - if (goog.isDefAndNotNull(resolution) && goog.isDefAndNotNull(opt_anchor)) { - var anchor = opt_anchor; - var oldCenter = /** @type {!ol.Coordinate} */ (this.getCenter()); - var oldResolution = this.getResolution(); - var x = anchor.x - resolution * (anchor.x - oldCenter.x) / oldResolution; - var y = anchor.y - resolution * (anchor.y - oldCenter.y) / oldResolution; - var center = new ol.Coordinate(x, y); - map.withFrozenRendering(function() { - this.setCenter(center); +ol.View2D.prototype.zoomNoConstraint = + function(map, resolution, opt_anchor, opt_duration) { + if (goog.isDefAndNotNull(resolution)) { + var currentResolution = this.getResolution(); + var currentCenter = this.getCenter(); + if (goog.isDef(currentResolution) && goog.isDef(currentCenter) && + goog.isDef(opt_duration)) { + map.requestRenderFrame(); + map.addPreRenderFunction(ol.animation.zoom({ + resolution: currentResolution, + duration: opt_duration, + easing: goog.fx.easing.easeOut + })); + if (goog.isDef(opt_anchor)) { + map.addPreRenderFunction(ol.animation.pan({ + source: currentCenter, + duration: opt_duration, + easing: goog.fx.easing.easeOut + })); + } + } + if (goog.isDefAndNotNull(opt_anchor)) { + var anchor = opt_anchor; + var oldCenter = /** @type {!ol.Coordinate} */ (this.getCenter()); + var oldResolution = this.getResolution(); + var x = anchor.x - resolution * (anchor.x - oldCenter.x) / oldResolution; + var y = anchor.y - resolution * (anchor.y - oldCenter.y) / oldResolution; + var center = new ol.Coordinate(x, y); + map.withFrozenRendering(function() { + this.setCenter(center); + this.setResolution(resolution); + }, this); + } else { this.setResolution(resolution); - }, this); - } else { - this.setResolution(resolution); + } } }; From 066a81a88a787507b7f0829cd7da196b8bf81707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 4 Mar 2013 20:59:33 +0100 Subject: [PATCH 132/180] Animate zoom after pinch --- src/ol/interaction/touchzoominteraction.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ol/interaction/touchzoominteraction.js b/src/ol/interaction/touchzoominteraction.js index ed14b714cf..76d99bafd7 100644 --- a/src/ol/interaction/touchzoominteraction.js +++ b/src/ol/interaction/touchzoominteraction.js @@ -8,6 +8,12 @@ goog.require('ol.ViewHint'); goog.require('ol.interaction.Touch'); +/** + * @define {number} Animation duration. + */ +ol.interaction.TOUCHZOOM_ANIMATION_DURATION = 250; + + /** * @constructor @@ -73,7 +79,8 @@ ol.interaction.TouchZoom.prototype.handleTouchEnd = var map = mapBrowserEvent.map; var view = map.getView(); // take the resolution constraint into account - view.zoom(map, view.getResolution()); + view.zoom(map, view.getResolution(), undefined, + ol.interaction.TOUCHZOOM_ANIMATION_DURATION); view.setHint(ol.ViewHint.INTERACTING, -1); return false; } else { From 5644c27d9e4043fc10980d712379da80ca3e6f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 5 Mar 2013 10:07:33 +0100 Subject: [PATCH 133/180] Animate double click/tap zoom --- src/ol/interaction/dblclickzoominteraction.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ol/interaction/dblclickzoominteraction.js b/src/ol/interaction/dblclickzoominteraction.js index c8f63a37c7..e758e67d44 100644 --- a/src/ol/interaction/dblclickzoominteraction.js +++ b/src/ol/interaction/dblclickzoominteraction.js @@ -8,6 +8,12 @@ goog.require('ol.View2D'); goog.require('ol.interaction.Interaction'); +/** + * @define {number} Animation duration. + */ +ol.interaction.DBLCLICKZOOM_ANIMATION_DURATION = 250; + + /** * @constructor @@ -41,7 +47,8 @@ ol.interaction.DblClickZoom.prototype.handleMapBrowserEvent = // FIXME works for View2D only var view = map.getView(); goog.asserts.assert(view instanceof ol.View2D); - view.zoomByDelta(map, delta, anchor); + view.zoomByDelta(map, delta, anchor, + ol.interaction.DBLCLICKZOOM_ANIMATION_DURATION); mapBrowserEvent.preventDefault(); browserEvent.preventDefault(); } From a912d48b597cc1d54b0b902193a80e5635b98fdf Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 5 Mar 2013 10:01:27 +0100 Subject: [PATCH 134/180] Do not use tiledWMSOptions in closure function See #269. --- src/ol/source/tiledwmssource.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ol/source/tiledwmssource.js b/src/ol/source/tiledwmssource.js index ebc8e7ab8c..935fac2554 100644 --- a/src/ol/source/tiledwmssource.js +++ b/src/ol/source/tiledwmssource.js @@ -49,8 +49,7 @@ ol.source.TiledWMS = function(tiledWMSOptions) { var x = tileCoord.x; var tileExtent = tileGrid.getTileCoordExtent(tileCoord); var projectionExtent = projection.getExtent(); - var extent = goog.isDef(tiledWMSOptions.extent) ? - tiledWMSOptions.extent : projectionExtent; + extent = goog.isDef(extent) ? extent : projectionExtent; // FIXME do we want a wrapDateLine param? The code below will break maps // with projections that do not span the whole world width. if (extent.minX === projectionExtent.minX && From c1f3a6cc5461054d7d6dd7a67b32e413ca58e213 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 5 Mar 2013 10:54:33 +0100 Subject: [PATCH 135/180] WMS GetMap request parameters shall be params To avoid surprises, we configure everything that is a WMS GetMap request parameter in the params object, and not as direct configuration option. This affects the VERSION and TRANSPARENT params. --- src/objectliterals.exports | 3 --- src/ol/imageurlfunction.js | 5 ++--- src/ol/source/singleimagewmssource.js | 3 +-- src/ol/source/tiledwmssource.js | 9 ++++----- src/ol/source/wms.js | 26 ++++++++++++++------------ src/ol/tileurlfunction.js | 5 ++--- test/spec/ol/tileurlfunction.test.js | 16 ++++++++-------- 7 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/objectliterals.exports b/src/objectliterals.exports index e082c7e264..8df22dc74e 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -110,7 +110,6 @@ @exportObjectLiteralProperty ol.source.SingleImageWMSOptions.projection ol.Projection|undefined @exportObjectLiteralProperty ol.source.SingleImageWMSOptions.resolutions Array.|undefined @exportObjectLiteralProperty ol.source.SingleImageWMSOptions.url string|undefined -@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.version string|undefined @exportObjectLiteral ol.source.SourceOptions @exportObjectLiteralProperty ol.source.SourceOptions.attributions Array.|undefined @@ -136,11 +135,9 @@ @exportObjectLiteral ol.source.TiledWMSOptions @exportObjectLiteralProperty ol.source.TiledWMSOptions.attributions Array.|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.params Object -@exportObjectLiteralProperty ol.source.TiledWMSOptions.version string|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.crossOrigin null|string|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.extent ol.Extent|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.tileGrid ol.tilegrid.TileGrid|undefined -@exportObjectLiteralProperty ol.source.TiledWMSOptions.transparent boolean|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.maxZoom number|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.projection ol.Projection|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.url string|undefined diff --git a/src/ol/imageurlfunction.js b/src/ol/imageurlfunction.js index 4af4b0a5d2..5bd6ec96a7 100644 --- a/src/ol/imageurlfunction.js +++ b/src/ol/imageurlfunction.js @@ -15,14 +15,13 @@ ol.ImageUrlFunctionType; /** * @param {string} baseUrl Base URL (may have query data). * @param {Object.} params WMS parameters. - * @param {string=} opt_version WMS version. * @return {ol.ImageUrlFunctionType} Image URL function. */ ol.ImageUrlFunction.createWMSParams = - function(baseUrl, params, opt_version) { + function(baseUrl, params) { return function(extent, size, projection) { return ol.source.wms.getUrl( - baseUrl, params, extent, size, projection, opt_version); + baseUrl, params, extent, size, projection); }; }; diff --git a/src/ol/source/singleimagewmssource.js b/src/ol/source/singleimagewmssource.js index 0a8ea8d66c..211abdbd36 100644 --- a/src/ol/source/singleimagewmssource.js +++ b/src/ol/source/singleimagewmssource.js @@ -15,8 +15,7 @@ goog.require('ol.source.ImageSource'); */ ol.source.SingleImageWMS = function(options) { var imageUrlFunction = goog.isDef(options.url) ? - ol.ImageUrlFunction.createWMSParams( - options.url, options.params, options.version) : + ol.ImageUrlFunction.createWMSParams(options.url, options.params) : ol.ImageUrlFunction.nullImageUrlFunction; goog.base(this, { diff --git a/src/ol/source/tiledwmssource.js b/src/ol/source/tiledwmssource.js index 935fac2554..db1315a93e 100644 --- a/src/ol/source/tiledwmssource.js +++ b/src/ol/source/tiledwmssource.js @@ -21,25 +21,24 @@ ol.source.TiledWMS = function(tiledWMSOptions) { if (goog.isDef(tiledWMSOptions.tileGrid)) { tileGrid = tiledWMSOptions.tileGrid; } - var version = tiledWMSOptions.version; var tileUrlFunction; if (tiledWMSOptions.urls) { var tileUrlFunctions = goog.array.map( tiledWMSOptions.urls, function(url) { return ol.TileUrlFunction.createWMSParams( - url, tiledWMSOptions.params, version); + url, tiledWMSOptions.params); }); tileUrlFunction = ol.TileUrlFunction.createFromTileUrlFunctions( tileUrlFunctions); } else if (tiledWMSOptions.url) { tileUrlFunction = ol.TileUrlFunction.createWMSParams( - tiledWMSOptions.url, tiledWMSOptions.params, version); + tiledWMSOptions.url, tiledWMSOptions.params); } else { tileUrlFunction = ol.TileUrlFunction.nullTileUrlFunction; } - var transparent = goog.isDef(tiledWMSOptions.transparent) ? - tiledWMSOptions.transparent : true; + var transparent = goog.isDef(tiledWMSOptions.params['TRANSPARENT']) ? + tiledWMSOptions.params['TRANSPARENT'] : true; var extent = tiledWMSOptions.extent; var tileCoordTransform = function(tileCoord, tileGrid, projection) { diff --git a/src/ol/source/wms.js b/src/ol/source/wms.js index 0fff2f8e25..f076c75c59 100644 --- a/src/ol/source/wms.js +++ b/src/ol/source/wms.js @@ -7,31 +7,33 @@ goog.provide('ol.source.wms'); * @param {ol.Extent} extent Extent. * @param {ol.Size} size Size. * @param {ol.Projection} projection Projection. - * @param {string=} opt_version WMS version. Default is '1.3.0'. * @return {string} WMS GetMap request URL. */ ol.source.wms.getUrl = - function(baseUrl, params, extent, size, projection, opt_version) { - var version = goog.isDef(opt_version) ? opt_version : '1.3.0'; - var wms13 = version >= '1.3'; - var axisOrientation = projection.getAxisOrientation(); - var bboxValues = (wms13 && axisOrientation.substr(0, 2) == 'ne') ? - [extent.minY, extent.minX, extent.maxY, extent.maxX] : - [extent.minX, extent.minY, extent.maxX, extent.maxY]; + function(baseUrl, params, extent, size, projection) { var baseParams = { 'SERVICE': 'WMS', - 'VERSION': version, + 'VERSION': '1.3.0', 'REQUEST': 'GetMap', 'FORMAT': 'image/png', 'TRANSPARENT': true, 'WIDTH': size.width, - 'HEIGHT': size.height, - 'BBOX': bboxValues.join(',') + 'HEIGHT': size.height }; goog.object.extend(baseParams, params); - baseParams[wms13 ? 'CRS' : 'SRS'] = projection.getCode(); + //TODO: Provide our own appendParams function to avoid this empty string hack var stylesParam = 'STYLES'; baseParams[stylesParam] = params[stylesParam] || new String(''); + + var wms13 = baseParams['VERSION'] > '1.3'; + baseParams[wms13 ? 'CRS' : 'SRS'] = projection.getCode(); + + var axisOrientation = projection.getAxisOrientation(); + var bboxValues = (wms13 && axisOrientation.substr(0, 2) == 'ne') ? + [extent.minY, extent.minX, extent.maxY, extent.maxX] : + [extent.minX, extent.minY, extent.maxX, extent.maxY]; + baseParams['BBOX'] = bboxValues.join(','); + return goog.uri.utils.appendParamsFromMap(baseUrl, baseParams); }; diff --git a/src/ol/tileurlfunction.js b/src/ol/tileurlfunction.js index 4e09acc976..3c270b80c6 100644 --- a/src/ol/tileurlfunction.js +++ b/src/ol/tileurlfunction.js @@ -74,11 +74,10 @@ ol.TileUrlFunction.createFromTileUrlFunctions = function(tileUrlFunctions) { /** * @param {string} baseUrl Base URL (may have query data). * @param {Object.} params WMS parameters. - * @param {string=} opt_version WMS version. * @return {ol.TileUrlFunctionType} Tile URL function. */ ol.TileUrlFunction.createWMSParams = - function(baseUrl, params, opt_version) { + function(baseUrl, params) { return function(tileCoord, tileGrid, projection) { if (goog.isNull(tileCoord)) { return undefined; @@ -86,7 +85,7 @@ ol.TileUrlFunction.createWMSParams = var size = tileGrid.getTileSize(tileCoord.z); var extent = tileGrid.getTileCoordExtent(tileCoord); return ol.source.wms.getUrl( - baseUrl, params, extent, size, projection, opt_version); + baseUrl, params, extent, size, projection); } }; }; diff --git a/test/spec/ol/tileurlfunction.test.js b/test/spec/ol/tileurlfunction.test.js index d411ed4e24..a19f0659fc 100644 --- a/test/spec/ol/tileurlfunction.test.js +++ b/test/spec/ol/tileurlfunction.test.js @@ -74,10 +74,10 @@ describe('ol.TileUrlFunction', function() { 'http://wms?foo=bar', {}); var tileCoord = new ol.TileCoord(1, 0, 0); var tileUrl = tileUrlFunction(tileCoord, tileGrid, epsg3857); - var expected = 'http://wms?foo=bar&SERVICE=WMS&VERSION=1.3.0&' + - 'REQUEST=GetMap&FORMAT=image%2Fpng&TRANSPARENT=true&WIDTH=256&' + - 'HEIGHT=256&BBOX=-20037508.342789244%2C20037508.342789244%2C0%2C' + - '40075016.68557849&CRS=EPSG%3A3857&STYLES='; + var expected = 'http://wms?foo=bar&SERVICE=WMS&VERSION=1.3.0&REQUEST=' + + 'GetMap&FORMAT=image%2Fpng&TRANSPARENT=true&WIDTH=256&HEIGHT=256&' + + 'STYLES=&CRS=EPSG%3A3857&BBOX=-20037508.342789244%2C2' + + '0037508.342789244%2C0%2C40075016.68557849'; expect(tileUrl).toEqual(expected); }); it('creates expected URL respecting axis orientation', function() { @@ -86,10 +86,10 @@ describe('ol.TileUrlFunction', function() { 'http://wms?foo=bar', {}); var tileCoord = new ol.TileCoord(1, 0, 0); var tileUrl = tileUrlFunction(tileCoord, tileGrid, epsg4326); - var expected = 'http://wms?foo=bar&SERVICE=WMS&VERSION=1.3.0&' + - 'REQUEST=GetMap&FORMAT=image%2Fpng&TRANSPARENT=true&WIDTH=256&' + - 'HEIGHT=256&BBOX=20037508.342789244%2C-20037508.342789244%2C' + - '40075016.68557849%2C0&CRS=EPSG%3A4326&STYLES='; + var expected = 'http://wms?foo=bar&SERVICE=WMS&VERSION=1.3.0&REQUEST=' + + 'GetMap&FORMAT=image%2Fpng&TRANSPARENT=true&WIDTH=256&HEIGHT=256&' + + 'STYLES=&CRS=EPSG%3A4326&BBOX=20037508.342789244%2C' + + '-20037508.342789244%2C40075016.68557849%2C0'; expect(tileUrl).toEqual(expected); }); }); From 2ee19f699ab0fe4f46313fb4848ff77e2e4e2081 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 5 Mar 2013 11:11:41 +0100 Subject: [PATCH 136/180] Moving VERSION into params --- examples/epsg-4326.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/epsg-4326.js b/examples/epsg-4326.js index c6d48eb4ed..1f8cff65b6 100644 --- a/examples/epsg-4326.js +++ b/examples/epsg-4326.js @@ -16,8 +16,8 @@ var layers = new ol.Collection([ source: new ol.source.TiledWMS({ url: 'http://vmap0.tiles.osgeo.org/wms/vmap0', crossOrigin: null, - version: '1.1.1', params: { + 'VERSION': '1.1.1', 'LAYERS': 'basic', 'FORMAT': 'image/jpeg' }, From 221c8077e0f889f016092bc58d2db27dacf10c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 5 Mar 2013 11:26:22 +0100 Subject: [PATCH 137/180] Make hostexamples target more robust --- build.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/build.py b/build.py index c0e7e7c1b8..0154f20d94 100755 --- a/build.py +++ b/build.py @@ -363,15 +363,21 @@ def jsdoc_BRANCH_timestamp(t): @target('hostexamples', 'build', 'examples', phony=True) def hostexamples(t): - t.makedirs('build/gh-pages/%(BRANCH)s/examples') - t.makedirs('build/gh-pages/%(BRANCH)s/build') - t.cp(EXAMPLES, (path.replace('.html', '.js') for path in EXAMPLES), 'examples/style.css', 'build/gh-pages/%(BRANCH)s/examples/') - t.rm_rf('build/gh-pages/%(BRANCH)s/examples/data') - t.cp_r('examples/data', 'build/gh-pages/%(BRANCH)s/examples/data') - t.cp('build/loader_hosted_examples.js', 'build/gh-pages/%(BRANCH)s/examples/loader.js') - t.cp('build/ol.js', 'build/ol-simple.js', 'build/ol-whitespace.js', 'build/ol.css', 'build/gh-pages/%(BRANCH)s/build/') - t.cp('examples/example-list.html', 'build/gh-pages/%(BRANCH)s/examples/index.html') - t.cp('examples/example-list.js', 'examples/example-list.xml', 'examples/Jugl.js', 'build/gh-pages/%(BRANCH)s/examples/') + examples_dir = 'build/gh-pages/%(BRANCH)s/examples' + build_dir = 'build/gh-pages/%(BRANCH)s/build' + t.rm_rf(examples_dir) + t.makedirs(examples_dir) + t.rm_rf(build_dir) + t.makedirs(build_dir) + t.cp(EXAMPLES, (path.replace('.html', '.js') for path in EXAMPLES), + 'examples/style.css', examples_dir) + t.cp_r('examples/data', examples_dir + '/data') + t.cp('build/loader_hosted_examples.js', examples_dir + '/loader.js') + t.cp('build/ol.js', 'build/ol-simple.js', 'build/ol-whitespace.js', + 'build/ol.css', build_dir) + t.cp('examples/example-list.html', examples_dir + '/index.html') + t.cp('examples/example-list.js', 'examples/example-list.xml', + 'examples/Jugl.js', examples_dir) @target('check-examples', 'hostexamples', phony=True) From 382022bdb578c56a8a599316ebc51887217e4cd0 Mon Sep 17 00:00:00 2001 From: Bruno Binet Date: Tue, 5 Mar 2013 11:19:46 +0100 Subject: [PATCH 138/180] Add defaultNamespaceURI and errorProperty properties for WMTS. Thanks @bartvde. --- src/ol/parser/ogc/wmtscapabilities_v1_0_0.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ol/parser/ogc/wmtscapabilities_v1_0_0.js b/src/ol/parser/ogc/wmtscapabilities_v1_0_0.js index 9260038a63..19ad6d481e 100644 --- a/src/ol/parser/ogc/wmtscapabilities_v1_0_0.js +++ b/src/ol/parser/ogc/wmtscapabilities_v1_0_0.js @@ -12,6 +12,8 @@ goog.require('ol.projection'); * @extends {ol.parser.XML} */ ol.parser.ogc.WMTSCapabilities_v1_0_0 = function() { + this.defaultNamespaceURI = 'http://www.opengis.net/wtms/1.0'; + this.errorProperty = 'serviceIdentification'; this.readers = { 'http://www.opengis.net/wmts/1.0': { 'Capabilities': function(node, obj) { From a0ba80bcbb5f8c6973162ab56b7b9d05fdbfa281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 5 Mar 2013 11:37:30 +0100 Subject: [PATCH 139/180] Check the examples against whitespace build first This is to produce stack traces that are easier to understand when an example fails to load. --- build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.py b/build.py index 0154f20d94..ce0cad2162 100755 --- a/build.py +++ b/build.py @@ -384,9 +384,9 @@ def hostexamples(t): def check_examples(t): directory = 'build/gh-pages/%(BRANCH)s/' examples = ['build/gh-pages/%(BRANCH)s/' + e for e in EXAMPLES] - all_examples = examples + \ + all_examples = [e + '?mode=whitespace' for e in examples] + \ [e + '?mode=simple' for e in examples] + \ - [e + '?mode=whitespace' for e in examples] + examples for example in all_examples: t.run('%(PHANTOMJS)s', 'bin/check-example.js', example) From ba78812796ab81bac03045d27999796fc787fedf Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 5 Mar 2013 11:51:53 +0100 Subject: [PATCH 140/180] Improve comment parsing when checking for requires --- build.py | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/build.py b/build.py index ce0cad2162..e81e3e7426 100755 --- a/build.py +++ b/build.py @@ -243,24 +243,24 @@ def build_lint_src_timestamp(t): def _strip_comments(lines): # FIXME this is a horribe hack, we should use a proper JavaScript parser here - in_comment = False + in_multiline_comment = False + lineno = 0 for line in lines: - if in_comment: + lineno += 1 + if in_multiline_comment: index = line.find('*/') if index != -1: - in_comment = False - yield line[index + 2:] - else: + in_multiline_comment = False + line = line[index + 2:] + if not in_multiline_comment: + line = re.sub(r'//[^\n]*', '', line) + line = re.sub(r'/\*.*?\*/', '', line) index = line.find('/*') if index != -1: - yield line[:index] - in_comment = True + yield lineno, line[:index] + in_multiline_comment = True else: - index = line.find('//') - if index != -1: - yield line[:index] - else: - yield line + yield lineno, line @target('build/check-requires-timestamp', SRC, INTERNAL_SRC, EXTERNAL_SRC, EXAMPLES_SRC, SPEC) @@ -272,10 +272,8 @@ def build_check_requires_timestamp(t): continue require_linenos = {} uses = set() - lineno = 0 lines = open(filename).readlines() - for line in lines: - lineno += 1 + for lineno, line in _strip_comments(lines): m = re.match(r'goog.provide\(\'(.*)\'\);', line) if m: all_provides.add(m.group(1)) @@ -284,7 +282,7 @@ def build_check_requires_timestamp(t): if m: require_linenos[m.group(1)] = lineno continue - for line in lines: + for lineno, line in _strip_comments(lines): for require in require_linenos.iterkeys(): if require in line: uses.add(require) @@ -302,9 +300,7 @@ def build_check_requires_timestamp(t): provides = set() requires = set() uses = set() - lineno = 0 - for line in _strip_comments(open(filename)): - lineno += 1 + for lineno, line in _strip_comments(open(filename)): m = re.match(r'goog.provide\(\'(.*)\'\);', line) if m: provides.add(m.group(1)) From 5d99ead2b7a68c7b830f3c263fd73d51b880c334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Tue, 5 Mar 2013 12:51:03 +0100 Subject: [PATCH 141/180] View2D NoConstraint methods renamed --- src/ol/interaction/dragrotateinteraction.js | 2 +- src/ol/interaction/touchrotateinteraction.js | 3 ++- src/ol/interaction/touchzoominteraction.js | 2 +- src/ol/view2d.js | 10 +++++----- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ol/interaction/dragrotateinteraction.js b/src/ol/interaction/dragrotateinteraction.js index d4036be9c1..2ad45dfa78 100644 --- a/src/ol/interaction/dragrotateinteraction.js +++ b/src/ol/interaction/dragrotateinteraction.js @@ -53,7 +53,7 @@ ol.interaction.DragRotate.prototype.handleDrag = function(mapBrowserEvent) { // FIXME supports View2D only goog.asserts.assert(view instanceof ol.View2D); map.requestRenderFrame(); - view.rotateNoConstraint(map, view.getRotation() - delta); + view.rotateWithoutConstraints(map, view.getRotation() - delta); } this.lastAngle_ = theta; }; diff --git a/src/ol/interaction/touchrotateinteraction.js b/src/ol/interaction/touchrotateinteraction.js index a3a6e14dfe..3f4abc3ce7 100644 --- a/src/ol/interaction/touchrotateinteraction.js +++ b/src/ol/interaction/touchrotateinteraction.js @@ -98,7 +98,8 @@ ol.interaction.TouchRotate.prototype.handleTouchMove = // rotate if (this.rotating_) { - view.rotateNoConstraint(map, view.getRotation() + rotationDelta, anchor); + view.rotateWithoutConstraints(map, view.getRotation() + rotationDelta, + anchor); } }; diff --git a/src/ol/interaction/touchzoominteraction.js b/src/ol/interaction/touchzoominteraction.js index 76d99bafd7..bbb0bab518 100644 --- a/src/ol/interaction/touchzoominteraction.js +++ b/src/ol/interaction/touchzoominteraction.js @@ -65,7 +65,7 @@ ol.interaction.TouchZoom.prototype.handleTouchMove = var anchor = map.getCoordinateFromPixel(centroid); // scale, bypass the resolution constraint - view.zoomNoConstraint(map, view.getResolution() * scaleDelta, anchor); + view.zoomWithoutConstraints(map, view.getResolution() * scaleDelta, anchor); }; diff --git a/src/ol/view2d.js b/src/ol/view2d.js index f022a1bf25..f472014250 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -264,7 +264,7 @@ goog.exportProperty( ol.View2D.prototype.rotate = function(map, rotation, opt_anchor, opt_duration) { rotation = this.constraints_.rotation(rotation, 0); - this.rotateNoConstraint(map, rotation, opt_anchor, opt_duration); + this.rotateWithoutConstraints(map, rotation, opt_anchor, opt_duration); }; @@ -274,7 +274,7 @@ ol.View2D.prototype.rotate = * @param {ol.Coordinate=} opt_anchor Anchor coordinate. * @param {number=} opt_duration Duration. */ -ol.View2D.prototype.rotateNoConstraint = +ol.View2D.prototype.rotateWithoutConstraints = function(map, rotation, opt_anchor, opt_duration) { if (goog.isDefAndNotNull(rotation)) { var currentRotation = this.getRotation(); @@ -324,7 +324,7 @@ ol.View2D.prototype.rotateNoConstraint = ol.View2D.prototype.zoom = function(map, resolution, opt_anchor, opt_duration) { resolution = this.constraints_.resolution(resolution, 0); - this.zoomNoConstraint(map, resolution, opt_anchor, opt_duration); + this.zoomWithoutConstraints(map, resolution, opt_anchor, opt_duration); }; @@ -338,7 +338,7 @@ ol.View2D.prototype.zoomByDelta = function(map, delta, opt_anchor, opt_duration) { var currentResolution = this.getResolution(); var resolution = this.constraints_.resolution(currentResolution, delta); - this.zoomNoConstraint(map, resolution, opt_anchor, opt_duration); + this.zoomWithoutConstraints(map, resolution, opt_anchor, opt_duration); }; @@ -348,7 +348,7 @@ ol.View2D.prototype.zoomByDelta = * @param {ol.Coordinate=} opt_anchor Anchor coordinate. * @param {number=} opt_duration Duration. */ -ol.View2D.prototype.zoomNoConstraint = +ol.View2D.prototype.zoomWithoutConstraints = function(map, resolution, opt_anchor, opt_duration) { if (goog.isDefAndNotNull(resolution)) { var currentResolution = this.getResolution(); From 95f3fe535ac91fb73a3ecadf6092eb9a22dc9e62 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 5 Mar 2013 15:25:47 +0100 Subject: [PATCH 142/180] No projection configuration needed on the layer --- examples/epsg-4326.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/epsg-4326.js b/examples/epsg-4326.js index 1f8cff65b6..303da4c23e 100644 --- a/examples/epsg-4326.js +++ b/examples/epsg-4326.js @@ -9,8 +9,6 @@ goog.require('ol.projection'); goog.require('ol.source.TiledWMS'); -var epsg4326 = ol.projection.getFromCode('EPSG:4326'); - var layers = new ol.Collection([ new ol.layer.TileLayer({ source: new ol.source.TiledWMS({ @@ -20,8 +18,7 @@ var layers = new ol.Collection([ 'VERSION': '1.1.1', 'LAYERS': 'basic', 'FORMAT': 'image/jpeg' - }, - projection: epsg4326 + } }) }) ]); @@ -34,7 +31,7 @@ var map = new ol.Map({ scaleLineUnits: ol.control.ScaleLineUnits.DEGREES, target: 'map', view: new ol.View2D({ - projection: epsg4326, + projection: ol.projection.getFromCode('EPSG:4326'), center: new ol.Coordinate(0, 0), zoom: 2 }) From c0c8faaf9f909ac7636153bd2e653d2e885204e2 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 6 Mar 2013 00:52:48 +0100 Subject: [PATCH 143/180] Allow literal from symbolizer without feature --- src/ol/style/line.js | 8 ++++++-- src/ol/style/polygon.js | 8 ++++++-- src/ol/style/shape.js | 8 ++++++-- src/ol/style/symbolizer.js | 2 +- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/ol/style/line.js b/src/ol/style/line.js index 59de816f72..c9ae263b6d 100644 --- a/src/ol/style/line.js +++ b/src/ol/style/line.js @@ -94,8 +94,12 @@ goog.inherits(ol.style.Line, ol.style.Symbolizer); * @inheritDoc * @return {ol.style.LineLiteral} Literal line symbolizer. */ -ol.style.Line.prototype.createLiteral = function(feature) { - var attrs = feature.getAttributes(); +ol.style.Line.prototype.createLiteral = function(opt_feature) { + var attrs, + feature = opt_feature; + if (goog.isDef(feature)) { + attrs = feature.getAttributes(); + } var strokeStyle = this.strokeStyle_.evaluate(feature, attrs); goog.asserts.assertString(strokeStyle, 'strokeStyle must be a string'); diff --git a/src/ol/style/polygon.js b/src/ol/style/polygon.js index c144ada353..6fe033f0de 100644 --- a/src/ol/style/polygon.js +++ b/src/ol/style/polygon.js @@ -109,8 +109,12 @@ goog.inherits(ol.style.Polygon, ol.style.Symbolizer); * @inheritDoc * @return {ol.style.PolygonLiteral} Literal shape symbolizer. */ -ol.style.Polygon.prototype.createLiteral = function(feature) { - var attrs = feature.getAttributes(); +ol.style.Polygon.prototype.createLiteral = function(opt_feature) { + var attrs, + feature = opt_feature; + if (goog.isDef(feature)) { + attrs = feature.getAttributes(); + } var fillStyle = this.fillStyle_.evaluate(feature, attrs); goog.asserts.assertString(fillStyle, 'fillStyle must be a string'); diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js index 83056a6276..bedf8b213d 100644 --- a/src/ol/style/shape.js +++ b/src/ol/style/shape.js @@ -143,8 +143,12 @@ ol.style.Shape = function(options) { * @inheritDoc * @return {ol.style.ShapeLiteral} Literal shape symbolizer. */ -ol.style.Shape.prototype.createLiteral = function(feature) { - var attrs = feature.getAttributes(); +ol.style.Shape.prototype.createLiteral = function(opt_feature) { + var attrs, + feature = opt_feature; + if (goog.isDef(feature)) { + attrs = feature.getAttributes(); + } var size = this.size_.evaluate(feature, attrs); goog.asserts.assertNumber(size, 'size must be a number'); diff --git a/src/ol/style/symbolizer.js b/src/ol/style/symbolizer.js index 36bec5fbdc..b35ad81561 100644 --- a/src/ol/style/symbolizer.js +++ b/src/ol/style/symbolizer.js @@ -27,7 +27,7 @@ ol.style.Symbolizer = function() {}; /** - * @param {ol.Feature} feature Feature for evaluating expressions. + * @param {ol.Feature=} opt_feature Feature for evaluating expressions. * @return {ol.style.SymbolizerLiteral} Literal symbolizer. */ ol.style.Symbolizer.prototype.createLiteral = goog.abstractMethod; From ac10bb6ca50c733f079b94f7890cbcf5fb620570 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 6 Mar 2013 02:18:27 +0100 Subject: [PATCH 144/180] Enforce that either stroke or fill is provided --- src/ol/renderer/canvas/canvasrenderer.js | 9 +- src/ol/style/shape.js | 107 ++++++++++++++++------- test/spec/ol/style/shape.test.js | 41 ++++++++- 3 files changed, 117 insertions(+), 40 deletions(-) diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index 4f0d0b7fb4..47fd2f4f18 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -244,7 +244,8 @@ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = * @private */ ol.renderer.canvas.Renderer.renderCircle_ = function(circle) { - var size = circle.size + (2 * circle.strokeWidth) + 1, + var strokeWidth = circle.strokeWidth || 0, + size = circle.size + (2 * strokeWidth) + 1, mid = size / 2, canvas = /** @type {HTMLCanvasElement} */ (goog.dom.createElement(goog.dom.TagName.CANVAS)), @@ -260,11 +261,11 @@ ol.renderer.canvas.Renderer.renderCircle_ = function(circle) { context.globalAlpha = circle.opacity; if (fillStyle) { - context.fillStyle = circle.fillStyle; + context.fillStyle = fillStyle; } if (strokeStyle) { - context.lineWidth = circle.strokeWidth; - context.strokeStyle = circle.strokeStyle; + context.lineWidth = strokeWidth; + context.strokeStyle = strokeStyle; } context.beginPath(); diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js index bedf8b213d..246553b5c8 100644 --- a/src/ol/style/shape.js +++ b/src/ol/style/shape.js @@ -19,9 +19,9 @@ ol.style.ShapeType = { /** * @typedef {{type: (ol.style.ShapeType), * size: (number), - * fillStyle: (string), - * strokeStyle: (string), - * strokeWidth: (number), + * fillStyle: (string|undefined), + * strokeStyle: (string|undefined), + * strokeWidth: (number|undefined), * opacity: (number)}} */ ol.style.ShapeLiteralOptions; @@ -43,17 +43,30 @@ ol.style.ShapeLiteral = function(config) { /** @type {number} */ this.size = config.size; - goog.asserts.assertString(config.fillStyle, 'fillStyle must be a string'); - /** @type {string} */ + if (goog.isDef(config.fillStyle)) { + goog.asserts.assertString(config.fillStyle, 'fillStyle must be a string'); + } + /** @type {string|undefined} */ this.fillStyle = config.fillStyle; - goog.asserts.assertString(config.strokeStyle, 'strokeStyle must be a string'); - /** @type {string} */ + /** @type {string|undefined} */ this.strokeStyle = config.strokeStyle; + if (goog.isDef(this.strokeStyle)) { + goog.asserts.assertString( + this.strokeStyle, 'strokeStyle must be a string'); + } - goog.asserts.assertNumber(config.strokeWidth, 'strokeWidth must be a number'); - /** @type {number} */ + /** @type {number|undefined} */ this.strokeWidth = config.strokeWidth; + if (goog.isDef(this.strokeWidth)) { + goog.asserts.assertNumber( + this.strokeWidth, 'strokeWidth must be a number'); + } + + goog.asserts.assert( + goog.isDef(this.fillStyle) || + (goog.isDef(this.strokeStyle) && goog.isDef(this.strokeWidth)), + 'Either fillStyle or strokeStyle and strokeWidth must be set'); goog.asserts.assertNumber(config.opacity, 'opacity must be a number'); /** @type {number} */ @@ -88,8 +101,8 @@ ol.style.Shape = function(options) { * @type {ol.style.ShapeType} * @private */ - this.type_ = /** @type {ol.style.ShapeType} */ goog.isDef(options.type) ? - options.type : ol.style.ShapeDefaults.type; + this.type_ = /** @type {ol.style.ShapeType} */ (goog.isDef(options.type) ? + options.type : ol.style.ShapeDefaults.type); /** * @type {ol.Expression} @@ -104,28 +117,45 @@ ol.style.Shape = function(options) { * @type {ol.Expression} * @private */ - this.fillStyle_ = !goog.isDef(options.fillStyle) ? - new ol.ExpressionLiteral(ol.style.ShapeDefaults.fillStyle) : + this.fillStyle_ = !goog.isDefAndNotNull(options.fillStyle) ? + null : (options.fillStyle instanceof ol.Expression) ? options.fillStyle : new ol.ExpressionLiteral(options.fillStyle); - /** - * @type {ol.Expression} - * @private - */ - this.strokeStyle_ = !goog.isDef(options.strokeStyle) ? - new ol.ExpressionLiteral(ol.style.ShapeDefaults.strokeStyle) : - (options.strokeStyle instanceof ol.Expression) ? - options.strokeStyle : new ol.ExpressionLiteral(options.strokeStyle); + // stroke handling - if any stroke property is supplied, use defaults + var strokeStyle = null, + strokeWidth = null; + + if (goog.isDefAndNotNull(options.strokeStyle) || + goog.isDefAndNotNull(options.strokeWidth)) { + + strokeStyle = !goog.isDefAndNotNull(options.strokeStyle) ? + new ol.ExpressionLiteral(ol.style.ShapeDefaults.strokeStyle) : + (options.strokeStyle instanceof ol.Expression) ? + options.strokeStyle : new ol.ExpressionLiteral(options.strokeStyle); + + strokeWidth = !goog.isDef(options.strokeWidth) ? + new ol.ExpressionLiteral(ol.style.ShapeDefaults.strokeWidth) : + (options.strokeWidth instanceof ol.Expression) ? + options.strokeWidth : new ol.ExpressionLiteral(options.strokeWidth); + } /** * @type {ol.Expression} * @private */ - this.strokeWidth_ = !goog.isDef(options.strokeWidth) ? - new ol.ExpressionLiteral(ol.style.ShapeDefaults.strokeWidth) : - (options.strokeWidth instanceof ol.Expression) ? - options.strokeWidth : new ol.ExpressionLiteral(options.strokeWidth); + this.strokeStyle_ = strokeStyle; + + /** + * @type {ol.Expression} + * @private + */ + this.strokeWidth_ = strokeWidth; + + // one of stroke or fill can be null, both null is user error + goog.asserts.assert(!goog.isNull(this.fillStyle_) || + !(goog.isNull(this.strokeStyle_) && goog.isNull(this.strokeWidth_)), + 'Stroke or fill properties must be provided'); /** * @type {ol.Expression} @@ -153,14 +183,22 @@ ol.style.Shape.prototype.createLiteral = function(opt_feature) { var size = this.size_.evaluate(feature, attrs); goog.asserts.assertNumber(size, 'size must be a number'); - var fillStyle = this.fillStyle_.evaluate(feature, attrs); - goog.asserts.assertString(fillStyle, 'fillStyle must be a string'); + var fillStyle = goog.isNull(this.fillStyle_) ? + undefined : this.fillStyle_.evaluate(feature, attrs); + goog.asserts.assert(!goog.isDef(fillStyle) || goog.isString(fillStyle)); - var strokeStyle = this.strokeStyle_.evaluate(feature, attrs); - goog.asserts.assertString(strokeStyle, 'strokeStyle must be a string'); + var strokeStyle = goog.isNull(this.strokeStyle_) ? + undefined : this.strokeStyle_.evaluate(feature, attrs); + goog.asserts.assert(!goog.isDef(strokeStyle) || goog.isString(strokeStyle)); - var strokeWidth = this.strokeWidth_.evaluate(feature, attrs); - goog.asserts.assertNumber(strokeWidth, 'strokeWidth must be a number'); + var strokeWidth = goog.isNull(this.strokeWidth_) ? + undefined : this.strokeWidth_.evaluate(feature, attrs); + goog.asserts.assert(!goog.isDef(strokeWidth) || goog.isNumber(strokeWidth)); + + goog.asserts.assert( + goog.isDef(fillStyle) || + (goog.isDef(strokeStyle) && goog.isDef(strokeWidth)), + 'either fill style or strokeStyle and strokeWidth must be defined'); var opacity = this.opacity_.evaluate(feature, attrs); goog.asserts.assertNumber(opacity, 'opacity must be a number'); @@ -168,9 +206,10 @@ ol.style.Shape.prototype.createLiteral = function(opt_feature) { return new ol.style.ShapeLiteral({ type: this.type_, size: size, - fillStyle: fillStyle, - strokeStyle: strokeStyle, - strokeWidth: strokeWidth, + // TODO: check if typecast can be avoided here + fillStyle: /** @type {string|undefined} */ (fillStyle), + strokeStyle: /** @type {string|undefined} */ (strokeStyle), + strokeWidth: /** @type {number|undefined} */ (strokeWidth), opacity: opacity }); }; diff --git a/test/spec/ol/style/shape.test.js b/test/spec/ol/style/shape.test.js index 4f06a7a661..147528df0c 100644 --- a/test/spec/ol/style/shape.test.js +++ b/test/spec/ol/style/shape.test.js @@ -52,7 +52,7 @@ describe('ol.style.Shape', function() { it('accepts expressions', function() { var symbolizer = new ol.style.Shape({ size: new ol.Expression('sizeAttr'), - strokeStyle: ol.Expression('color') + strokeStyle: new ol.Expression('color') }); expect(symbolizer).toBeA(ol.style.Shape); }); @@ -64,7 +64,44 @@ describe('ol.style.Shape', function() { it('evaluates expressions with the given feature', function() { var symbolizer = new ol.style.Shape({ size: new ol.Expression('sizeAttr'), - opacity: new ol.Expression('opacityAttr') + opacity: new ol.Expression('opacityAttr'), + fillStyle: '#BADA55' + }); + + var feature = new ol.Feature({ + sizeAttr: 42, + opacityAttr: 0.4 + }); + + var literal = symbolizer.createLiteral(feature); + expect(literal).toBeA(ol.style.ShapeLiteral); + expect(literal.size).toBe(42); + expect(literal.opacity).toBe(0.4); + }); + + it('can be called without a feature', function() { + var symbolizer = new ol.style.Shape({ + size: 10, + opacity: 1, + fillStyle: '#BADA55', + strokeStyle: '#013', + strokeWidth: 2 + }); + + var literal = symbolizer.createLiteral(); + expect(literal).toBeA(ol.style.ShapeLiteral); + expect(literal.size).toBe(10); + expect(literal.opacity).toBe(1); + expect(literal.fillStyle).toBe('#BADA55'); + expect(literal.strokeStyle).toBe('#013'); + expect(literal.strokeWidth).toBe(2); + }); + + it('applies default type if none provided', function() { + var symbolizer = new ol.style.Shape({ + size: new ol.Expression('sizeAttr'), + opacity: new ol.Expression('opacityAttr'), + fillStyle: '#BADA55' }); var feature = new ol.Feature({ From 345bb55f98de34ae12d9530fe4b859114d10a0c3 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 6 Mar 2013 12:29:20 +0100 Subject: [PATCH 145/180] Respect width and height from symbolizer --- src/ol/renderer/canvas/canvasrenderer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index b4609931a1..ff147328ea 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -185,7 +185,7 @@ ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = point = /** @type {ol.geom.Point} */ features[i].getGeometry(); vec = goog.vec.Mat4.multVec3( this.transform_, [point.get(0), point.get(1), 0], []); - context.drawImage(content, vec[0], vec[1]); + context.drawImage(content, vec[0], vec[1], content.width, content.height); } context.restore(); @@ -355,8 +355,10 @@ ol.renderer.canvas.Renderer.renderIcon = function(icon, opt_callback) { image.height = height; } else if (goog.isDef(width)) { image.height = width / image.width * image.height; + image.width = width; } else if (goog.isDef(height)) { image.width = height / image.height * image.width; + image.height = height; } } return deferred ? null : image; From 2147674e2f4eb2017a4ecf2fd005db26293b5c89 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 6 Mar 2013 12:49:06 +0100 Subject: [PATCH 146/180] Adding exports for icon and feature --- src/objectliterals.exports | 7 +++++++ src/ol/style.exports | 2 ++ src/ol/style/icon.js | 10 ---------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/objectliterals.exports b/src/objectliterals.exports index 8df22dc74e..dae5ee0510 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -143,6 +143,13 @@ @exportObjectLiteralProperty ol.source.TiledWMSOptions.url string|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.urls Array.|undefined +@exportObjectLiteral ol.style.IconOptions +@exportObjectLiteralProperty ol.style.IconOptions.url string|ol.Expression +@exportObjectLiteralProperty ol.style.IconOptions.width number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.IconOptions.height number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.IconOptions.opacity number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.IconOptions.rotation number|ol.Expression|undefined + @exportObjectLiteral ol.style.LineOptions @exportObjectLiteralProperty ol.style.LineOptions.strokeStyle string|ol.Expression|undefined @exportObjectLiteralProperty ol.style.LineOptions.strokeWidth number|ol.Expression|undefined diff --git a/src/ol/style.exports b/src/ol/style.exports index 9b47275dfb..8df1a0a1bd 100644 --- a/src/ol/style.exports +++ b/src/ol/style.exports @@ -1,7 +1,9 @@ +@exportClass ol.style.Icon ol.style.IconOptions @exportClass ol.style.Line ol.style.LineOptions @exportClass ol.style.Polygon ol.style.PolygonOptions @exportClass ol.style.Rule ol.style.RuleOptions @exportClass ol.style.Shape ol.style.ShapeOptions @exportClass ol.style.Style ol.style.StyleOptions +@exportSymbol ol.style.IconType @exportSymbol ol.style.ShapeType @exportProperty ol.style.ShapeType.CIRCLE diff --git a/src/ol/style/icon.js b/src/ol/style/icon.js index 251981b6d3..16e2e90b34 100644 --- a/src/ol/style/icon.js +++ b/src/ol/style/icon.js @@ -57,16 +57,6 @@ ol.style.IconLiteral.prototype.equals = function(iconLiteral) { }; -/** - * @typedef {{url: (string|ol.Expression), - * width: (number|ol.Expression|undefined), - * height: (number|ol.Expression|undefined), - * opacity: (number|ol.Expression|undefined), - * rotation: (number|ol.Expression|undefined)}} - */ -ol.style.IconOptions; - - /** * @constructor From 2af82abcda44cab5eb0e8cc53f480ca70e5c47b1 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 6 Mar 2013 21:03:57 +0100 Subject: [PATCH 147/180] Adding missing export --- src/ol/feature.exports | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ol/feature.exports b/src/ol/feature.exports index 3192a1fd2d..67545ce3d5 100644 --- a/src/ol/feature.exports +++ b/src/ol/feature.exports @@ -4,3 +4,4 @@ @exportProperty ol.Feature.prototype.getGeometry @exportProperty ol.Feature.prototype.set @exportProperty ol.Feature.prototype.setGeometry +@exportProperty ol.Feature.prototype.setSymbolizers From 100c8a1cf4341873f15a7c3f226eecd47caa1c66 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 6 Mar 2013 21:06:21 +0100 Subject: [PATCH 148/180] Adding gutter when requesting features for a tile By doing so, we avoid features being cut off when the symbolizer causes it to be rendered across tile borders. --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index a81f18d951..dc22f5d27f 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -271,11 +271,13 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var renderedFeatures = {}; var tilesToRender = {}; + // TODO make gutter configurable? + var tileGutter = 15 * tileResolution; var tile, tileCoord, key, tileState, x, y; // render features by geometry type var filters = this.geometryFilters_, numFilters = filters.length, - i, geomFilter, extentFilter, type, features, + i, geomFilter, tileExtent, extentFilter, type, features, groups, group, j, numGroups, deferred; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { @@ -285,8 +287,12 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = if (this.tileCache_.containsKey(key)) { tilesToRender[key] = tileCoord; } else if (!frameState.viewHints[ol.ViewHint.ANIMATING]) { - extentFilter = new ol.filter.Extent( - tileGrid.getTileCoordExtent(tileCoord)); + tileExtent = tileGrid.getTileCoordExtent(tileCoord); + tileExtent.minX -= tileGutter; + tileExtent.minY -= tileGutter; + tileExtent.maxX += tileGutter; + tileExtent.maxY += tileGutter; + extentFilter = new ol.filter.Extent(tileExtent); for (i = 0; i < numFilters; ++i) { geomFilter = filters[i]; type = geomFilter.getType(); From 7c4c3c547c470511ab1b5c7b08cff209e1d356e4 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 6 Mar 2013 19:01:16 -0700 Subject: [PATCH 149/180] Draw multi-part geometries Polygons still need holes. --- src/ol/renderer/canvas/canvasrenderer.js | 121 +++++++++++++++-------- 1 file changed, 79 insertions(+), 42 deletions(-) diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index ff147328ea..c171227cfb 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -10,6 +10,9 @@ goog.require('ol.canvas'); goog.require('ol.geom.Geometry'); goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); goog.require('ol.geom.Point'); goog.require('ol.geom.Polygon'); goog.require('ol.style.IconLiteral'); @@ -95,17 +98,23 @@ ol.renderer.canvas.Renderer.prototype.renderFeaturesByGeometryType = var deferred = false; switch (type) { case ol.geom.GeometryType.POINT: - goog.asserts.assert(symbolizer instanceof ol.style.PointLiteral); + case ol.geom.GeometryType.MULTIPOINT: + goog.asserts.assert(symbolizer instanceof ol.style.PointLiteral, + 'Expected point symbolizer: ' + symbolizer); deferred = this.renderPointFeatures_( features, /** @type {ol.style.PointLiteral} */ (symbolizer)); break; case ol.geom.GeometryType.LINESTRING: - goog.asserts.assert(symbolizer instanceof ol.style.LineLiteral); + case ol.geom.GeometryType.MULTILINESTRING: + goog.asserts.assert(symbolizer instanceof ol.style.LineLiteral, + 'Expected line symbolizer: ' + symbolizer); this.renderLineStringFeatures_( features, /** @type {ol.style.LineLiteral} */ (symbolizer)); break; case ol.geom.GeometryType.POLYGON: - goog.asserts.assert(symbolizer instanceof ol.style.PolygonLiteral); + case ol.geom.GeometryType.MULTIPOLYGON: + goog.asserts.assert(symbolizer instanceof ol.style.PolygonLiteral, + 'Expected polygon symbolizer: ' + symbolizer); this.renderPolygonFeatures_( features, /** @type {ol.style.PolygonLiteral} */ (symbolizer)); break; @@ -125,22 +134,32 @@ ol.renderer.canvas.Renderer.prototype.renderLineStringFeatures_ = function(features, symbolizer) { var context = this.context_, - i, ii, line, dim, j, jj, x, y; + i, ii, geometry, components, j, jj, line, dim, k, kk, 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 = /** @type {ol.geom.LineString} */ features[i].getGeometry(); - dim = line.dimension; - 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 { - context.lineTo(x, y); + geometry = features[i].getGeometry(); + if (geometry instanceof ol.geom.LineString) { + components = [geometry]; + } else { + goog.asserts.assert(geometry instanceof ol.geom.MultiLineString, + 'Expected MultiLineString'); + components = geometry.components; + } + for (j = 0, jj = components.length; j < jj; ++j) { + line = components[j]; + dim = line.dimension; + for (k = 0, kk = line.getCount(); k < kk; ++k) { + x = line.get(k, 0); + y = line.get(k, 1); + if (k === 0) { + context.moveTo(x, y); + } else { + context.lineTo(x, y); + } } } } @@ -159,7 +178,7 @@ ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = function(features, symbolizer) { var context = this.context_, - content, alpha, i, ii, point, vec; + content, alpha, i, ii, geometry, components, j, jj, point, vec; if (symbolizer instanceof ol.style.ShapeLiteral) { content = ol.renderer.canvas.Renderer.renderShape(symbolizer); @@ -182,10 +201,20 @@ ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = context.setTransform(1, 0, 0, 1, -midWidth, -midHeight); context.globalAlpha = alpha; for (i = 0, ii = features.length; i < ii; ++i) { - point = /** @type {ol.geom.Point} */ features[i].getGeometry(); - vec = goog.vec.Mat4.multVec3( - this.transform_, [point.get(0), point.get(1), 0], []); - context.drawImage(content, vec[0], vec[1], content.width, content.height); + geometry = features[i].getGeometry(); + if (geometry instanceof ol.geom.Point) { + components = [geometry]; + } else { + goog.asserts.assert(geometry instanceof ol.geom.MultiPoint, + 'Expected MultiPoint'); + components = geometry.components; + } + for (j = 0, jj = components.length; j < jj; ++j) { + point = components[j]; + vec = goog.vec.Mat4.multVec3( + this.transform_, [point.get(0), point.get(1), 0], []); + context.drawImage(content, vec[0], vec[1], content.width, content.height); + } } context.restore(); @@ -204,7 +233,8 @@ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = var context = this.context_, strokeStyle = symbolizer.strokeStyle, fillStyle = symbolizer.fillStyle, - i, ii, poly, rings, numRings, ring, dim, j, jj, x, y; + i, ii, geometry, components, j, jj, poly, + rings, numRings, ring, dim, k, kk, x, y; context.globalAlpha = symbolizer.opacity; if (strokeStyle) { @@ -224,31 +254,38 @@ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = */ context.beginPath(); for (i = 0, ii = features.length; i < ii; ++i) { - poly = /** @type {ol.geom.Polygon} */ features[i].getGeometry(); - dim = poly.dimension; - rings = poly.rings; - numRings = rings.length; - if (numRings > 1) { - // scenario 4 - // TODO: use sketch canvas to render outer and punch holes for inner rings - throw new Error('Rendering holes not implemented'); + geometry = features[i].getGeometry(); + if (geometry instanceof ol.geom.Polygon) { + components = [geometry]; } else { - 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 { - context.lineTo(x, y); + goog.asserts.assert(geometry instanceof ol.geom.MultiPolygon, + 'Expected MultiPolygon'); + components = geometry.components; + } + for (j = 0, jj = components.length; j < jj; ++j) { + poly = components[j]; + dim = poly.dimension; + rings = poly.rings; + numRings = rings.length; + if (numRings > 0) { + // TODO: scenario 4 + ring = rings[0]; + for (k = 0, kk = ring.getCount(); k < kk; ++k) { + x = ring.get(k, 0); + y = ring.get(k, 1); + if (k === 0) { + context.moveTo(x, y); + } else { + context.lineTo(x, y); + } } - } - if (fillStyle && strokeStyle) { - // scenario 3 - fill and stroke each time - context.fill(); - context.stroke(); - if (i < ii - 1) { - context.beginPath(); + if (fillStyle && strokeStyle) { + // scenario 3 - fill and stroke each time + context.fill(); + context.stroke(); + if (i < ii - 1) { + context.beginPath(); + } } } } From d7c547f736b19a5e14d126e8b8bc486b3bfdde14 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 6 Mar 2013 19:02:21 -0700 Subject: [PATCH 150/180] Method for parsing features Shared structures are still per geometry type. Need to be made per symbolizer type. --- src/ol/layer/vectorlayer.js | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 77fde89ac2..44edaa6d22 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -1,7 +1,10 @@ goog.provide('ol.layer.Vector'); +goog.require('goog.events.EventType'); goog.require('ol.Feature'); +goog.require('ol.geom.SharedVertices'); goog.require('ol.layer.Layer'); +goog.require('ol.projection'); goog.require('ol.source.Vector'); goog.require('ol.structs.RTree'); goog.require('ol.style.Style'); @@ -198,6 +201,8 @@ ol.layer.Vector.prototype.addFeatures = function(features) { for (var i = 0, ii = features.length; i < ii; ++i) { this.featureCache_.add(features[i]); } + // TODO: events for real - listeners want features and extent here + this.dispatchEvent(goog.events.EventType.CHANGE); }; @@ -259,6 +264,60 @@ ol.layer.Vector.prototype.groupFeaturesBySymbolizerLiteral = }; +/** + * @param {Object|Element|Document|string} data Feature data. + * @param {ol.parser.Parser} parser Feature parser. + * @param {ol.Projection} projection This sucks. The layer should be a view in + * one projection. + */ +ol.layer.Vector.prototype.parseFeatures = function(data, parser, projection) { + var features; + var pointVertices = new ol.geom.SharedVertices(); + var lineVertices = new ol.geom.SharedVertices(); + var polygonVertices = new ol.geom.SharedVertices(); + + var lookup = { + 'point': pointVertices, + 'linestring': lineVertices, + 'polygon': polygonVertices, + 'multipoint': pointVertices, + 'multilinstring': lineVertices, + 'multipolygon': polygonVertices + }; + + var callback = function(feature, type) { + return lookup[type]; + }; + if (typeof data === 'string') { + goog.asserts.assert(typeof parser.readFeaturesFromString === 'function', + 'Expected a parser with readFeaturesFromString method.'); + features = parser.readFeaturesFromString(data, {callback: callback}); + } else { + // TODO: parse more data types + throw new Error('Data type not supported: ' + data); + } + var sourceProjection = this.getSource().getProjection(); + var transform = ol.projection.getTransform(sourceProjection, projection); + + transform( + pointVertices.coordinates, + pointVertices.coordinates, + pointVertices.getDimension()); + + transform( + lineVertices.coordinates, + lineVertices.coordinates, + lineVertices.getDimension()); + + transform( + polygonVertices.coordinates, + polygonVertices.coordinates, + polygonVertices.getDimension()); + + this.addFeatures(features); +}; + + goog.require('ol.filter.Extent'); goog.require('ol.filter.Geometry'); goog.require('ol.filter.Logical'); From 00fa7ff3a96431d7df2e67ee5a8f651202237a61 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 6 Mar 2013 19:05:35 -0700 Subject: [PATCH 151/180] Listen for layer changes and expire tiles We need a more flexible event system. We could have a VectorLayerEvent type and dispatch 'featuresadded' here. But listeners want features typically and perhaps extent. This won't be true for all vector layer events (suggesting a more specific VectorFeatureEvent type or something). --- .../canvas/canvasvectorlayerrenderer.js | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index dc22f5d27f..242c79bdfc 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -69,6 +69,9 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { */ this.tileCache_ = new ol.TileCache( ol.renderer.canvas.VectorLayer.TILECACHE_SIZE); + // TODO: this is far too coarse, we want extent of added features + goog.events.listenOnce(layer, goog.events.EventType.CHANGE, + this.handleLayerChange_, false, this); /** * @private @@ -139,6 +142,20 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { goog.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); +/** + * Get rid cached tiles. If the optional extent is provided, only tiles that + * intersect that extent will be removed. + * @param {ol.Extent=} opt_extent extent Expire tiles within this extent only. + * @private + */ +ol.renderer.canvas.VectorLayer.prototype.expireTiles_ = function(opt_extent) { + if (goog.isDef(opt_extent)) { + // TODO: implement this + } + this.tileCache_.clear(); +}; + + /** * @inheritDoc */ @@ -163,6 +180,17 @@ ol.renderer.canvas.VectorLayer.prototype.getTransform = function() { }; +/** + * @param {goog.events.Event} event Layer change event. + * @private + */ +ol.renderer.canvas.VectorLayer.prototype.handleLayerChange_ = function(event) { + // TODO: get rid of this in favor of vector specific events + this.expireTiles_(); + this.requestMapRenderFrame_(); +}; + + /** * @inheritDoc */ From 2e4dc430e99d89809e796e1740f46a06c1c95d28 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 6 Mar 2013 19:06:41 -0700 Subject: [PATCH 152/180] Render multi-part geometries As noted in the comment, filtering/rendering once per geometry type will be replaced by rendering once per symbolizer type. --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 242c79bdfc..a7aa2c9251 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -83,12 +83,15 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { * Geometry filters in rendering order. * @private * @type {Array.} - * TODO: deal with multis + * TODO: these will go away shortly (in favor of one call per symbolizer type) */ this.geometryFilters_ = [ - new ol.filter.Geometry(ol.geom.GeometryType.POLYGON), + new ol.filter.Geometry(ol.geom.GeometryType.POINT), + new ol.filter.Geometry(ol.geom.GeometryType.MULTIPOINT), new ol.filter.Geometry(ol.geom.GeometryType.LINESTRING), - new ol.filter.Geometry(ol.geom.GeometryType.POINT) + new ol.filter.Geometry(ol.geom.GeometryType.MULTILINESTRING), + new ol.filter.Geometry(ol.geom.GeometryType.POLYGON), + new ol.filter.Geometry(ol.geom.GeometryType.MULTIPOLYGON) ]; /** From 84527c4b566a3ceb89d97a8c698938e122e1f063 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 6 Mar 2013 19:07:26 -0700 Subject: [PATCH 153/180] Disable bbox filtering for now The RTree doesn't appear to be performing properly --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index a7aa2c9251..bc90f22115 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -327,8 +327,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = for (i = 0; i < numFilters; ++i) { geomFilter = filters[i]; type = geomFilter.getType(); - features = layer.getFeatures(new ol.filter.Logical( - [geomFilter, extentFilter], ol.filter.LogicalOperator.AND)); + features = layer.getFeatures(geomFilter); if (features.length) { groups = layer.groupFeaturesBySymbolizerLiteral(features); numGroups = groups.length; From f96193082389df3184b7d56df0409b6e9f9b3b64 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 6 Mar 2013 19:24:53 -0700 Subject: [PATCH 154/180] Render countries from GeoJSON This reveals a number of issues that still need to be addressed. --- examples/vector-layer.js | 30 ++++++++++++++++++++---------- src/ol/layer/vectorlayer.exports | 2 +- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/examples/vector-layer.js b/examples/vector-layer.js index e28f0d9cd6..303dd4829a 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -8,6 +8,7 @@ goog.require('ol.geom.LineString'); goog.require('ol.geom.Point'); goog.require('ol.layer.TileLayer'); goog.require('ol.layer.Vector'); +goog.require('ol.parser.GeoJSON'); goog.require('ol.projection'); goog.require('ol.source.MapQuestOpenAerial'); goog.require('ol.source.Vector'); @@ -19,21 +20,30 @@ var raster = new ol.layer.TileLayer({ var vector = new ol.layer.Vector({ source: new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:3857') + projection: ol.projection.getFromCode('EPSG:4326') }) }); -vector.addFeatures([ - new ol.Feature({ - g: new ol.geom.LineString([[-10000000, -10000000], [10000000, 10000000]]) - }), - new ol.Feature({ - g: new ol.geom.LineString([[-10000000, 10000000], [10000000, -10000000]]) - }), - new ol.Feature({g: new ol.geom.Point([-10000000, 5000000])}) -]); +var geojson = new ol.parser.GeoJSON(); +var url = '../test/spec/ol/parser/geojson/countries.json'; +var xhr = new XMLHttpRequest(); +xhr.open('GET', url, true); +/** + * onload handler for the XHR request. + */ +xhr.onload = function() { + if (xhr.status == 200) { + // this is silly to have to tell the layer the destination projection + var projection = map.getView().getProjection(); + vector.parseFeatures(xhr.responseText, geojson, projection); + } else { + throw new Error('Data loading failed: ' + xhr.status); + } +}; +xhr.send(); + var map = new ol.Map({ layers: new ol.Collection([raster, vector]), renderer: ol.RendererHint.CANVAS, diff --git a/src/ol/layer/vectorlayer.exports b/src/ol/layer/vectorlayer.exports index 6abe609b01..ff90656cad 100644 --- a/src/ol/layer/vectorlayer.exports +++ b/src/ol/layer/vectorlayer.exports @@ -1,3 +1,3 @@ @exportClass ol.layer.Vector ol.layer.LayerOptions -@exportProperty ol.layer.Vector.prototype.addFeatures +@exportProperty ol.layer.Vector.prototype.parseFeatures From f0039ee460286389a02a094d66a5c1f1fb0f75f7 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 7 Mar 2013 14:36:09 +0100 Subject: [PATCH 155/180] Additional index dimension for RTree; use RTree again The RTree can easily maintain an additional index dimension, by passing a type with each added item. Now instead of maintaining an RTree for each geometry type, we have a single RTree with a type filter. With this change, using the RTree finally speeds up rendering as expected. --- src/ol/layer/vectorlayer.js | 30 +++++++------------ .../canvas/canvasvectorlayerrenderer.js | 3 +- src/ol/structs/rtree.js | 28 +++++++++++++---- 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 44edaa6d22..32ba3454b8 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -29,10 +29,10 @@ ol.layer.FeatureCache = function() { this.geometryTypeIndex_; /** - * @type {Object.} + * @type {ol.structs.RTree} * @private */ - this.boundsByGeometryType_; + this.rTree_; this.clear(); @@ -44,17 +44,12 @@ ol.layer.FeatureCache = function() { */ ol.layer.FeatureCache.prototype.clear = function() { this.idLookup_ = {}; - - var geometryTypeIndex = {}, - boundsByGeometryType = {}, - geometryType; + var geometryTypeIndex = {}; for (var key in ol.geom.GeometryType) { - geometryType = ol.geom.GeometryType[key]; - geometryTypeIndex[geometryType] = {}; - boundsByGeometryType[geometryType] = new ol.structs.RTree(); + geometryTypeIndex[ol.geom.GeometryType[key]] = {}; } this.geometryTypeIndex_ = geometryTypeIndex; - this.boundsByGeometryType_ = boundsByGeometryType; + this.rTree_ = new ol.structs.RTree(); }; @@ -72,8 +67,8 @@ ol.layer.FeatureCache.prototype.add = function(feature) { if (!goog.isNull(geometry)) { var geometryType = geometry.getType(); this.geometryTypeIndex_[geometryType][id] = feature; - this.boundsByGeometryType_[geometryType].put(geometry.getBounds(), - feature); + this.rTree_.put(geometry.getBounds(), + feature, geometryType); } }; @@ -91,12 +86,7 @@ ol.layer.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) { if (opt_filter instanceof ol.filter.Geometry) { features = this.geometryTypeIndex_[opt_filter.getType()]; } else if (opt_filter instanceof ol.filter.Extent) { - var boundsByGeometryType = this.boundsByGeometryType_, - extent = opt_filter.getExtent(); - features = {}; - for (i in boundsByGeometryType) { - goog.object.extend(features, boundsByGeometryType[i].find(extent)); - } + features = this.rTree_.find(opt_filter.getExtent()); } else if (opt_filter instanceof ol.filter.Logical && opt_filter.operator === ol.filter.LogicalOperator.AND) { var filters = opt_filter.getFilters(); @@ -111,8 +101,8 @@ ol.layer.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) { } } if (extentFilter && geometryFilter) { - features = this.boundsByGeometryType_[geometryFilter.getType()] - .find(extentFilter.getExtent()); + features = this.rTree_.find( + extentFilter.getExtent(), geometryFilter.getType()); } } } diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index bc90f22115..a7aa2c9251 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -327,7 +327,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = for (i = 0; i < numFilters; ++i) { geomFilter = filters[i]; type = geomFilter.getType(); - features = layer.getFeatures(geomFilter); + features = layer.getFeatures(new ol.filter.Logical( + [geomFilter, extentFilter], ol.filter.LogicalOperator.AND)); if (features.length) { groups = layer.groupFeaturesBySymbolizerLiteral(features); numGroups = groups.length; diff --git a/src/ol/structs/rtree.js b/src/ol/structs/rtree.js index 0d72dbe6ee..0d2373a105 100644 --- a/src/ol/structs/rtree.js +++ b/src/ol/structs/rtree.js @@ -39,6 +39,11 @@ ol.RTreeNode_ = function(minX, minY, maxX, maxY, parent, level) { */ this.level = level; + /** + * @type {Object.} + */ + this.types = {}; + /** * @type {Array.} */ @@ -52,9 +57,11 @@ goog.inherits(ol.RTreeNode_, ol.Rectangle); * Find all objects intersected by a rectangle. * @param {ol.Rectangle} bounds Bounding box. * @param {Object.} results Target object for results. + * @param {string=} opt_type Type for another indexing dimension. */ -ol.RTreeNode_.prototype.find = function(bounds, results) { - if (this.intersects(bounds)) { +ol.RTreeNode_.prototype.find = function(bounds, results, opt_type) { + if (this.intersects(bounds) && + (!goog.isDef(opt_type) || this.types[opt_type] === true)) { var numChildren = this.children.length; if (numChildren === 0) { if (goog.isDef(this.object)) { @@ -62,7 +69,7 @@ ol.RTreeNode_.prototype.find = function(bounds, results) { } } else { for (var i = 0; i < numChildren; ++i) { - this.children[i].find(bounds, results); + this.children[i].find(bounds, results, opt_type); } } } @@ -123,9 +130,11 @@ ol.RTreeNode_.prototype.divide = function() { if (i % half === 0) { node = new ol.RTreeNode_(child.minX, child.minY, child.maxX, child.maxY, this, this.level + 1); + goog.object.extend(this.types, node.types); this.children.push(node); } child.parent = /** @type {ol.RTreeNode_} */ node; + goog.object.extend(node.types, child.types); node.children.push(child); node.extend(child); } @@ -151,11 +160,12 @@ ol.structs.RTree = function() { /** * @param {ol.Rectangle} bounds Bounding box. + * @param {string=} opt_type Type for another indexing dimension. * @return {Object.} Results for the passed bounding box. */ -ol.structs.RTree.prototype.find = function(bounds) { +ol.structs.RTree.prototype.find = function(bounds, opt_type) { var results = /** @type {Object.} */ {}; - this.root_.find(bounds, results); + this.root_.find(bounds, results, opt_type); return results; }; @@ -163,8 +173,9 @@ ol.structs.RTree.prototype.find = function(bounds) { /** * @param {ol.Rectangle} bounds Bounding box. * @param {Object} object Object to store with the passed bounds. + * @param {string=} opt_type Type for another indexing dimension. */ -ol.structs.RTree.prototype.put = function(bounds, object) { +ol.structs.RTree.prototype.put = function(bounds, object, opt_type) { var found = this.root_.get(bounds); if (found) { var node = new ol.RTreeNode_( @@ -176,6 +187,11 @@ ol.structs.RTree.prototype.put = function(bounds, object) { found.children.push(node); found.update(bounds); + if (goog.isDef(opt_type)) { + node.types[opt_type] = true; + found.types[opt_type] = true; + } + if (found.children.length >= ol.structs.RTree.MAX_OBJECTS && found.level < ol.structs.RTree.MAX_SUB_DIVISIONS) { found.divide(); From f4a16e7f57b8475f20361d661db814b9f79ec604 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 7 Mar 2013 16:57:22 +0100 Subject: [PATCH 156/180] Collect features before rendering to the sketch canvas This avoids features being rendered multiple times when they cross tile borders. Currently this makes the style-rules.html example extremely slow. Fix for that to come in my next commit. --- src/ol/layer/vectorlayer.js | 29 +++++++------- .../canvas/canvasvectorlayerrenderer.js | 38 +++++++++++-------- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 32ba3454b8..176602e01e 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -76,9 +76,8 @@ ol.layer.FeatureCache.prototype.add = function(feature) { /** * @param {ol.filter.Filter=} opt_filter Optional filter. * @return {Object.} Object of features, keyed by id. - * @private */ -ol.layer.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) { +ol.layer.FeatureCache.prototype.getFeaturesObject = function(opt_filter) { var i, features; if (!goog.isDef(opt_filter)) { features = this.idLookup_; @@ -123,15 +122,6 @@ ol.layer.FeatureCache.prototype.getFeaturesObject_ = function(opt_filter) { }; -/** - * @param {ol.filter.Filter=} opt_filter Optional filter. - * @return {Array.} Array of features. - */ -ol.layer.FeatureCache.prototype.getFeatures = function(opt_filter) { - return goog.object.getValues(this.getFeaturesObject_(opt_filter)); -}; - - /** * @param {ol.filter.Geometry} filter Geometry type filter. * @return {Array.} Array of features. @@ -209,12 +199,22 @@ ol.layer.Vector.prototype.getVectorSource = function() { * @return {Array.} Array of features. */ ol.layer.Vector.prototype.getFeatures = function(opt_filter) { - return this.featureCache_.getFeatures(opt_filter); + return goog.object.getValues( + this.featureCache_.getFeaturesObject(opt_filter)); }; /** - * @param {Array.} features Features. + * @param {ol.filter.Filter=} opt_filter Optional filter. + * @return {Object.} Features. + */ +ol.layer.Vector.prototype.getFeaturesObject = function(opt_filter) { + return this.featureCache_.getFeaturesObject(opt_filter); +}; + + +/** + * @param {Object.} features Features. * @return {Array.} symbolizers for features. */ ol.layer.Vector.prototype.groupFeaturesBySymbolizerLiteral = @@ -222,9 +222,8 @@ ol.layer.Vector.prototype.groupFeaturesBySymbolizerLiteral = var uniqueLiterals = {}, featuresBySymbolizer = [], style = this.style_, - numFeatures = features.length, i, j, l, feature, literals, numLiterals, literal, uniqueLiteral, key; - for (i = 0; i < numFeatures; ++i) { + for (i in features) { feature = features[i]; literals = feature.getSymbolizerLiterals(); if (goog.isNull(literals)) { diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index a7aa2c9251..31a95d3588 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -300,15 +300,16 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = finalCanvas.height = sketchSize.height; var finalContext = this.context_; - var renderedFeatures = {}; + var featuresToRender = {}; var tilesToRender = {}; + var tilesOnSketchCanvas = {}; // TODO make gutter configurable? var tileGutter = 15 * tileResolution; var tile, tileCoord, key, tileState, x, y; // render features by geometry type var filters = this.geometryFilters_, numFilters = filters.length, - i, geomFilter, tileExtent, extentFilter, type, features, + i, geomFilter, tileExtent, extentFilter, type, groups, group, j, numGroups, deferred; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { @@ -327,25 +328,32 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = for (i = 0; i < numFilters; ++i) { geomFilter = filters[i]; type = geomFilter.getType(); - features = layer.getFeatures(new ol.filter.Logical( - [geomFilter, extentFilter], ol.filter.LogicalOperator.AND)); - if (features.length) { - groups = layer.groupFeaturesBySymbolizerLiteral(features); - numGroups = groups.length; - for (j = 0; j < numGroups; ++j) { - group = groups[j]; - deferred = sketchCanvasRenderer.renderFeaturesByGeometryType( - type, group[0], group[1]) || deferred; - } + if (!goog.isDef(featuresToRender[type])) { + featuresToRender[type] = {}; } + goog.object.extend(featuresToRender[type], + layer.getFeaturesObject(new ol.filter.Logical( + [geomFilter, extentFilter], ol.filter.LogicalOperator.AND))); } - if (!deferred) { - tilesToRender[key] = tileCoord; - } + tilesOnSketchCanvas[key] = tileCoord; } } } + for (type in featuresToRender) { + groups = layer.groupFeaturesBySymbolizerLiteral(featuresToRender[type]); + numGroups = groups.length; + for (j = 0; j < numGroups; ++j) { + group = groups[j]; + deferred = sketchCanvasRenderer.renderFeaturesByGeometryType( + /** @type {ol.geom.GeometryType} */ (type), + group[0], group[1]) || deferred; + } + if (!deferred) { + goog.object.extend(tilesToRender, tilesOnSketchCanvas); + } + } + this.dirty_ = true; for (key in tilesToRender) { tileCoord = tilesToRender[key]; From 5752a69d6b4e976a3c962a53f3c7f42b26970151 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 7 Mar 2013 10:09:19 -0700 Subject: [PATCH 157/180] Adding addFeatures back as an API method temporarily Eventually, parseFeatures and addFeatures should be collapsed. --- src/ol/layer/vectorlayer.exports | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ol/layer/vectorlayer.exports b/src/ol/layer/vectorlayer.exports index ff90656cad..9e76b4263b 100644 --- a/src/ol/layer/vectorlayer.exports +++ b/src/ol/layer/vectorlayer.exports @@ -1,3 +1,4 @@ @exportClass ol.layer.Vector ol.layer.LayerOptions +@exportProperty ol.layer.Vector.prototype.addFeatures @exportProperty ol.layer.Vector.prototype.parseFeatures From 5af71a488a3146038e1f5cdecbab06db2047bbbf Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 7 Mar 2013 17:59:39 +0100 Subject: [PATCH 158/180] No need to continue rendering when we need to defer --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 31a95d3588..4e900ca5e7 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -116,7 +116,6 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { */ this.dirty_ = false; - /** * @private * @type {boolean} @@ -309,11 +308,11 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = // render features by geometry type var filters = this.geometryFilters_, numFilters = filters.length, + deferred = false, i, geomFilter, tileExtent, extentFilter, type, - groups, group, j, numGroups, deferred; + groups, group, j, numGroups; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - deferred = false; tileCoord = new ol.TileCoord(z, x, y); key = tileCoord.toString(); if (this.tileCache_.containsKey(key)) { @@ -347,7 +346,10 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = group = groups[j]; deferred = sketchCanvasRenderer.renderFeaturesByGeometryType( /** @type {ol.geom.GeometryType} */ (type), - group[0], group[1]) || deferred; + group[0], group[1]); + if (deferred) { + break; + } } if (!deferred) { goog.object.extend(tilesToRender, tilesOnSketchCanvas); From b28a1efec64cf41e0c6c7ecfbbe2b66f03f59d52 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 7 Mar 2013 10:26:15 -0700 Subject: [PATCH 159/180] Export GeoJSON parser --- src/ol/parser/geojson.exports | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/ol/parser/geojson.exports diff --git a/src/ol/parser/geojson.exports b/src/ol/parser/geojson.exports new file mode 100644 index 0000000000..d7ec119aa5 --- /dev/null +++ b/src/ol/parser/geojson.exports @@ -0,0 +1,3 @@ +@exportSymbol ol.parser.GeoJSON + +@exportProperty ol.parser.GeoJSON.prototype.read From db4e62477d64761ba6e9fe6118f86c054771d912 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 7 Mar 2013 10:37:48 -0700 Subject: [PATCH 160/180] Nicer caps and joins As mentioned in the TODOs, caps and joins should be user configurable on the symbolizers. --- src/ol/renderer/canvas/canvasrenderer.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index c171227cfb..f10c360949 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -139,6 +139,8 @@ ol.renderer.canvas.Renderer.prototype.renderLineStringFeatures_ = context.globalAlpha = symbolizer.opacity; context.strokeStyle = symbolizer.strokeStyle; context.lineWidth = symbolizer.strokeWidth * this.inverseScale_; + context.lineCap = 'round'; // TODO: accept this as a symbolizer property + context.lineJoin = 'round'; // TODO: accept this as a symbolizer property context.beginPath(); for (i = 0, ii = features.length; i < ii; ++i) { geometry = features[i].getGeometry(); @@ -240,6 +242,8 @@ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = if (strokeStyle) { context.strokeStyle = symbolizer.strokeStyle; context.lineWidth = symbolizer.strokeWidth * this.inverseScale_; + context.lineCap = 'round'; // TODO: accept this as a symbolizer property + context.lineJoin = 'round'; // TODO: accept this as a symbolizer property } if (fillStyle) { context.fillStyle = fillStyle; @@ -330,6 +334,8 @@ ol.renderer.canvas.Renderer.renderCircle_ = function(circle) { if (strokeStyle) { context.lineWidth = strokeWidth; context.strokeStyle = strokeStyle; + context.lineCap = 'round'; // TODO: accept this as a symbolizer property + context.lineJoin = 'round'; // TODO: accept this as a symbolizer property } context.beginPath(); From f93bf2ad777a059a17c683997c5790761d541814 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 7 Mar 2013 21:39:34 -0700 Subject: [PATCH 161/180] Avoid filling/stroking parts of multi-polygons twice --- src/ol/renderer/canvas/canvasrenderer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index f10c360949..ced16ffba2 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -231,7 +231,6 @@ ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = */ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = function(features, symbolizer) { - var context = this.context_, strokeStyle = symbolizer.strokeStyle, fillStyle = symbolizer.fillStyle, @@ -287,7 +286,7 @@ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = // scenario 3 - fill and stroke each time context.fill(); context.stroke(); - if (i < ii - 1) { + if (i < ii - 1 || j < jj - 1) { context.beginPath(); } } From 77355ca634654449fb5ebaf588665014196e4ff3 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 7 Mar 2013 22:28:39 -0700 Subject: [PATCH 162/180] Optional polygon stroke or fill --- src/ol/style/polygon.js | 99 +++++++++++++++++++++--------- test/spec/ol/style/polygon.test.js | 15 ++++- 2 files changed, 84 insertions(+), 30 deletions(-) diff --git a/src/ol/style/polygon.js b/src/ol/style/polygon.js index 6fe033f0de..bb0a49a168 100644 --- a/src/ol/style/polygon.js +++ b/src/ol/style/polygon.js @@ -8,9 +8,9 @@ goog.require('ol.style.SymbolizerLiteral'); /** - * @typedef {{fillStyle: (string), - * strokeStyle: (string), - * strokeWidth: (number), + * @typedef {{fillStyle: (string|undefined), + * strokeStyle: (string|undefined), + * strokeWidth: (number|undefined), * opacity: (number)}} */ ol.style.PolygonLiteralOptions; @@ -25,17 +25,30 @@ ol.style.PolygonLiteralOptions; ol.style.PolygonLiteral = function(config) { goog.base(this); - goog.asserts.assertString(config.fillStyle, 'fillStyle must be a string'); - /** @type {string} */ + /** @type {string|undefined} */ this.fillStyle = config.fillStyle; + if (goog.isDef(config.fillStyle)) { + goog.asserts.assertString(config.fillStyle, 'fillStyle must be a string'); + } - goog.asserts.assertString(config.strokeStyle, 'strokeStyle must be a string'); - /** @type {string} */ + /** @type {string|undefined} */ this.strokeStyle = config.strokeStyle; + if (goog.isDef(this.strokeStyle)) { + goog.asserts.assertString( + this.strokeStyle, 'strokeStyle must be a string'); + } - goog.asserts.assertNumber(config.strokeWidth, 'strokeWidth must be a number'); - /** @type {number} */ + /** @type {number|undefined} */ this.strokeWidth = config.strokeWidth; + if (goog.isDef(this.strokeWidth)) { + goog.asserts.assertNumber( + this.strokeWidth, 'strokeWidth must be a number'); + } + + goog.asserts.assert( + goog.isDef(this.fillStyle) || + (goog.isDef(this.strokeStyle) && goog.isDef(this.strokeWidth)), + 'Either fillStyle or strokeStyle and strokeWidth must be set'); goog.asserts.assertNumber(config.opacity, 'opacity must be a number'); /** @type {number} */ @@ -69,28 +82,45 @@ ol.style.Polygon = function(options) { * @type {ol.Expression} * @private */ - this.fillStyle_ = !goog.isDef(options.fillStyle) ? - new ol.ExpressionLiteral(ol.style.PolygonDefaults.fillStyle) : + this.fillStyle_ = !goog.isDefAndNotNull(options.fillStyle) ? + null : (options.fillStyle instanceof ol.Expression) ? options.fillStyle : new ol.ExpressionLiteral(options.fillStyle); - /** - * @type {ol.Expression} - * @private - */ - this.strokeStyle_ = !goog.isDef(options.strokeStyle) ? - new ol.ExpressionLiteral(ol.style.PolygonDefaults.strokeStyle) : - (options.strokeStyle instanceof ol.Expression) ? - options.strokeStyle : new ol.ExpressionLiteral(options.strokeStyle); + // stroke handling - if any stroke property is supplied, use defaults + var strokeStyle = null, + strokeWidth = null; + + if (goog.isDefAndNotNull(options.strokeStyle) || + goog.isDefAndNotNull(options.strokeWidth)) { + + strokeStyle = !goog.isDefAndNotNull(options.strokeStyle) ? + new ol.ExpressionLiteral(ol.style.PolygonDefaults.strokeStyle) : + (options.strokeStyle instanceof ol.Expression) ? + options.strokeStyle : new ol.ExpressionLiteral(options.strokeStyle); + + strokeWidth = !goog.isDef(options.strokeWidth) ? + new ol.ExpressionLiteral(ol.style.PolygonDefaults.strokeWidth) : + (options.strokeWidth instanceof ol.Expression) ? + options.strokeWidth : new ol.ExpressionLiteral(options.strokeWidth); + } /** * @type {ol.Expression} * @private */ - this.strokeWidth_ = !goog.isDef(options.strokeWidth) ? - new ol.ExpressionLiteral(ol.style.PolygonDefaults.strokeWidth) : - (options.strokeWidth instanceof ol.Expression) ? - options.strokeWidth : new ol.ExpressionLiteral(options.strokeWidth); + this.strokeStyle_ = strokeStyle; + + /** + * @type {ol.Expression} + * @private + */ + this.strokeWidth_ = strokeWidth; + + // one of stroke or fill can be null, both null is user error + goog.asserts.assert(!goog.isNull(this.fillStyle_) || + !(goog.isNull(this.strokeStyle_) && goog.isNull(this.strokeWidth_)), + 'Stroke or fill properties must be provided'); /** * @type {ol.Expression} @@ -116,14 +146,25 @@ ol.style.Polygon.prototype.createLiteral = function(opt_feature) { attrs = feature.getAttributes(); } - var fillStyle = this.fillStyle_.evaluate(feature, attrs); - goog.asserts.assertString(fillStyle, 'fillStyle must be a string'); + var fillStyle = goog.isNull(this.fillStyle_) ? + undefined : + /** @type {string} */ (this.fillStyle_.evaluate(feature, attrs)); + goog.asserts.assert(!goog.isDef(fillStyle) || goog.isString(fillStyle)); - var strokeStyle = this.strokeStyle_.evaluate(feature, attrs); - goog.asserts.assertString(strokeStyle, 'strokeStyle must be a string'); + var strokeStyle = goog.isNull(this.strokeStyle_) ? + undefined : + /** @type {string} */ (this.strokeStyle_.evaluate(feature, attrs)); + goog.asserts.assert(!goog.isDef(strokeStyle) || goog.isString(strokeStyle)); - var strokeWidth = this.strokeWidth_.evaluate(feature, attrs); - goog.asserts.assertNumber(strokeWidth, 'strokeWidth must be a number'); + var strokeWidth = goog.isNull(this.strokeWidth_) ? + undefined : + /** @type {number} */ (this.strokeWidth_.evaluate(feature, attrs)); + goog.asserts.assert(!goog.isDef(strokeWidth) || goog.isNumber(strokeWidth)); + + goog.asserts.assert( + goog.isDef(fillStyle) || + (goog.isDef(strokeStyle) && goog.isDef(strokeWidth)), + 'either fill style or strokeStyle and strokeWidth must be defined'); var opacity = this.opacity_.evaluate(feature, attrs); goog.asserts.assertNumber(opacity, 'opacity must be a number'); diff --git a/test/spec/ol/style/polygon.test.js b/test/spec/ol/style/polygon.test.js index 4c8d443db3..34a0cdaf62 100644 --- a/test/spec/ol/style/polygon.test.js +++ b/test/spec/ol/style/polygon.test.js @@ -46,7 +46,7 @@ describe('ol.style.Polygon', function() { it('accepts expressions', function() { var symbolizer = new ol.style.Polygon({ opacity: new ol.Expression('value / 100'), - fillStyle: ol.Expression('fillAttr') + fillStyle: new ol.Expression('fillAttr') }); expect(symbolizer).toBeA(ol.style.Polygon); }); @@ -70,6 +70,19 @@ describe('ol.style.Polygon', function() { expect(literal).toBeA(ol.style.PolygonLiteral); expect(literal.opacity).toBe(42 / 100); expect(literal.fillStyle).toBe('#ff0000'); + expect(literal.strokeStyle).toBeUndefined(); + }); + + it('applies default strokeWidth if only strokeStyle is given', function() { + var symbolizer = new ol.style.Polygon({ + strokeStyle: '#ff0000' + }); + + var literal = symbolizer.createLiteral(); + expect(literal).toBeA(ol.style.PolygonLiteral); + expect(literal.strokeStyle).toBe('#ff0000'); + expect(literal.strokeWidth).toBe(1.5); + expect(literal.fillStyle).toBeUndefined(); }); }); From 5ce114c1b6537607d497a2e2b5e4998e19165e0a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 7 Mar 2013 22:29:00 -0700 Subject: [PATCH 163/180] Style in example --- examples/vector-layer.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/examples/vector-layer.js b/examples/vector-layer.js index 303dd4829a..0204accb60 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -12,6 +12,9 @@ goog.require('ol.parser.GeoJSON'); goog.require('ol.projection'); goog.require('ol.source.MapQuestOpenAerial'); goog.require('ol.source.Vector'); +goog.require('ol.style.Polygon'); +goog.require('ol.style.Rule'); +goog.require('ol.style.Style'); var raster = new ol.layer.TileLayer({ @@ -21,7 +24,18 @@ var raster = new ol.layer.TileLayer({ var vector = new ol.layer.Vector({ source: new ol.source.Vector({ projection: ol.projection.getFromCode('EPSG:4326') - }) + }), + style: new ol.style.Style({rules: [ + new ol.style.Rule({ + symbolizers: [ + new ol.style.Polygon({ + strokeStyle: '#696969', + strokeWidth: 1, + opacity: 1.5 + }) + ] + }) + ]}) }); var geojson = new ol.parser.GeoJSON(); From faee18bae1731f1e51772cb1eca437288af23677 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 7 Mar 2013 22:51:14 -0700 Subject: [PATCH 164/180] Use fillColor and strokeColor instead of fillStyle and strokeStyle The color names are more intuitive. And if we want to support pattern strokes or fills, we'll need additional proerties to represent other pattern properties. --- src/objectliterals.exports | 10 +-- src/ol/renderer/canvas/canvasrenderer.js | 36 +++++----- src/ol/style/line.js | 24 +++---- src/ol/style/polygon.js | 76 ++++++++++----------- src/ol/style/shape.js | 84 ++++++++++++------------ test/spec/ol/layer/vectorlayer.test.js | 10 +-- test/spec/ol/style/line.test.js | 8 +-- test/spec/ol/style/polygon.test.js | 30 ++++----- test/spec/ol/style/shape.test.js | 28 ++++---- test/spec/ol/style/style.test.js | 4 +- 10 files changed, 156 insertions(+), 154 deletions(-) diff --git a/src/objectliterals.exports b/src/objectliterals.exports index dae5ee0510..3d793c1f25 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -151,13 +151,13 @@ @exportObjectLiteralProperty ol.style.IconOptions.rotation number|ol.Expression|undefined @exportObjectLiteral ol.style.LineOptions -@exportObjectLiteralProperty ol.style.LineOptions.strokeStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.LineOptions.strokeColor string|ol.Expression|undefined @exportObjectLiteralProperty ol.style.LineOptions.strokeWidth number|ol.Expression|undefined @exportObjectLiteralProperty ol.style.LineOptions.opacity number|ol.Expression|undefined @exportObjectLiteral ol.style.PolygonOptions -@exportObjectLiteralProperty ol.style.PolygonOptions.fillStyle string|ol.Expression|undefined -@exportObjectLiteralProperty ol.style.PolygonOptions.strokeStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.PolygonOptions.fillColor string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.PolygonOptions.strokeColor string|ol.Expression|undefined @exportObjectLiteralProperty ol.style.PolygonOptions.strokeWidth number|ol.Expression|undefined @exportObjectLiteralProperty ol.style.PolygonOptions.opacity number|ol.Expression|undefined @@ -168,8 +168,8 @@ @exportObjectLiteral ol.style.ShapeOptions @exportObjectLiteralProperty ol.style.ShapeOptions.type ol.style.ShapeType|undefined @exportObjectLiteralProperty ol.style.ShapeOptions.size number|ol.Expression|undefined -@exportObjectLiteralProperty ol.style.ShapeOptions.fillStyle string|ol.Expression|undefined -@exportObjectLiteralProperty ol.style.ShapeOptions.strokeStyle string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.fillColor string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.strokeColor string|ol.Expression|undefined @exportObjectLiteralProperty ol.style.ShapeOptions.strokeWidth number|ol.Expression|undefined @exportObjectLiteralProperty ol.style.ShapeOptions.opacity number|ol.Expression|undefined diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index ced16ffba2..4899aa5e18 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -137,7 +137,7 @@ ol.renderer.canvas.Renderer.prototype.renderLineStringFeatures_ = i, ii, geometry, components, j, jj, line, dim, k, kk, x, y; context.globalAlpha = symbolizer.opacity; - context.strokeStyle = symbolizer.strokeStyle; + context.strokeStyle = symbolizer.strokeColor; context.lineWidth = symbolizer.strokeWidth * this.inverseScale_; context.lineCap = 'round'; // TODO: accept this as a symbolizer property context.lineJoin = 'round'; // TODO: accept this as a symbolizer property @@ -232,20 +232,20 @@ ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = function(features, symbolizer) { var context = this.context_, - strokeStyle = symbolizer.strokeStyle, - fillStyle = symbolizer.fillStyle, + strokeColor = symbolizer.strokeColor, + fillColor = symbolizer.fillColor, i, ii, geometry, components, j, jj, poly, rings, numRings, ring, dim, k, kk, x, y; context.globalAlpha = symbolizer.opacity; - if (strokeStyle) { - context.strokeStyle = symbolizer.strokeStyle; + if (strokeColor) { + context.strokeStyle = symbolizer.strokeColor; context.lineWidth = symbolizer.strokeWidth * this.inverseScale_; context.lineCap = 'round'; // TODO: accept this as a symbolizer property context.lineJoin = 'round'; // TODO: accept this as a symbolizer property } - if (fillStyle) { - context.fillStyle = fillStyle; + if (fillColor) { + context.fillStyle = fillColor; } /** @@ -282,7 +282,7 @@ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = context.lineTo(x, y); } } - if (fillStyle && strokeStyle) { + if (fillColor && strokeColor) { // scenario 3 - fill and stroke each time context.fill(); context.stroke(); @@ -293,8 +293,8 @@ ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = } } } - if (!(fillStyle && strokeStyle)) { - if (fillStyle) { + if (!(fillColor && strokeColor)) { + if (fillColor) { // scenario 2 - fill all at once context.fill(); } else { @@ -318,8 +318,8 @@ ol.renderer.canvas.Renderer.renderCircle_ = function(circle) { (goog.dom.createElement(goog.dom.TagName.CANVAS)), context = /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')), - fillStyle = circle.fillStyle, - strokeStyle = circle.strokeStyle, + fillColor = circle.fillColor, + strokeColor = circle.strokeColor, twoPi = Math.PI * 2; canvas.height = size; @@ -327,12 +327,12 @@ ol.renderer.canvas.Renderer.renderCircle_ = function(circle) { context.globalAlpha = circle.opacity; - if (fillStyle) { - context.fillStyle = fillStyle; + if (fillColor) { + context.fillStyle = fillColor; } - if (strokeStyle) { + if (strokeColor) { context.lineWidth = strokeWidth; - context.strokeStyle = strokeStyle; + context.strokeStyle = strokeColor; context.lineCap = 'round'; // TODO: accept this as a symbolizer property context.lineJoin = 'round'; // TODO: accept this as a symbolizer property } @@ -340,10 +340,10 @@ ol.renderer.canvas.Renderer.renderCircle_ = function(circle) { context.beginPath(); context.arc(mid, mid, circle.size / 2, 0, twoPi, true); - if (fillStyle) { + if (fillColor) { context.fill(); } - if (strokeStyle) { + if (strokeColor) { context.stroke(); } return canvas; diff --git a/src/ol/style/line.js b/src/ol/style/line.js index c9ae263b6d..340181a256 100644 --- a/src/ol/style/line.js +++ b/src/ol/style/line.js @@ -8,7 +8,7 @@ goog.require('ol.style.SymbolizerLiteral'); /** - * @typedef {{strokeStyle: (string), + * @typedef {{strokeColor: (string), * strokeWidth: (number), * opacity: (number)}} */ @@ -24,9 +24,9 @@ ol.style.LineLiteralOptions; ol.style.LineLiteral = function(config) { goog.base(this); - goog.asserts.assertString(config.strokeStyle, 'strokeStyle must be a string'); + goog.asserts.assertString(config.strokeColor, 'strokeColor must be a string'); /** @type {string} */ - this.strokeStyle = config.strokeStyle; + this.strokeColor = config.strokeColor; goog.asserts.assertNumber(config.strokeWidth, 'strokeWidth must be a number'); /** @type {number} */ @@ -44,7 +44,7 @@ goog.inherits(ol.style.LineLiteral, ol.style.SymbolizerLiteral); * @inheritDoc */ ol.style.LineLiteral.prototype.equals = function(lineLiteral) { - return this.strokeStyle == lineLiteral.strokeStyle && + return this.strokeColor == lineLiteral.strokeColor && this.strokeWidth == lineLiteral.strokeWidth && this.opacity == lineLiteral.opacity; }; @@ -63,10 +63,10 @@ ol.style.Line = function(options) { * @type {ol.Expression} * @private */ - this.strokeStyle_ = !goog.isDef(options.strokeStyle) ? - new ol.ExpressionLiteral(ol.style.LineDefaults.strokeStyle) : - (options.strokeStyle instanceof ol.Expression) ? - options.strokeStyle : new ol.ExpressionLiteral(options.strokeStyle); + this.strokeColor_ = !goog.isDef(options.strokeColor) ? + new ol.ExpressionLiteral(ol.style.LineDefaults.strokeColor) : + (options.strokeColor instanceof ol.Expression) ? + options.strokeColor : new ol.ExpressionLiteral(options.strokeColor); /** * @type {ol.Expression} @@ -101,8 +101,8 @@ ol.style.Line.prototype.createLiteral = function(opt_feature) { attrs = feature.getAttributes(); } - var strokeStyle = this.strokeStyle_.evaluate(feature, attrs); - goog.asserts.assertString(strokeStyle, 'strokeStyle must be a string'); + var strokeColor = this.strokeColor_.evaluate(feature, attrs); + goog.asserts.assertString(strokeColor, 'strokeColor must be a string'); var strokeWidth = this.strokeWidth_.evaluate(feature, attrs); goog.asserts.assertNumber(strokeWidth, 'strokeWidth must be a number'); @@ -111,7 +111,7 @@ ol.style.Line.prototype.createLiteral = function(opt_feature) { goog.asserts.assertNumber(opacity, 'opacity must be a number'); return new ol.style.LineLiteral({ - strokeStyle: strokeStyle, + strokeColor: strokeColor, strokeWidth: strokeWidth, opacity: opacity }); @@ -122,7 +122,7 @@ ol.style.Line.prototype.createLiteral = function(opt_feature) { * @type {ol.style.LineLiteral} */ ol.style.LineDefaults = new ol.style.LineLiteral({ - strokeStyle: '#696969', + strokeColor: '#696969', strokeWidth: 1.5, opacity: 0.75 }); diff --git a/src/ol/style/polygon.js b/src/ol/style/polygon.js index bb0a49a168..ef2a017ec1 100644 --- a/src/ol/style/polygon.js +++ b/src/ol/style/polygon.js @@ -8,8 +8,8 @@ goog.require('ol.style.SymbolizerLiteral'); /** - * @typedef {{fillStyle: (string|undefined), - * strokeStyle: (string|undefined), + * @typedef {{fillColor: (string|undefined), + * strokeColor: (string|undefined), * strokeWidth: (number|undefined), * opacity: (number)}} */ @@ -26,16 +26,16 @@ ol.style.PolygonLiteral = function(config) { goog.base(this); /** @type {string|undefined} */ - this.fillStyle = config.fillStyle; - if (goog.isDef(config.fillStyle)) { - goog.asserts.assertString(config.fillStyle, 'fillStyle must be a string'); + this.fillColor = config.fillColor; + if (goog.isDef(config.fillColor)) { + goog.asserts.assertString(config.fillColor, 'fillColor must be a string'); } /** @type {string|undefined} */ - this.strokeStyle = config.strokeStyle; - if (goog.isDef(this.strokeStyle)) { + this.strokeColor = config.strokeColor; + if (goog.isDef(this.strokeColor)) { goog.asserts.assertString( - this.strokeStyle, 'strokeStyle must be a string'); + this.strokeColor, 'strokeColor must be a string'); } /** @type {number|undefined} */ @@ -46,9 +46,9 @@ ol.style.PolygonLiteral = function(config) { } goog.asserts.assert( - goog.isDef(this.fillStyle) || - (goog.isDef(this.strokeStyle) && goog.isDef(this.strokeWidth)), - 'Either fillStyle or strokeStyle and strokeWidth must be set'); + goog.isDef(this.fillColor) || + (goog.isDef(this.strokeColor) && goog.isDef(this.strokeWidth)), + 'Either fillColor or strokeColor and strokeWidth must be set'); goog.asserts.assertNumber(config.opacity, 'opacity must be a number'); /** @type {number} */ @@ -62,8 +62,8 @@ goog.inherits(ol.style.PolygonLiteral, ol.style.SymbolizerLiteral); * @inheritDoc */ ol.style.PolygonLiteral.prototype.equals = function(polygonLiteral) { - return this.fillStyle == polygonLiteral.fillStyle && - this.strokeStyle == polygonLiteral.strokeStyle && + return this.fillColor == polygonLiteral.fillColor && + this.strokeColor == polygonLiteral.strokeColor && this.strokeWidth == polygonLiteral.strokeWidth && this.opacity == polygonLiteral.opacity; }; @@ -82,22 +82,22 @@ ol.style.Polygon = function(options) { * @type {ol.Expression} * @private */ - this.fillStyle_ = !goog.isDefAndNotNull(options.fillStyle) ? + this.fillColor_ = !goog.isDefAndNotNull(options.fillColor) ? null : - (options.fillStyle instanceof ol.Expression) ? - options.fillStyle : new ol.ExpressionLiteral(options.fillStyle); + (options.fillColor instanceof ol.Expression) ? + options.fillColor : new ol.ExpressionLiteral(options.fillColor); // stroke handling - if any stroke property is supplied, use defaults - var strokeStyle = null, + var strokeColor = null, strokeWidth = null; - if (goog.isDefAndNotNull(options.strokeStyle) || + if (goog.isDefAndNotNull(options.strokeColor) || goog.isDefAndNotNull(options.strokeWidth)) { - strokeStyle = !goog.isDefAndNotNull(options.strokeStyle) ? - new ol.ExpressionLiteral(ol.style.PolygonDefaults.strokeStyle) : - (options.strokeStyle instanceof ol.Expression) ? - options.strokeStyle : new ol.ExpressionLiteral(options.strokeStyle); + strokeColor = !goog.isDefAndNotNull(options.strokeColor) ? + new ol.ExpressionLiteral(ol.style.PolygonDefaults.strokeColor) : + (options.strokeColor instanceof ol.Expression) ? + options.strokeColor : new ol.ExpressionLiteral(options.strokeColor); strokeWidth = !goog.isDef(options.strokeWidth) ? new ol.ExpressionLiteral(ol.style.PolygonDefaults.strokeWidth) : @@ -109,7 +109,7 @@ ol.style.Polygon = function(options) { * @type {ol.Expression} * @private */ - this.strokeStyle_ = strokeStyle; + this.strokeColor_ = strokeColor; /** * @type {ol.Expression} @@ -118,8 +118,8 @@ ol.style.Polygon = function(options) { this.strokeWidth_ = strokeWidth; // one of stroke or fill can be null, both null is user error - goog.asserts.assert(!goog.isNull(this.fillStyle_) || - !(goog.isNull(this.strokeStyle_) && goog.isNull(this.strokeWidth_)), + goog.asserts.assert(!goog.isNull(this.fillColor_) || + !(goog.isNull(this.strokeColor_) && goog.isNull(this.strokeWidth_)), 'Stroke or fill properties must be provided'); /** @@ -146,15 +146,15 @@ ol.style.Polygon.prototype.createLiteral = function(opt_feature) { attrs = feature.getAttributes(); } - var fillStyle = goog.isNull(this.fillStyle_) ? + var fillColor = goog.isNull(this.fillColor_) ? undefined : - /** @type {string} */ (this.fillStyle_.evaluate(feature, attrs)); - goog.asserts.assert(!goog.isDef(fillStyle) || goog.isString(fillStyle)); + /** @type {string} */ (this.fillColor_.evaluate(feature, attrs)); + goog.asserts.assert(!goog.isDef(fillColor) || goog.isString(fillColor)); - var strokeStyle = goog.isNull(this.strokeStyle_) ? + var strokeColor = goog.isNull(this.strokeColor_) ? undefined : - /** @type {string} */ (this.strokeStyle_.evaluate(feature, attrs)); - goog.asserts.assert(!goog.isDef(strokeStyle) || goog.isString(strokeStyle)); + /** @type {string} */ (this.strokeColor_.evaluate(feature, attrs)); + goog.asserts.assert(!goog.isDef(strokeColor) || goog.isString(strokeColor)); var strokeWidth = goog.isNull(this.strokeWidth_) ? undefined : @@ -162,16 +162,16 @@ ol.style.Polygon.prototype.createLiteral = function(opt_feature) { goog.asserts.assert(!goog.isDef(strokeWidth) || goog.isNumber(strokeWidth)); goog.asserts.assert( - goog.isDef(fillStyle) || - (goog.isDef(strokeStyle) && goog.isDef(strokeWidth)), - 'either fill style or strokeStyle and strokeWidth must be defined'); + goog.isDef(fillColor) || + (goog.isDef(strokeColor) && goog.isDef(strokeWidth)), + 'either fill style or strokeColor and strokeWidth must be defined'); var opacity = this.opacity_.evaluate(feature, attrs); goog.asserts.assertNumber(opacity, 'opacity must be a number'); return new ol.style.PolygonLiteral({ - fillStyle: fillStyle, - strokeStyle: strokeStyle, + fillColor: fillColor, + strokeColor: strokeColor, strokeWidth: strokeWidth, opacity: opacity }); @@ -182,8 +182,8 @@ ol.style.Polygon.prototype.createLiteral = function(opt_feature) { * @type {ol.style.PolygonLiteral} */ ol.style.PolygonDefaults = new ol.style.PolygonLiteral({ - fillStyle: '#ffffff', - strokeStyle: '#696969', + fillColor: '#ffffff', + strokeColor: '#696969', strokeWidth: 1.5, opacity: 0.75 }); diff --git a/src/ol/style/shape.js b/src/ol/style/shape.js index bb15a0665b..d0af55a4d1 100644 --- a/src/ol/style/shape.js +++ b/src/ol/style/shape.js @@ -19,8 +19,8 @@ ol.style.ShapeType = { /** * @typedef {{type: (ol.style.ShapeType), * size: (number), - * fillStyle: (string|undefined), - * strokeStyle: (string|undefined), + * fillColor: (string|undefined), + * strokeColor: (string|undefined), * strokeWidth: (number|undefined), * opacity: (number)}} */ @@ -44,16 +44,16 @@ ol.style.ShapeLiteral = function(config) { this.size = config.size; /** @type {string|undefined} */ - this.fillStyle = config.fillStyle; - if (goog.isDef(config.fillStyle)) { - goog.asserts.assertString(config.fillStyle, 'fillStyle must be a string'); + this.fillColor = config.fillColor; + if (goog.isDef(config.fillColor)) { + goog.asserts.assertString(config.fillColor, 'fillColor must be a string'); } /** @type {string|undefined} */ - this.strokeStyle = config.strokeStyle; - if (goog.isDef(this.strokeStyle)) { + this.strokeColor = config.strokeColor; + if (goog.isDef(this.strokeColor)) { goog.asserts.assertString( - this.strokeStyle, 'strokeStyle must be a string'); + this.strokeColor, 'strokeColor must be a string'); } /** @type {number|undefined} */ @@ -64,9 +64,9 @@ ol.style.ShapeLiteral = function(config) { } goog.asserts.assert( - goog.isDef(this.fillStyle) || - (goog.isDef(this.strokeStyle) && goog.isDef(this.strokeWidth)), - 'Either fillStyle or strokeStyle and strokeWidth must be set'); + goog.isDef(this.fillColor) || + (goog.isDef(this.strokeColor) && goog.isDef(this.strokeWidth)), + 'Either fillColor or strokeColor and strokeWidth must be set'); goog.asserts.assertNumber(config.opacity, 'opacity must be a number'); /** @type {number} */ @@ -82,8 +82,8 @@ goog.inherits(ol.style.ShapeLiteral, ol.style.PointLiteral); ol.style.ShapeLiteral.prototype.equals = function(shapeLiteral) { return this.type == shapeLiteral.type && this.size == shapeLiteral.size && - this.fillStyle == shapeLiteral.fillStyle && - this.strokeStyle == shapeLiteral.strokeStyle && + this.fillColor == shapeLiteral.fillColor && + this.strokeColor == shapeLiteral.strokeColor && this.strokeWidth == shapeLiteral.strokeWidth && this.opacity == shapeLiteral.opacity; }; @@ -117,22 +117,22 @@ ol.style.Shape = function(options) { * @type {ol.Expression} * @private */ - this.fillStyle_ = !goog.isDefAndNotNull(options.fillStyle) ? + this.fillColor_ = !goog.isDefAndNotNull(options.fillColor) ? null : - (options.fillStyle instanceof ol.Expression) ? - options.fillStyle : new ol.ExpressionLiteral(options.fillStyle); + (options.fillColor instanceof ol.Expression) ? + options.fillColor : new ol.ExpressionLiteral(options.fillColor); // stroke handling - if any stroke property is supplied, use defaults - var strokeStyle = null, + var strokeColor = null, strokeWidth = null; - if (goog.isDefAndNotNull(options.strokeStyle) || + if (goog.isDefAndNotNull(options.strokeColor) || goog.isDefAndNotNull(options.strokeWidth)) { - strokeStyle = !goog.isDefAndNotNull(options.strokeStyle) ? - new ol.ExpressionLiteral(ol.style.ShapeDefaults.strokeStyle) : - (options.strokeStyle instanceof ol.Expression) ? - options.strokeStyle : new ol.ExpressionLiteral(options.strokeStyle); + strokeColor = !goog.isDefAndNotNull(options.strokeColor) ? + new ol.ExpressionLiteral(ol.style.ShapeDefaults.strokeColor) : + (options.strokeColor instanceof ol.Expression) ? + options.strokeColor : new ol.ExpressionLiteral(options.strokeColor); strokeWidth = !goog.isDef(options.strokeWidth) ? new ol.ExpressionLiteral(ol.style.ShapeDefaults.strokeWidth) : @@ -144,7 +144,7 @@ ol.style.Shape = function(options) { * @type {ol.Expression} * @private */ - this.strokeStyle_ = strokeStyle; + this.strokeColor_ = strokeColor; /** * @type {ol.Expression} @@ -153,8 +153,8 @@ ol.style.Shape = function(options) { this.strokeWidth_ = strokeWidth; // one of stroke or fill can be null, both null is user error - goog.asserts.assert(!goog.isNull(this.fillStyle_) || - !(goog.isNull(this.strokeStyle_) && goog.isNull(this.strokeWidth_)), + goog.asserts.assert(!goog.isNull(this.fillColor_) || + !(goog.isNull(this.strokeColor_) && goog.isNull(this.strokeWidth_)), 'Stroke or fill properties must be provided'); /** @@ -183,22 +183,25 @@ ol.style.Shape.prototype.createLiteral = function(opt_feature) { var size = this.size_.evaluate(feature, attrs); goog.asserts.assertNumber(size, 'size must be a number'); - var fillStyle = goog.isNull(this.fillStyle_) ? - undefined : this.fillStyle_.evaluate(feature, attrs); - goog.asserts.assert(!goog.isDef(fillStyle) || goog.isString(fillStyle)); + var fillColor = goog.isNull(this.fillColor_) ? + undefined : + /** @type {string} */ (this.fillColor_.evaluate(feature, attrs)); + goog.asserts.assert(!goog.isDef(fillColor) || goog.isString(fillColor)); - var strokeStyle = goog.isNull(this.strokeStyle_) ? - undefined : this.strokeStyle_.evaluate(feature, attrs); - goog.asserts.assert(!goog.isDef(strokeStyle) || goog.isString(strokeStyle)); + var strokeColor = goog.isNull(this.strokeColor_) ? + undefined : + /** @type {string} */ (this.strokeColor_.evaluate(feature, attrs)); + goog.asserts.assert(!goog.isDef(strokeColor) || goog.isString(strokeColor)); var strokeWidth = goog.isNull(this.strokeWidth_) ? - undefined : this.strokeWidth_.evaluate(feature, attrs); + undefined : + /** @type {number} */ (this.strokeWidth_.evaluate(feature, attrs)); goog.asserts.assert(!goog.isDef(strokeWidth) || goog.isNumber(strokeWidth)); goog.asserts.assert( - goog.isDef(fillStyle) || - (goog.isDef(strokeStyle) && goog.isDef(strokeWidth)), - 'either fill style or strokeStyle and strokeWidth must be defined'); + goog.isDef(fillColor) || + (goog.isDef(strokeColor) && goog.isDef(strokeWidth)), + 'either fill style or strokeColor and strokeWidth must be defined'); var opacity = this.opacity_.evaluate(feature, attrs); goog.asserts.assertNumber(opacity, 'opacity must be a number'); @@ -206,10 +209,9 @@ ol.style.Shape.prototype.createLiteral = function(opt_feature) { return new ol.style.ShapeLiteral({ type: this.type_, size: size, - // TODO: check if typecast can be avoided here - fillStyle: /** @type {string|undefined} */ (fillStyle), - strokeStyle: /** @type {string|undefined} */ (strokeStyle), - strokeWidth: /** @type {number|undefined} */ (strokeWidth), + fillColor: fillColor, + strokeColor: strokeColor, + strokeWidth: strokeWidth, opacity: opacity }); }; @@ -221,8 +223,8 @@ ol.style.Shape.prototype.createLiteral = function(opt_feature) { ol.style.ShapeDefaults = new ol.style.ShapeLiteral({ type: ol.style.ShapeType.CIRCLE, size: 5, - fillStyle: '#ffffff', - strokeStyle: '#696969', + fillColor: '#ffffff', + strokeColor: '#696969', strokeWidth: 1.5, opacity: 0.75 }); diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js index f63402c935..40640fdc07 100644 --- a/test/spec/ol/layer/vectorlayer.test.js +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -107,7 +107,7 @@ describe('ol.layer.Vector', function() { symbolizers: [ new ol.style.Line({ strokeWidth: 2, - strokeStyle: new ol.Expression('colorProperty'), + strokeColor: new ol.Expression('colorProperty'), opacity: 1 }) ] @@ -136,20 +136,20 @@ describe('ol.layer.Vector', function() { var groups = layer.groupFeaturesBySymbolizerLiteral(features); expect(groups.length).toBe(2); expect(groups[0][0].length).toBe(1); - expect(groups[0][1].strokeStyle).toBe('#BADA55'); + expect(groups[0][1].strokeColor).toBe('#BADA55'); expect(groups[1][0].length).toBe(2); - expect(groups[1][1].strokeStyle).toBe('#013'); + expect(groups[1][1].strokeColor).toBe('#013'); }); it('groups equal symbolizers also when defined on features', function() { var symbolizer = new ol.style.Line({ strokeWidth: 3, - strokeStyle: new ol.Expression('colorProperty'), + strokeColor: new ol.Expression('colorProperty'), opacity: 1 }); var anotherSymbolizer = new ol.style.Line({ strokeWidth: 3, - strokeStyle: '#BADA55', + strokeColor: '#BADA55', opacity: 1 }); var featureWithSymbolizers = new ol.Feature({ diff --git a/test/spec/ol/style/line.test.js b/test/spec/ol/style/line.test.js index 95784ab15f..4100652a7d 100644 --- a/test/spec/ol/style/line.test.js +++ b/test/spec/ol/style/line.test.js @@ -7,16 +7,16 @@ describe('ol.style.LineLiteral', function() { it('identifies equal literals', function() { var literal = new ol.style.LineLiteral({ strokeWidth: 3, - strokeStyle: '#BADA55', + strokeColor: '#BADA55', opacity: 1 }); var equalLiteral = new ol.style.LineLiteral({ - strokeStyle: '#BADA55', + strokeColor: '#BADA55', strokeWidth: 3, opacity: 1 }); var differentLiteral = new ol.style.LineLiteral({ - strokeStyle: '#013', + strokeColor: '#013', strokeWidth: 3, opacity: 1 }); @@ -34,7 +34,7 @@ describe('ol.style.Line', function() { it('accepts literal values', function() { var symbolizer = new ol.style.Line({ - strokeStyle: '#BADA55', + strokeColor: '#BADA55', strokeWidth: 3 }); expect(symbolizer).toBeA(ol.style.Line); diff --git a/test/spec/ol/style/polygon.test.js b/test/spec/ol/style/polygon.test.js index 34a0cdaf62..8859fe4780 100644 --- a/test/spec/ol/style/polygon.test.js +++ b/test/spec/ol/style/polygon.test.js @@ -7,19 +7,19 @@ describe('ol.style.PolygonLiteral', function() { it('identifies equal literals', function() { var literal = new ol.style.PolygonLiteral({ strokeWidth: 3, - strokeStyle: '#013', - fillStyle: '#BADA55', + strokeColor: '#013', + fillColor: '#BADA55', opacity: 1 }); var equalLiteral = new ol.style.PolygonLiteral({ - fillStyle: '#BADA55', - strokeStyle: '#013', + fillColor: '#BADA55', + strokeColor: '#013', strokeWidth: 3, opacity: 1 }); var differentLiteral = new ol.style.PolygonLiteral({ - fillStyle: '#013', - strokeStyle: '#013', + fillColor: '#013', + strokeColor: '#013', strokeWidth: 3, opacity: 1 }); @@ -37,7 +37,7 @@ describe('ol.style.Polygon', function() { it('accepts literal values', function() { var symbolizer = new ol.style.Polygon({ - fillStyle: '#BADA55', + fillColor: '#BADA55', strokeWidth: 3 }); expect(symbolizer).toBeA(ol.style.Polygon); @@ -46,7 +46,7 @@ describe('ol.style.Polygon', function() { it('accepts expressions', function() { var symbolizer = new ol.style.Polygon({ opacity: new ol.Expression('value / 100'), - fillStyle: new ol.Expression('fillAttr') + fillColor: new ol.Expression('fillAttr') }); expect(symbolizer).toBeA(ol.style.Polygon); }); @@ -58,7 +58,7 @@ describe('ol.style.Polygon', function() { it('evaluates expressions with the given feature', function() { var symbolizer = new ol.style.Polygon({ opacity: new ol.Expression('value / 100'), - fillStyle: new ol.Expression('fillAttr') + fillColor: new ol.Expression('fillAttr') }); var feature = new ol.Feature({ @@ -69,20 +69,20 @@ describe('ol.style.Polygon', function() { var literal = symbolizer.createLiteral(feature); expect(literal).toBeA(ol.style.PolygonLiteral); expect(literal.opacity).toBe(42 / 100); - expect(literal.fillStyle).toBe('#ff0000'); - expect(literal.strokeStyle).toBeUndefined(); + expect(literal.fillColor).toBe('#ff0000'); + expect(literal.strokeColor).toBeUndefined(); }); - it('applies default strokeWidth if only strokeStyle is given', function() { + it('applies default strokeWidth if only strokeColor is given', function() { var symbolizer = new ol.style.Polygon({ - strokeStyle: '#ff0000' + strokeColor: '#ff0000' }); var literal = symbolizer.createLiteral(); expect(literal).toBeA(ol.style.PolygonLiteral); - expect(literal.strokeStyle).toBe('#ff0000'); + expect(literal.strokeColor).toBe('#ff0000'); expect(literal.strokeWidth).toBe(1.5); - expect(literal.fillStyle).toBeUndefined(); + expect(literal.fillColor).toBeUndefined(); }); }); diff --git a/test/spec/ol/style/shape.test.js b/test/spec/ol/style/shape.test.js index 147528df0c..dfd8c8c377 100644 --- a/test/spec/ol/style/shape.test.js +++ b/test/spec/ol/style/shape.test.js @@ -8,24 +8,24 @@ describe('ol.style.ShapeLiteral', function() { var literal = new ol.style.ShapeLiteral({ type: ol.style.ShapeType.CIRCLE, size: 4, - fillStyle: '#BADA55', - strokeStyle: '#013', + fillColor: '#BADA55', + strokeColor: '#013', strokeWidth: 3, opacity: 1 }); var equalLiteral = new ol.style.ShapeLiteral({ type: ol.style.ShapeType.CIRCLE, size: 4, - fillStyle: '#BADA55', - strokeStyle: '#013', + fillColor: '#BADA55', + strokeColor: '#013', strokeWidth: 3, opacity: 1 }); var differentLiteral = new ol.style.ShapeLiteral({ type: ol.style.ShapeType.CIRCLE, size: 4, - fillStyle: '#013', - strokeStyle: '#013', + fillColor: '#013', + strokeColor: '#013', strokeWidth: 3, opacity: 1 }); @@ -44,7 +44,7 @@ describe('ol.style.Shape', function() { it('accepts literal values', function() { var symbolizer = new ol.style.Shape({ size: 4, - fillStyle: '#BADA55' + fillColor: '#BADA55' }); expect(symbolizer).toBeA(ol.style.Shape); }); @@ -52,7 +52,7 @@ describe('ol.style.Shape', function() { it('accepts expressions', function() { var symbolizer = new ol.style.Shape({ size: new ol.Expression('sizeAttr'), - strokeStyle: new ol.Expression('color') + strokeColor: new ol.Expression('color') }); expect(symbolizer).toBeA(ol.style.Shape); }); @@ -65,7 +65,7 @@ describe('ol.style.Shape', function() { var symbolizer = new ol.style.Shape({ size: new ol.Expression('sizeAttr'), opacity: new ol.Expression('opacityAttr'), - fillStyle: '#BADA55' + fillColor: '#BADA55' }); var feature = new ol.Feature({ @@ -83,8 +83,8 @@ describe('ol.style.Shape', function() { var symbolizer = new ol.style.Shape({ size: 10, opacity: 1, - fillStyle: '#BADA55', - strokeStyle: '#013', + fillColor: '#BADA55', + strokeColor: '#013', strokeWidth: 2 }); @@ -92,8 +92,8 @@ describe('ol.style.Shape', function() { expect(literal).toBeA(ol.style.ShapeLiteral); expect(literal.size).toBe(10); expect(literal.opacity).toBe(1); - expect(literal.fillStyle).toBe('#BADA55'); - expect(literal.strokeStyle).toBe('#013'); + expect(literal.fillColor).toBe('#BADA55'); + expect(literal.strokeColor).toBe('#013'); expect(literal.strokeWidth).toBe(2); }); @@ -101,7 +101,7 @@ describe('ol.style.Shape', function() { var symbolizer = new ol.style.Shape({ size: new ol.Expression('sizeAttr'), opacity: new ol.Expression('opacityAttr'), - fillStyle: '#BADA55' + fillColor: '#BADA55' }); var feature = new ol.Feature({ diff --git a/test/spec/ol/style/style.test.js b/test/spec/ol/style/style.test.js index 9b76d23b60..599982e39d 100644 --- a/test/spec/ol/style/style.test.js +++ b/test/spec/ol/style/style.test.js @@ -15,7 +15,7 @@ describe('ol.style.Style', function() { symbolizers: [ new ol.style.Shape({ size: 4, - fillStyle: '#BADA55' + fillColor: '#BADA55' }) ] }) @@ -24,7 +24,7 @@ describe('ol.style.Style', function() { var feature = new ol.Feature(); feature.set('foo', 'bar'); expect(style.apply(feature).length).toBe(1); - expect(style.apply(feature)[0].fillStyle).toBe('#BADA55'); + expect(style.apply(feature)[0].fillColor).toBe('#BADA55'); feature.set('foo', 'baz'); expect(style.apply(feature).length).toBe(0); }); From 4918106a22c9f7123593f134eb03fb7f9312b894 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 7 Mar 2013 23:09:23 -0700 Subject: [PATCH 165/180] Make shared vertices structures accessible --- src/ol/layer/vectorlayer.js | 78 ++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index 176602e01e..da15f119b3 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -170,6 +170,27 @@ ol.layer.Vector = function(layerOptions) { */ this.featureCache_ = new ol.layer.FeatureCache(); + /** + * TODO: this means we need to know dimension at construction + * @type {ol.geom.SharedVertices} + * @private + */ + this.pointVertices_ = new ol.geom.SharedVertices(); + + /** + * TODO: this means we need to know dimension at construction + * @type {ol.geom.SharedVertices} + * @private + */ + this.lineVertices_ = new ol.geom.SharedVertices(); + + /** + * TODO: this means we need to know dimension at construction + * @type {ol.geom.SharedVertices} + * @private + */ + this.polygonVertices_ = new ol.geom.SharedVertices(); + }; goog.inherits(ol.layer.Vector, ol.layer.Layer); @@ -213,6 +234,30 @@ ol.layer.Vector.prototype.getFeaturesObject = function(opt_filter) { }; +/** + * @return {ol.geom.SharedVertices} Shared line vertices. + */ +ol.layer.Vector.prototype.getLineVertices = function() { + return this.lineVertices_; +}; + + +/** + * @return {ol.geom.SharedVertices} Shared point vertices. + */ +ol.layer.Vector.prototype.getPointVertices = function() { + return this.pointVertices_; +}; + + +/** + * @return {ol.geom.SharedVertices} Shared polygon vertices. + */ +ol.layer.Vector.prototype.getPolygonVertices = function() { + return this.polygonVertices_; +}; + + /** * @param {Object.} features Features. * @return {Array.} symbolizers for features. @@ -261,17 +306,14 @@ ol.layer.Vector.prototype.groupFeaturesBySymbolizerLiteral = */ ol.layer.Vector.prototype.parseFeatures = function(data, parser, projection) { var features; - var pointVertices = new ol.geom.SharedVertices(); - var lineVertices = new ol.geom.SharedVertices(); - var polygonVertices = new ol.geom.SharedVertices(); var lookup = { - 'point': pointVertices, - 'linestring': lineVertices, - 'polygon': polygonVertices, - 'multipoint': pointVertices, - 'multilinstring': lineVertices, - 'multipolygon': polygonVertices + 'point': this.pointVertices_, + 'linestring': this.lineVertices_, + 'polygon': this.polygonVertices_, + 'multipoint': this.pointVertices_, + 'multilinstring': this.lineVertices_, + 'multipolygon': this.polygonVertices_ }; var callback = function(feature, type) { @@ -289,19 +331,19 @@ ol.layer.Vector.prototype.parseFeatures = function(data, parser, projection) { var transform = ol.projection.getTransform(sourceProjection, projection); transform( - pointVertices.coordinates, - pointVertices.coordinates, - pointVertices.getDimension()); + this.pointVertices_.coordinates, + this.pointVertices_.coordinates, + this.pointVertices_.getDimension()); transform( - lineVertices.coordinates, - lineVertices.coordinates, - lineVertices.getDimension()); + this.lineVertices_.coordinates, + this.lineVertices_.coordinates, + this.lineVertices_.getDimension()); transform( - polygonVertices.coordinates, - polygonVertices.coordinates, - polygonVertices.getDimension()); + this.polygonVertices_.coordinates, + this.polygonVertices_.coordinates, + this.polygonVertices_.getDimension()); this.addFeatures(features); }; From 85a1599a1a62e6782d18465ec9ec4d3d514a806d Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 7 Mar 2013 23:32:56 -0700 Subject: [PATCH 166/180] Removing the style rules example for now The addFeatures method on layer is going to go away temporarily (so all feature adding will take advantage of shared vertices structures). Later we can accept feature arrays in parseFeatures and rename the method back to addFeatures. --- examples/style-rules.html | 47 ------------------- examples/style-rules.js | 99 --------------------------------------- 2 files changed, 146 deletions(-) delete mode 100644 examples/style-rules.html delete mode 100644 examples/style-rules.js diff --git a/examples/style-rules.html b/examples/style-rules.html deleted file mode 100644 index aebef4be82..0000000000 --- a/examples/style-rules.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - Vector Layer Example - - -
-
-

Style rules example

-
Draws features with rule based styles.
-
-

See the - style-rules.js source - to see how this is done.

-
-
-
-
vector, feature, canvas
- - - diff --git a/examples/style-rules.js b/examples/style-rules.js deleted file mode 100644 index aa34ce8a00..0000000000 --- a/examples/style-rules.js +++ /dev/null @@ -1,99 +0,0 @@ -goog.require('ol.Collection'); -goog.require('ol.Coordinate'); -goog.require('ol.Expression'); -goog.require('ol.Feature'); -goog.require('ol.Map'); -goog.require('ol.RendererHint'); -goog.require('ol.View2D'); -goog.require('ol.filter.Filter'); -goog.require('ol.geom.LineString'); -goog.require('ol.layer.Vector'); -goog.require('ol.projection'); -goog.require('ol.source.Vector'); -goog.require('ol.style.Line'); -goog.require('ol.style.Rule'); -goog.require('ol.style.Style'); - - -var style = new ol.style.Style({rules: [ - new ol.style.Rule({ - filter: new ol.filter.Filter(function(feature) { - return feature.get('where') == 'outer'; - }), - symbolizers: [ - new ol.style.Line({ - strokeStyle: new ol.Expression('color'), - strokeWidth: 4, - opacity: 1 - }) - ] - }), - new ol.style.Rule({ - filter: new ol.filter.Filter(function(feature) { - return feature.get('where') == 'inner'; - }), - symbolizers: [ - new ol.style.Line({ - strokeStyle: '#013', - strokeWidth: 4, - opacity: 1 - }), - new ol.style.Line({ - strokeStyle: new ol.Expression('color'), - strokeWidth: 2, - opacity: 1 - }) - ] - }) -]}); - -var vector = new ol.layer.Vector({ - style: style, - source: new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:3857') - }) -}); - -vector.addFeatures([ - new ol.Feature({ - g: new ol.geom.LineString([[-10000000, -10000000], [10000000, 10000000]]), - 'color': '#BADA55', - 'where': 'inner' - }), - new ol.Feature({ - g: new ol.geom.LineString([[-10000000, 10000000], [10000000, -10000000]]), - 'color': '#BADA55', - 'where': 'inner' - }), - new ol.Feature({ - g: new ol.geom.LineString([[-10000000, -10000000], [-10000000, 10000000]]), - 'color': '#013', - 'where': 'outer' - }), - new ol.Feature({ - g: new ol.geom.LineString([[-10000000, 10000000], [10000000, 10000000]]), - 'color': '#013', - 'where': 'outer' - }), - new ol.Feature({ - g: new ol.geom.LineString([[10000000, 10000000], [10000000, -10000000]]), - 'color': '#013', - 'where': 'outer' - }), - new ol.Feature({ - g: new ol.geom.LineString([[10000000, -10000000], [-10000000, -10000000]]), - 'color': '#013', - 'where': 'outer' - }) -]); - - -var map = new ol.Map({ - layers: new ol.Collection([vector]), - renderer: ol.RendererHint.CANVAS, - target: 'map', - view: new ol.View2D({ - center: new ol.Coordinate(0, 0), - zoom: 2 - }) -}); From 895bc4e76c9f901d7b459f6924d10b93870f6ed0 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 7 Mar 2013 23:47:41 -0700 Subject: [PATCH 167/180] The addFeatures method will be added back later This needs to be made to take advantage of the shared vertices. --- src/ol/layer/vectorlayer.exports | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ol/layer/vectorlayer.exports b/src/ol/layer/vectorlayer.exports index 9e76b4263b..ff90656cad 100644 --- a/src/ol/layer/vectorlayer.exports +++ b/src/ol/layer/vectorlayer.exports @@ -1,4 +1,3 @@ @exportClass ol.layer.Vector ol.layer.LayerOptions -@exportProperty ol.layer.Vector.prototype.addFeatures @exportProperty ol.layer.Vector.prototype.parseFeatures From ee75c4b90e6b8c67f53aa8e7cfe5b639f6abba38 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 7 Mar 2013 23:48:10 -0700 Subject: [PATCH 168/180] Use new projection getter --- examples/vector-layer.js | 2 +- test/spec/ol/layer/vectorlayer.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/vector-layer.js b/examples/vector-layer.js index 0204accb60..4c0e5dae50 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -23,7 +23,7 @@ var raster = new ol.layer.TileLayer({ var vector = new ol.layer.Vector({ source: new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:4326') + projection: ol.projection.get('EPSG:4326') }), style: new ol.style.Style({rules: [ new ol.style.Rule({ diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js index 40640fdc07..2c3ee6c4f4 100644 --- a/test/spec/ol/layer/vectorlayer.test.js +++ b/test/spec/ol/layer/vectorlayer.test.js @@ -99,7 +99,7 @@ describe('ol.layer.Vector', function() { var layer = new ol.layer.Vector({ source: new ol.source.Vector({ - projection: ol.projection.getFromCode('EPSG:4326') + projection: ol.projection.get('EPSG:4326') }), style: new ol.style.Style({ rules: [ From 7f62b26c8026089422131d5790d782be76916f19 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Thu, 7 Mar 2013 23:48:23 -0700 Subject: [PATCH 169/180] New example style --- examples/vector-layer.html | 77 ++++++++++++++++++++------------------ examples/vector-layer.js | 2 +- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/examples/vector-layer.html b/examples/vector-layer.html index a15adb4e8a..9ec61c5232 100644 --- a/examples/vector-layer.html +++ b/examples/vector-layer.html @@ -1,47 +1,52 @@ - - + + - + - - - Vector Layer Example + + + + Vector layer example -
-
-

Vector rendering example

-
Uses a canvas renderer for drawing vector features.
-
-

See the - vector-layer.js source - to see how this is done.

+ + -
vector, feature, canvas
+ +
+ +
+
+
+
+
+ +
+ +
+

Vector layer example

+

Example of a vector layer.

+
+

See the vector-layer.js source to see how this is done.

+
+
vector, geojson, style
+
+ +
+ +
+ + diff --git a/examples/vector-layer.js b/examples/vector-layer.js index 4c0e5dae50..a5e7d42972 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -64,6 +64,6 @@ var map = new ol.Map({ target: 'map', view: new ol.View2D({ center: new ol.Coordinate(0, 0), - zoom: 0 + zoom: 1 }) }); From 389b8d14d7aa8e0508782a6f9780981c64a1972f Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 8 Mar 2013 16:25:23 +0100 Subject: [PATCH 170/180] Support GeoJSON as parsed object With this change, we can programmatically create GeoJSONFeatureCollection objects programmatically, without reading a GeoJSON file. --- src/ol/layer/vectorlayer.js | 4 ++++ src/ol/parser/geojson.js | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js index da15f119b3..b49ff250d9 100644 --- a/src/ol/layer/vectorlayer.js +++ b/src/ol/layer/vectorlayer.js @@ -323,6 +323,10 @@ ol.layer.Vector.prototype.parseFeatures = function(data, parser, projection) { goog.asserts.assert(typeof parser.readFeaturesFromString === 'function', 'Expected a parser with readFeaturesFromString method.'); features = parser.readFeaturesFromString(data, {callback: callback}); + } else if (typeof data === 'object') { + goog.asserts.assert(typeof parser.readFeaturesFromObject === 'function', + 'Expected a parser with a readFeaturesFromObject method.'); + features = parser.readFeaturesFromObject(data, {callback: callback}); } else { // TODO: parse more data types throw new Error('Data type not supported: ' + data); diff --git a/src/ol/parser/geojson.js b/src/ol/parser/geojson.js index 94624e3d7a..ce5d0e27c4 100644 --- a/src/ol/parser/geojson.js +++ b/src/ol/parser/geojson.js @@ -51,6 +51,19 @@ ol.parser.GeoJSON.prototype.readFeaturesFromString = }; +/** + * Parse a GeoJSON feature collection from decoded JSON. + * @param {GeoJSONFeatureCollection} object GeoJSON feature collection decoded + * from JSON. + * @param {ol.parser.ReadFeaturesOptions=} opt_options Reader options. + * @return {Array.} Array of features. + */ +ol.parser.GeoJSON.prototype.readFeaturesFromObject = + function(object, opt_options) { + return this.parseFeatureCollection_(object, opt_options); +}; + + /** * @param {GeoJSONObject} json GeoJSON object. * @return {ol.Feature|Array.| From b971fc1aae4ee674238a8b241c71d3872f57a0e4 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 8 Mar 2013 16:26:18 +0100 Subject: [PATCH 171/180] Bringing back the style-rules example Features are now created programmatically as GeoJSONFeatureCollection. --- examples/style-rules.html | 52 +++++++++++++++ examples/style-rules.js | 136 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 examples/style-rules.html create mode 100644 examples/style-rules.js diff --git a/examples/style-rules.html b/examples/style-rules.html new file mode 100644 index 0000000000..7a0859f3ba --- /dev/null +++ b/examples/style-rules.html @@ -0,0 +1,52 @@ + + + + + + + + + + Style with rules example + + + + + +
+ +
+
+
+
+
+ +
+ +
+

Style with rules example

+

Draws features with rule based styles.

+
+

See the style-rules.js source to see how this is done.

+
+
vector, geojson, style
+
+ +
+ +
+ + + + + diff --git a/examples/style-rules.js b/examples/style-rules.js new file mode 100644 index 0000000000..ea7a132d13 --- /dev/null +++ b/examples/style-rules.js @@ -0,0 +1,136 @@ +goog.require('ol.Collection'); +goog.require('ol.Coordinate'); +goog.require('ol.Expression'); +goog.require('ol.Feature'); +goog.require('ol.Map'); +goog.require('ol.RendererHint'); +goog.require('ol.View2D'); +goog.require('ol.control.defaults'); +goog.require('ol.filter.Filter'); +goog.require('ol.geom.LineString'); +goog.require('ol.layer.Vector'); +goog.require('ol.parser.GeoJSON'); +goog.require('ol.projection'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Line'); +goog.require('ol.style.Rule'); +goog.require('ol.style.Style'); + + +var style = new ol.style.Style({rules: [ + new ol.style.Rule({ + filter: new ol.filter.Filter(function(feature) { + return feature.get('where') == 'outer'; + }), + symbolizers: [ + new ol.style.Line({ + strokeColor: new ol.Expression('color'), + strokeWidth: 4, + opacity: 1 + }) + ] + }), + new ol.style.Rule({ + filter: new ol.filter.Filter(function(feature) { + return feature.get('where') == 'inner'; + }), + symbolizers: [ + new ol.style.Line({ + strokeColor: '#013', + strokeWidth: 4, + opacity: 1 + }), + new ol.style.Line({ + strokeColor: new ol.Expression('color'), + strokeWidth: 2, + opacity: 1 + }) + ] + }) +]}); + +var vector = new ol.layer.Vector({ + style: style, + source: new ol.source.Vector({ + projection: ol.projection.get('EPSG:3857') + }) +}); + +vector.parseFeatures({ + 'type': 'FeatureCollection', + 'features': [{ + 'type': 'Feature', + 'properties': { + 'color': '#BADA55', + 'where': 'inner' + }, + 'geometry': { + 'type': 'LineString', + 'coordinates': [[-10000000, -10000000], [10000000, 10000000]] + } + }, { + 'type': 'Feature', + 'properties': { + 'color': '#BADA55', + 'where': 'inner' + }, + 'geometry': { + 'type': 'LineString', + 'coordinates': [[-10000000, 10000000], [10000000, -10000000]] + } + }, { + 'type': 'Feature', + 'properties': { + 'color': '#013', + 'where': 'outer' + }, + 'geometry': { + 'type': 'LineString', + 'coordinates': [[-10000000, -10000000], [-10000000, 10000000]] + } + }, { + 'type': 'Feature', + 'properties': { + 'color': '#013', + 'where': 'outer' + }, + 'geometry': { + 'type': 'LineString', + 'coordinates': [[-10000000, 10000000], [10000000, 10000000]] + } + }, { + 'type': 'Feature', + 'properties': { + 'color': '#013', + 'where': 'outer' + }, + 'geometry': { + 'type': 'LineString', + 'coordinates': [[10000000, 10000000], [10000000, -10000000]] + } + }, { + 'type': 'Feature', + 'properties': { + 'color': '#013', + 'where': 'outer' + }, + 'geometry': { + 'type': 'LineString', + 'coordinates': [[10000000, -10000000], [-10000000, -10000000]] + } + }] +}, new ol.parser.GeoJSON(), ol.projection.get('EPSG:3857')); + + +var map = new ol.Map({ + layers: new ol.Collection([vector]), + controls: ol.control.defaults({ + attribution: false + }), + renderer: ol.RendererHint.CANVAS, + target: 'map', + view: new ol.View2D({ + center: new ol.Coordinate(0, 0), + zoom: 1 + }) +}); From 47e5b0475b0e5033dede1eb33f509a84a0944b89 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 12 Mar 2013 08:24:31 +0100 Subject: [PATCH 172/180] Since #274 we don't need to create an ol.Collection explicitly --- examples/style-rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/style-rules.js b/examples/style-rules.js index ea7a132d13..831f0bc791 100644 --- a/examples/style-rules.js +++ b/examples/style-rules.js @@ -123,7 +123,7 @@ vector.parseFeatures({ var map = new ol.Map({ - layers: new ol.Collection([vector]), + layers: [vector], controls: ol.control.defaults({ attribution: false }), From f5b53740e617c9111c75479f5f4e7b6c722ae203 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 12 Mar 2013 08:28:24 +0100 Subject: [PATCH 173/180] Define map at the top --- examples/vector-layer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/vector-layer.js b/examples/vector-layer.js index a5e7d42972..7a12e66761 100644 --- a/examples/vector-layer.js +++ b/examples/vector-layer.js @@ -17,6 +17,8 @@ goog.require('ol.style.Rule'); goog.require('ol.style.Style'); +var map; + var raster = new ol.layer.TileLayer({ source: new ol.source.MapQuestOpenAerial() }); @@ -58,7 +60,7 @@ xhr.onload = function() { }; xhr.send(); -var map = new ol.Map({ +map = new ol.Map({ layers: new ol.Collection([raster, vector]), renderer: ol.RendererHint.CANVAS, target: 'map', From b20a2bae94493881b4969ad6b84a4b7d72e02914 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 12 Mar 2013 08:35:57 +0100 Subject: [PATCH 174/180] Moving RTreeNode_ into ol.structs package --- src/ol/structs/rtree.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ol/structs/rtree.js b/src/ol/structs/rtree.js index 0d2373a105..710f79c8e4 100644 --- a/src/ol/structs/rtree.js +++ b/src/ol/structs/rtree.js @@ -11,11 +11,11 @@ goog.require('ol.Rectangle'); * @param {number} minY Minimum Y. * @param {number} maxX Maximum X. * @param {number} maxY Maximum Y. - * @param {ol.RTreeNode_} parent Parent node. + * @param {ol.structs.RTreeNode_} parent Parent node. * @param {number} level Level in the tree hierarchy. * @extends {ol.Rectangle} */ -ol.RTreeNode_ = function(minX, minY, maxX, maxY, parent, level) { +ol.structs.RTreeNode_ = function(minX, minY, maxX, maxY, parent, level) { goog.base(this, minX, minY, maxX, maxY); @@ -30,7 +30,7 @@ ol.RTreeNode_ = function(minX, minY, maxX, maxY, parent, level) { this.objectId; /** - * @type {ol.RTreeNode_} + * @type {ol.structs.RTreeNode_} */ this.parent = parent; @@ -45,12 +45,12 @@ ol.RTreeNode_ = function(minX, minY, maxX, maxY, parent, level) { this.types = {}; /** - * @type {Array.} + * @type {Array.} */ this.children = []; }; -goog.inherits(ol.RTreeNode_, ol.Rectangle); +goog.inherits(ol.structs.RTreeNode_, ol.Rectangle); /** @@ -59,7 +59,7 @@ goog.inherits(ol.RTreeNode_, ol.Rectangle); * @param {Object.} results Target object for results. * @param {string=} opt_type Type for another indexing dimension. */ -ol.RTreeNode_.prototype.find = function(bounds, results, opt_type) { +ol.structs.RTreeNode_.prototype.find = function(bounds, results, opt_type) { if (this.intersects(bounds) && (!goog.isDef(opt_type) || this.types[opt_type] === true)) { var numChildren = this.children.length; @@ -79,9 +79,9 @@ ol.RTreeNode_.prototype.find = function(bounds, results, opt_type) { /** * Find the appropriate node for insertion. * @param {ol.Rectangle} bounds Bounding box. - * @return {ol.RTreeNode_|undefined} Matching node. + * @return {ol.structs.RTreeNode_|undefined} Matching node. */ -ol.RTreeNode_.prototype.get = function(bounds) { +ol.structs.RTreeNode_.prototype.get = function(bounds) { if (this.intersects(bounds)) { var numChildren = this.children.length; if (numChildren === 0) { @@ -103,7 +103,7 @@ ol.RTreeNode_.prototype.get = function(bounds) { * Update boxes up to the root to ensure correct bounding * @param {ol.Rectangle} bounds Bounding box. */ -ol.RTreeNode_.prototype.update = function(bounds) { +ol.structs.RTreeNode_.prototype.update = function(bounds) { this.extend(bounds); if (!goog.isNull(this.parent)) { this.parent.update(bounds); @@ -116,7 +116,7 @@ ol.RTreeNode_.prototype.update = function(bounds) { * the split items. The top left will be the topmost leftmost child and the * bottom right will be the rightmost bottommost child. */ -ol.RTreeNode_.prototype.divide = function() { +ol.structs.RTreeNode_.prototype.divide = function() { var numChildren = this.children.length; if (numChildren === 0) { return; @@ -128,12 +128,12 @@ ol.RTreeNode_.prototype.divide = function() { for (var i = 0; i < numChildren; ++i) { child = this.children[i]; if (i % half === 0) { - node = new ol.RTreeNode_(child.minX, child.minY, child.maxX, child.maxY, + node = new ol.structs.RTreeNode_(child.minX, child.minY, child.maxX, child.maxY, this, this.level + 1); goog.object.extend(this.types, node.types); this.children.push(node); } - child.parent = /** @type {ol.RTreeNode_} */ node; + child.parent = /** @type {ol.structs.RTreeNode_} */ node; goog.object.extend(node.types, child.types); node.children.push(child); node.extend(child); @@ -149,9 +149,9 @@ ol.structs.RTree = function() { /** * @private - * @type {ol.RTreeNode_} + * @type {ol.structs.RTreeNode_} */ - this.root_ = new ol.RTreeNode_( + this.root_ = new ol.structs.RTreeNode_( Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, null, 0); @@ -178,7 +178,7 @@ ol.structs.RTree.prototype.find = function(bounds, opt_type) { ol.structs.RTree.prototype.put = function(bounds, object, opt_type) { var found = this.root_.get(bounds); if (found) { - var node = new ol.RTreeNode_( + var node = new ol.structs.RTreeNode_( bounds.minX, bounds.minY, bounds.maxX, bounds.maxY, found, found.level + 1); node.object = object; From 15375df7abeadc42658fcc1509174753ee04cbf0 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 12 Mar 2013 08:41:41 +0100 Subject: [PATCH 175/180] Line was too long after previous change --- src/ol/structs/rtree.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/structs/rtree.js b/src/ol/structs/rtree.js index 710f79c8e4..d3e22e1992 100644 --- a/src/ol/structs/rtree.js +++ b/src/ol/structs/rtree.js @@ -128,8 +128,8 @@ ol.structs.RTreeNode_.prototype.divide = function() { for (var i = 0; i < numChildren; ++i) { child = this.children[i]; if (i % half === 0) { - node = new ol.structs.RTreeNode_(child.minX, child.minY, child.maxX, child.maxY, - this, this.level + 1); + node = new ol.structs.RTreeNode_(child.minX, child.minY, + child.maxX, child.maxY, this, this.level + 1); goog.object.extend(this.types, node.types); this.children.push(node); } From bb8755eb686af83e8645404f1d645c34df40324b Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 12 Mar 2013 08:48:22 +0100 Subject: [PATCH 176/180] Recovering from bad merge --- src/objectliterals.exports | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/objectliterals.exports b/src/objectliterals.exports index 8b2e71a2bb..09a9e5fe10 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -155,6 +155,39 @@ @exportObjectLiteralProperty ol.source.TiledWMSOptions.url string|undefined @exportObjectLiteralProperty ol.source.TiledWMSOptions.urls Array.|undefined +@exportObjectLiteral ol.style.IconOptions +@exportObjectLiteralProperty ol.style.IconOptions.url string|ol.Expression +@exportObjectLiteralProperty ol.style.IconOptions.width number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.IconOptions.height number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.IconOptions.opacity number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.IconOptions.rotation number|ol.Expression|undefined + +@exportObjectLiteral ol.style.LineOptions +@exportObjectLiteralProperty ol.style.LineOptions.strokeColor string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.LineOptions.strokeWidth number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.LineOptions.opacity number|ol.Expression|undefined + +@exportObjectLiteral ol.style.PolygonOptions +@exportObjectLiteralProperty ol.style.PolygonOptions.fillColor string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.PolygonOptions.strokeColor string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.PolygonOptions.strokeWidth number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.PolygonOptions.opacity number|ol.Expression|undefined + +@exportObjectLiteral ol.style.RuleOptions +@exportObjectLiteralProperty ol.style.RuleOptions.filter ol.filter.Filter|undefined +@exportObjectLiteralProperty ol.style.RuleOptions.symbolizers Array.|undefined + +@exportObjectLiteral ol.style.ShapeOptions +@exportObjectLiteralProperty ol.style.ShapeOptions.type ol.style.ShapeType|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.size number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.fillColor string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.strokeColor string|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.strokeWidth number|ol.Expression|undefined +@exportObjectLiteralProperty ol.style.ShapeOptions.opacity number|ol.Expression|undefined + +@exportObjectLiteral ol.style.StyleOptions +@exportObjectLiteralProperty ol.style.StyleOptions.rules Array. + @exportObjectLiteral ol.source.WMTSOptions @exportObjectLiteralProperty ol.source.WMTSOptions.attributions Array.|undefined @exportObjectLiteralProperty ol.source.WMTSOptions.crossOrigin string|null|undefined From eeecd424bfb654b3763908e9d505aa8f48f34baf Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 12 Mar 2013 10:05:47 +0100 Subject: [PATCH 177/180] Moving vector rendering to ol.renderer.canvas.VectorRenderer --- src/ol/renderer/canvas/canvasrenderer.js | 438 ----------------- .../canvas/canvasvectorlayerrenderer.js | 4 +- .../renderer/canvas/canvasvectorrenderer.js | 440 ++++++++++++++++++ 3 files changed, 442 insertions(+), 440 deletions(-) create mode 100644 src/ol/renderer/canvas/canvasvectorrenderer.js diff --git a/src/ol/renderer/canvas/canvasrenderer.js b/src/ol/renderer/canvas/canvasrenderer.js index 4899aa5e18..2eda861ddc 100644 --- a/src/ol/renderer/canvas/canvasrenderer.js +++ b/src/ol/renderer/canvas/canvasrenderer.js @@ -1,27 +1,6 @@ -goog.provide('ol.renderer.canvas.Renderer'); goog.provide('ol.renderer.canvas.SUPPORTED'); -goog.require('goog.asserts'); -goog.require('goog.net.ImageLoader'); -goog.require('goog.vec.Mat4'); -goog.require('ol.Feature'); -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.MultiLineString'); -goog.require('ol.geom.MultiPoint'); -goog.require('ol.geom.MultiPolygon'); -goog.require('ol.geom.Point'); -goog.require('ol.geom.Polygon'); -goog.require('ol.style.IconLiteral'); -goog.require('ol.style.LineLiteral'); -goog.require('ol.style.PointLiteral'); -goog.require('ol.style.PolygonLiteral'); -goog.require('ol.style.ShapeLiteral'); -goog.require('ol.style.ShapeType'); -goog.require('ol.style.SymbolizerLiteral'); /** @@ -29,420 +8,3 @@ goog.require('ol.style.SymbolizerLiteral'); * @type {boolean} Is supported. */ ol.renderer.canvas.SUPPORTED = ol.canvas.SUPPORTED; - - - -/** - * @constructor - * @param {HTMLCanvasElement} canvas Target canvas. - * @param {goog.vec.Mat4.Number} transform Transform. - * @param {ol.Pixel=} opt_offset Pixel offset for top-left corner. This is - * provided as an optional argument as a convenience in cases where the - * transform applies to a separate canvas. - * @param {function()=} opt_iconLoadedCallback Callback for deferred rendering - * when images need to be loaded before rendering. - */ -ol.renderer.canvas.Renderer = - function(canvas, transform, opt_offset, opt_iconLoadedCallback) { - - var context = /** @type {CanvasRenderingContext2D} */ - (canvas.getContext('2d')), - dx = goog.isDef(opt_offset) ? opt_offset.x : 0, - dy = goog.isDef(opt_offset) ? opt_offset.y : 0; - - /** - * @type {goog.vec.Mat4.Number} - * @private - */ - this.transform_ = transform; - context.setTransform( - goog.vec.Mat4.getElement(transform, 0, 0), - goog.vec.Mat4.getElement(transform, 1, 0), - goog.vec.Mat4.getElement(transform, 0, 1), - goog.vec.Mat4.getElement(transform, 1, 1), - goog.vec.Mat4.getElement(transform, 0, 3) + dx, - goog.vec.Mat4.getElement(transform, 1, 3) + dy); - - var vec = [1, 0, 0]; - goog.vec.Mat4.multVec3NoTranslate(transform, vec, vec); - - /** - * @type {number} - * @private - */ - this.inverseScale_ = 1 / Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]); - - /** - * @type {CanvasRenderingContext2D} - * @private - */ - this.context_ = context; - - /** - * @type {function()|undefined} - * @private - */ - this.iconLoadedCallback_ = opt_iconLoadedCallback; - -}; - - -/** - * @param {ol.geom.GeometryType} type Geometry type. - * @param {Array.} features Array of features. - * @param {ol.style.SymbolizerLiteral} symbolizer Symbolizer. - * @return {boolean} true if deferred, false if rendered. - */ -ol.renderer.canvas.Renderer.prototype.renderFeaturesByGeometryType = - function(type, features, symbolizer) { - var deferred = false; - switch (type) { - case ol.geom.GeometryType.POINT: - case ol.geom.GeometryType.MULTIPOINT: - goog.asserts.assert(symbolizer instanceof ol.style.PointLiteral, - 'Expected point symbolizer: ' + symbolizer); - deferred = this.renderPointFeatures_( - features, /** @type {ol.style.PointLiteral} */ (symbolizer)); - break; - case ol.geom.GeometryType.LINESTRING: - case ol.geom.GeometryType.MULTILINESTRING: - goog.asserts.assert(symbolizer instanceof ol.style.LineLiteral, - 'Expected line symbolizer: ' + symbolizer); - this.renderLineStringFeatures_( - features, /** @type {ol.style.LineLiteral} */ (symbolizer)); - break; - case ol.geom.GeometryType.POLYGON: - case ol.geom.GeometryType.MULTIPOLYGON: - goog.asserts.assert(symbolizer instanceof ol.style.PolygonLiteral, - 'Expected polygon symbolizer: ' + symbolizer); - this.renderPolygonFeatures_( - features, /** @type {ol.style.PolygonLiteral} */ (symbolizer)); - break; - default: - throw new Error('Rendering not implemented for geometry type: ' + type); - } - return deferred; -}; - - -/** - * @param {Array.} features Array of line features. - * @param {ol.style.LineLiteral} symbolizer Line symbolizer. - * @private - */ -ol.renderer.canvas.Renderer.prototype.renderLineStringFeatures_ = - function(features, symbolizer) { - - var context = this.context_, - i, ii, geometry, components, j, jj, line, dim, k, kk, x, y; - - context.globalAlpha = symbolizer.opacity; - context.strokeStyle = symbolizer.strokeColor; - context.lineWidth = symbolizer.strokeWidth * this.inverseScale_; - context.lineCap = 'round'; // TODO: accept this as a symbolizer property - context.lineJoin = 'round'; // TODO: accept this as a symbolizer property - context.beginPath(); - for (i = 0, ii = features.length; i < ii; ++i) { - geometry = features[i].getGeometry(); - if (geometry instanceof ol.geom.LineString) { - components = [geometry]; - } else { - goog.asserts.assert(geometry instanceof ol.geom.MultiLineString, - 'Expected MultiLineString'); - components = geometry.components; - } - for (j = 0, jj = components.length; j < jj; ++j) { - line = components[j]; - dim = line.dimension; - for (k = 0, kk = line.getCount(); k < kk; ++k) { - x = line.get(k, 0); - y = line.get(k, 1); - if (k === 0) { - context.moveTo(x, y); - } else { - context.lineTo(x, y); - } - } - } - } - - context.stroke(); -}; - - -/** - * @param {Array.} features Array of point features. - * @param {ol.style.PointLiteral} symbolizer Point symbolizer. - * @return {boolean} true if deferred, false if rendered. - * @private - */ -ol.renderer.canvas.Renderer.prototype.renderPointFeatures_ = - function(features, symbolizer) { - - var context = this.context_, - content, alpha, i, ii, geometry, components, j, jj, point, vec; - - if (symbolizer instanceof ol.style.ShapeLiteral) { - content = ol.renderer.canvas.Renderer.renderShape(symbolizer); - alpha = 1; - } else if (symbolizer instanceof ol.style.IconLiteral) { - content = ol.renderer.canvas.Renderer.renderIcon( - symbolizer, this.iconLoadedCallback_); - alpha = symbolizer.opacity; - } else { - throw new Error('Unsupported symbolizer: ' + symbolizer); - } - - if (goog.isNull(content)) { - return true; - } - - var midWidth = content.width / 2; - var midHeight = content.height / 2; - context.save(); - context.setTransform(1, 0, 0, 1, -midWidth, -midHeight); - context.globalAlpha = alpha; - for (i = 0, ii = features.length; i < ii; ++i) { - geometry = features[i].getGeometry(); - if (geometry instanceof ol.geom.Point) { - components = [geometry]; - } else { - goog.asserts.assert(geometry instanceof ol.geom.MultiPoint, - 'Expected MultiPoint'); - components = geometry.components; - } - for (j = 0, jj = components.length; j < jj; ++j) { - point = components[j]; - vec = goog.vec.Mat4.multVec3( - this.transform_, [point.get(0), point.get(1), 0], []); - context.drawImage(content, vec[0], vec[1], content.width, content.height); - } - } - context.restore(); - - return false; -}; - - -/** - * @param {Array.} features Array of polygon features. - * @param {ol.style.PolygonLiteral} symbolizer Polygon symbolizer. - * @private - */ -ol.renderer.canvas.Renderer.prototype.renderPolygonFeatures_ = - function(features, symbolizer) { - var context = this.context_, - strokeColor = symbolizer.strokeColor, - fillColor = symbolizer.fillColor, - i, ii, geometry, components, j, jj, poly, - rings, numRings, ring, dim, k, kk, x, y; - - context.globalAlpha = symbolizer.opacity; - if (strokeColor) { - context.strokeStyle = symbolizer.strokeColor; - context.lineWidth = symbolizer.strokeWidth * this.inverseScale_; - context.lineCap = 'round'; // TODO: accept this as a symbolizer property - context.lineJoin = 'round'; // TODO: accept this as a symbolizer property - } - if (fillColor) { - context.fillStyle = fillColor; - } - - /** - * Four scenarios covered here: - * 1) stroke only, no holes - only need to have a single path - * 2) fill only, no holes - only need to have a single path - * 3) fill and stroke, no holes - * 4) holes - render polygon to sketch canvas first - */ - context.beginPath(); - for (i = 0, ii = features.length; i < ii; ++i) { - geometry = features[i].getGeometry(); - if (geometry instanceof ol.geom.Polygon) { - components = [geometry]; - } else { - goog.asserts.assert(geometry instanceof ol.geom.MultiPolygon, - 'Expected MultiPolygon'); - components = geometry.components; - } - for (j = 0, jj = components.length; j < jj; ++j) { - poly = components[j]; - dim = poly.dimension; - rings = poly.rings; - numRings = rings.length; - if (numRings > 0) { - // TODO: scenario 4 - ring = rings[0]; - for (k = 0, kk = ring.getCount(); k < kk; ++k) { - x = ring.get(k, 0); - y = ring.get(k, 1); - if (k === 0) { - context.moveTo(x, y); - } else { - context.lineTo(x, y); - } - } - if (fillColor && strokeColor) { - // scenario 3 - fill and stroke each time - context.fill(); - context.stroke(); - if (i < ii - 1 || j < jj - 1) { - context.beginPath(); - } - } - } - } - } - if (!(fillColor && strokeColor)) { - if (fillColor) { - // scenario 2 - fill all at once - context.fill(); - } else { - // scenario 1 - stroke all at once - context.stroke(); - } - } -}; - - -/** - * @param {ol.style.ShapeLiteral} circle Shape symbolizer. - * @return {!HTMLCanvasElement} Canvas element. - * @private - */ -ol.renderer.canvas.Renderer.renderCircle_ = function(circle) { - var strokeWidth = circle.strokeWidth || 0, - size = circle.size + (2 * strokeWidth) + 1, - mid = size / 2, - canvas = /** @type {HTMLCanvasElement} */ - (goog.dom.createElement(goog.dom.TagName.CANVAS)), - context = /** @type {CanvasRenderingContext2D} */ - (canvas.getContext('2d')), - fillColor = circle.fillColor, - strokeColor = circle.strokeColor, - twoPi = Math.PI * 2; - - canvas.height = size; - canvas.width = size; - - context.globalAlpha = circle.opacity; - - if (fillColor) { - context.fillStyle = fillColor; - } - if (strokeColor) { - context.lineWidth = strokeWidth; - context.strokeStyle = strokeColor; - context.lineCap = 'round'; // TODO: accept this as a symbolizer property - context.lineJoin = 'round'; // TODO: accept this as a symbolizer property - } - - context.beginPath(); - context.arc(mid, mid, circle.size / 2, 0, twoPi, true); - - if (fillColor) { - context.fill(); - } - if (strokeColor) { - context.stroke(); - } - return canvas; -}; - - -/** - * @param {ol.style.ShapeLiteral} shape Shape symbolizer. - * @return {!HTMLCanvasElement} Canvas element. - */ -ol.renderer.canvas.Renderer.renderShape = function(shape) { - var canvas; - if (shape.type === ol.style.ShapeType.CIRCLE) { - canvas = ol.renderer.canvas.Renderer.renderCircle_(shape); - } else { - throw new Error('Unsupported shape type: ' + shape); - } - return canvas; -}; - - -/** - * @param {ol.style.IconLiteral} icon Icon literal. - * @param {function()=} opt_callback Callback which will be called when - * the icon is loaded and rendering will work without deferring. - * @return {HTMLImageElement} image element of null if deferred. - */ -ol.renderer.canvas.Renderer.renderIcon = function(icon, opt_callback) { - var url = icon.url; - var image = ol.renderer.canvas.Renderer.icons_[url]; - var deferred = false; - if (!goog.isDef(image)) { - deferred = true; - image = /** @type {HTMLImageElement} */ - (goog.dom.createElement(goog.dom.TagName.IMG)); - goog.events.listenOnce(image, goog.events.EventType.ERROR, - goog.bind(ol.renderer.canvas.Renderer.handleIconError_, null, - opt_callback), - false, ol.renderer.canvas.Renderer.renderIcon); - goog.events.listenOnce(image, goog.events.EventType.LOAD, - goog.bind(ol.renderer.canvas.Renderer.handleIconLoad_, null, - opt_callback), - false, ol.renderer.canvas.Renderer.renderIcon); - image.setAttribute('src', url); - ol.renderer.canvas.Renderer.icons_[url] = image; - } else if (!goog.isNull(image)) { - var width = icon.width, - height = icon.height; - if (goog.isDef(width) && goog.isDef(height)) { - image.width = width; - image.height = height; - } else if (goog.isDef(width)) { - image.height = width / image.width * image.height; - image.width = width; - } else if (goog.isDef(height)) { - image.width = height / image.height * image.width; - image.height = height; - } - } - return deferred ? null : image; -}; - - -/** - * @type {Object.} - * @private - */ -ol.renderer.canvas.Renderer.icons_ = {}; - - -/** - * @param {function()=} opt_callback Callback. - * @param {Event=} opt_event Event. - * @private - */ -ol.renderer.canvas.Renderer.handleIconError_ = - function(opt_callback, opt_event) { - if (goog.isDef(opt_event)) { - var url = opt_event.target.getAttribute('src'); - ol.renderer.canvas.Renderer.icons_[url] = null; - ol.renderer.canvas.Renderer.handleIconLoad_(opt_callback, opt_event); - } -}; - - -/** - * @param {function()=} opt_callback Callback. - * @param {Event=} opt_event Event. - * @private - */ -ol.renderer.canvas.Renderer.handleIconLoad_ = - function(opt_callback, opt_event) { - if (goog.isDef(opt_event)) { - var url = opt_event.target.getAttribute('src'); - ol.renderer.canvas.Renderer.icons_[url] = - /** @type {HTMLImageElement} */ (opt_event.target); - } - if (goog.isDef(opt_callback)) { - opt_callback(); - } -}; - diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 4e900ca5e7..4d2d926f12 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -13,7 +13,7 @@ goog.require('ol.filter.LogicalOperator'); goog.require('ol.geom.GeometryType'); goog.require('ol.layer.Vector'); goog.require('ol.renderer.canvas.Layer'); -goog.require('ol.renderer.canvas.Renderer'); +goog.require('ol.renderer.canvas.VectorRenderer'); goog.require('ol.tilegrid.TileGrid'); @@ -290,7 +290,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = sketchCanvas.width = sketchSize.width; sketchCanvas.height = sketchSize.height; - var sketchCanvasRenderer = new ol.renderer.canvas.Renderer( + var sketchCanvasRenderer = new ol.renderer.canvas.VectorRenderer( sketchCanvas, sketchTransform, undefined, this.requestMapRenderFrame_); // clear/resize final canvas diff --git a/src/ol/renderer/canvas/canvasvectorrenderer.js b/src/ol/renderer/canvas/canvasvectorrenderer.js new file mode 100644 index 0000000000..da7ce21850 --- /dev/null +++ b/src/ol/renderer/canvas/canvasvectorrenderer.js @@ -0,0 +1,440 @@ +goog.provide('ol.renderer.canvas.VectorRenderer'); + + +goog.require('goog.asserts'); +goog.require('goog.net.ImageLoader'); +goog.require('goog.vec.Mat4'); +goog.require('ol.Feature'); +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.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.style.IconLiteral'); +goog.require('ol.style.LineLiteral'); +goog.require('ol.style.PointLiteral'); +goog.require('ol.style.PolygonLiteral'); +goog.require('ol.style.ShapeLiteral'); +goog.require('ol.style.ShapeType'); +goog.require('ol.style.SymbolizerLiteral'); + + +/** + * @constructor + * @param {HTMLCanvasElement} canvas Target canvas. + * @param {goog.vec.Mat4.Number} transform Transform. + * @param {ol.Pixel=} opt_offset Pixel offset for top-left corner. This is + * provided as an optional argument as a convenience in cases where the + * transform applies to a separate canvas. + * @param {function()=} opt_iconLoadedCallback Callback for deferred rendering + * when images need to be loaded before rendering. + */ +ol.renderer.canvas.VectorRenderer = + function(canvas, transform, opt_offset, opt_iconLoadedCallback) { + + var context = /** @type {CanvasRenderingContext2D} */ + (canvas.getContext('2d')), + dx = goog.isDef(opt_offset) ? opt_offset.x : 0, + dy = goog.isDef(opt_offset) ? opt_offset.y : 0; + + /** + * @type {goog.vec.Mat4.Number} + * @private + */ + this.transform_ = transform; + context.setTransform( + goog.vec.Mat4.getElement(transform, 0, 0), + goog.vec.Mat4.getElement(transform, 1, 0), + goog.vec.Mat4.getElement(transform, 0, 1), + goog.vec.Mat4.getElement(transform, 1, 1), + goog.vec.Mat4.getElement(transform, 0, 3) + dx, + goog.vec.Mat4.getElement(transform, 1, 3) + dy); + + var vec = [1, 0, 0]; + goog.vec.Mat4.multVec3NoTranslate(transform, vec, vec); + + /** + * @type {number} + * @private + */ + this.inverseScale_ = 1 / Math.sqrt(vec[0] * vec[0] + vec[1] * vec[1]); + + /** + * @type {CanvasRenderingContext2D} + * @private + */ + this.context_ = context; + + /** + * @type {function()|undefined} + * @private + */ + this.iconLoadedCallback_ = opt_iconLoadedCallback; + +}; + + +/** + * @param {ol.geom.GeometryType} type Geometry type. + * @param {Array.} features Array of features. + * @param {ol.style.SymbolizerLiteral} symbolizer Symbolizer. + * @return {boolean} true if deferred, false if rendered. + */ +ol.renderer.canvas.VectorRenderer.prototype.renderFeaturesByGeometryType = + function(type, features, symbolizer) { + var deferred = false; + switch (type) { + case ol.geom.GeometryType.POINT: + case ol.geom.GeometryType.MULTIPOINT: + goog.asserts.assert(symbolizer instanceof ol.style.PointLiteral, + 'Expected point symbolizer: ' + symbolizer); + deferred = this.renderPointFeatures_( + features, /** @type {ol.style.PointLiteral} */ (symbolizer)); + break; + case ol.geom.GeometryType.LINESTRING: + case ol.geom.GeometryType.MULTILINESTRING: + goog.asserts.assert(symbolizer instanceof ol.style.LineLiteral, + 'Expected line symbolizer: ' + symbolizer); + this.renderLineStringFeatures_( + features, /** @type {ol.style.LineLiteral} */ (symbolizer)); + break; + case ol.geom.GeometryType.POLYGON: + case ol.geom.GeometryType.MULTIPOLYGON: + goog.asserts.assert(symbolizer instanceof ol.style.PolygonLiteral, + 'Expected polygon symbolizer: ' + symbolizer); + this.renderPolygonFeatures_( + features, /** @type {ol.style.PolygonLiteral} */ (symbolizer)); + break; + default: + throw new Error('Rendering not implemented for geometry type: ' + type); + } + return deferred; +}; + + +/** + * @param {Array.} features Array of line features. + * @param {ol.style.LineLiteral} symbolizer Line symbolizer. + * @private + */ +ol.renderer.canvas.VectorRenderer.prototype.renderLineStringFeatures_ = + function(features, symbolizer) { + + var context = this.context_, + i, ii, geometry, components, j, jj, line, dim, k, kk, x, y; + + context.globalAlpha = symbolizer.opacity; + context.strokeStyle = symbolizer.strokeColor; + context.lineWidth = symbolizer.strokeWidth * this.inverseScale_; + context.lineCap = 'round'; // TODO: accept this as a symbolizer property + context.lineJoin = 'round'; // TODO: accept this as a symbolizer property + context.beginPath(); + for (i = 0, ii = features.length; i < ii; ++i) { + geometry = features[i].getGeometry(); + if (geometry instanceof ol.geom.LineString) { + components = [geometry]; + } else { + goog.asserts.assert(geometry instanceof ol.geom.MultiLineString, + 'Expected MultiLineString'); + components = geometry.components; + } + for (j = 0, jj = components.length; j < jj; ++j) { + line = components[j]; + dim = line.dimension; + for (k = 0, kk = line.getCount(); k < kk; ++k) { + x = line.get(k, 0); + y = line.get(k, 1); + if (k === 0) { + context.moveTo(x, y); + } else { + context.lineTo(x, y); + } + } + } + } + + context.stroke(); +}; + + +/** + * @param {Array.} features Array of point features. + * @param {ol.style.PointLiteral} symbolizer Point symbolizer. + * @return {boolean} true if deferred, false if rendered. + * @private + */ +ol.renderer.canvas.VectorRenderer.prototype.renderPointFeatures_ = + function(features, symbolizer) { + + var context = this.context_, + content, alpha, i, ii, geometry, components, j, jj, point, vec; + + if (symbolizer instanceof ol.style.ShapeLiteral) { + content = ol.renderer.canvas.VectorRenderer.renderShape(symbolizer); + alpha = 1; + } else if (symbolizer instanceof ol.style.IconLiteral) { + content = ol.renderer.canvas.VectorRenderer.renderIcon( + symbolizer, this.iconLoadedCallback_); + alpha = symbolizer.opacity; + } else { + throw new Error('Unsupported symbolizer: ' + symbolizer); + } + + if (goog.isNull(content)) { + return true; + } + + var midWidth = content.width / 2; + var midHeight = content.height / 2; + context.save(); + context.setTransform(1, 0, 0, 1, -midWidth, -midHeight); + context.globalAlpha = alpha; + for (i = 0, ii = features.length; i < ii; ++i) { + geometry = features[i].getGeometry(); + if (geometry instanceof ol.geom.Point) { + components = [geometry]; + } else { + goog.asserts.assert(geometry instanceof ol.geom.MultiPoint, + 'Expected MultiPoint'); + components = geometry.components; + } + for (j = 0, jj = components.length; j < jj; ++j) { + point = components[j]; + vec = goog.vec.Mat4.multVec3( + this.transform_, [point.get(0), point.get(1), 0], []); + context.drawImage(content, vec[0], vec[1], content.width, content.height); + } + } + context.restore(); + + return false; +}; + + +/** + * @param {Array.} features Array of polygon features. + * @param {ol.style.PolygonLiteral} symbolizer Polygon symbolizer. + * @private + */ +ol.renderer.canvas.VectorRenderer.prototype.renderPolygonFeatures_ = + function(features, symbolizer) { + var context = this.context_, + strokeColor = symbolizer.strokeColor, + fillColor = symbolizer.fillColor, + i, ii, geometry, components, j, jj, poly, + rings, numRings, ring, dim, k, kk, x, y; + + context.globalAlpha = symbolizer.opacity; + if (strokeColor) { + context.strokeStyle = symbolizer.strokeColor; + context.lineWidth = symbolizer.strokeWidth * this.inverseScale_; + context.lineCap = 'round'; // TODO: accept this as a symbolizer property + context.lineJoin = 'round'; // TODO: accept this as a symbolizer property + } + if (fillColor) { + context.fillStyle = fillColor; + } + + /** + * Four scenarios covered here: + * 1) stroke only, no holes - only need to have a single path + * 2) fill only, no holes - only need to have a single path + * 3) fill and stroke, no holes + * 4) holes - render polygon to sketch canvas first + */ + context.beginPath(); + for (i = 0, ii = features.length; i < ii; ++i) { + geometry = features[i].getGeometry(); + if (geometry instanceof ol.geom.Polygon) { + components = [geometry]; + } else { + goog.asserts.assert(geometry instanceof ol.geom.MultiPolygon, + 'Expected MultiPolygon'); + components = geometry.components; + } + for (j = 0, jj = components.length; j < jj; ++j) { + poly = components[j]; + dim = poly.dimension; + rings = poly.rings; + numRings = rings.length; + if (numRings > 0) { + // TODO: scenario 4 + ring = rings[0]; + for (k = 0, kk = ring.getCount(); k < kk; ++k) { + x = ring.get(k, 0); + y = ring.get(k, 1); + if (k === 0) { + context.moveTo(x, y); + } else { + context.lineTo(x, y); + } + } + if (fillColor && strokeColor) { + // scenario 3 - fill and stroke each time + context.fill(); + context.stroke(); + if (i < ii - 1 || j < jj - 1) { + context.beginPath(); + } + } + } + } + } + if (!(fillColor && strokeColor)) { + if (fillColor) { + // scenario 2 - fill all at once + context.fill(); + } else { + // scenario 1 - stroke all at once + context.stroke(); + } + } +}; + + +/** + * @param {ol.style.ShapeLiteral} circle Shape symbolizer. + * @return {!HTMLCanvasElement} Canvas element. + * @private + */ +ol.renderer.canvas.VectorRenderer.renderCircle_ = function(circle) { + var strokeWidth = circle.strokeWidth || 0, + size = circle.size + (2 * strokeWidth) + 1, + mid = size / 2, + canvas = /** @type {HTMLCanvasElement} */ + (goog.dom.createElement(goog.dom.TagName.CANVAS)), + context = /** @type {CanvasRenderingContext2D} */ + (canvas.getContext('2d')), + fillColor = circle.fillColor, + strokeColor = circle.strokeColor, + twoPi = Math.PI * 2; + + canvas.height = size; + canvas.width = size; + + context.globalAlpha = circle.opacity; + + if (fillColor) { + context.fillStyle = fillColor; + } + if (strokeColor) { + context.lineWidth = strokeWidth; + context.strokeStyle = strokeColor; + context.lineCap = 'round'; // TODO: accept this as a symbolizer property + context.lineJoin = 'round'; // TODO: accept this as a symbolizer property + } + + context.beginPath(); + context.arc(mid, mid, circle.size / 2, 0, twoPi, true); + + if (fillColor) { + context.fill(); + } + if (strokeColor) { + context.stroke(); + } + return canvas; +}; + + +/** + * @param {ol.style.ShapeLiteral} shape Shape symbolizer. + * @return {!HTMLCanvasElement} Canvas element. + */ +ol.renderer.canvas.VectorRenderer.renderShape = function(shape) { + var canvas; + if (shape.type === ol.style.ShapeType.CIRCLE) { + canvas = ol.renderer.canvas.VectorRenderer.renderCircle_(shape); + } else { + throw new Error('Unsupported shape type: ' + shape); + } + return canvas; +}; + + +/** + * @param {ol.style.IconLiteral} icon Icon literal. + * @param {function()=} opt_callback Callback which will be called when + * the icon is loaded and rendering will work without deferring. + * @return {HTMLImageElement} image element of null if deferred. + */ +ol.renderer.canvas.VectorRenderer.renderIcon = function(icon, opt_callback) { + var url = icon.url; + var image = ol.renderer.canvas.VectorRenderer.icons_[url]; + var deferred = false; + if (!goog.isDef(image)) { + deferred = true; + image = /** @type {HTMLImageElement} */ + (goog.dom.createElement(goog.dom.TagName.IMG)); + goog.events.listenOnce(image, goog.events.EventType.ERROR, + goog.bind(ol.renderer.canvas.VectorRenderer.handleIconError_, null, + opt_callback), + false, ol.renderer.canvas.VectorRenderer.renderIcon); + goog.events.listenOnce(image, goog.events.EventType.LOAD, + goog.bind(ol.renderer.canvas.VectorRenderer.handleIconLoad_, null, + opt_callback), + false, ol.renderer.canvas.VectorRenderer.renderIcon); + image.setAttribute('src', url); + ol.renderer.canvas.VectorRenderer.icons_[url] = image; + } else if (!goog.isNull(image)) { + var width = icon.width, + height = icon.height; + if (goog.isDef(width) && goog.isDef(height)) { + image.width = width; + image.height = height; + } else if (goog.isDef(width)) { + image.height = width / image.width * image.height; + image.width = width; + } else if (goog.isDef(height)) { + image.width = height / image.height * image.width; + image.height = height; + } + } + return deferred ? null : image; +}; + + +/** + * @type {Object.} + * @private + */ +ol.renderer.canvas.VectorRenderer.icons_ = {}; + + +/** + * @param {function()=} opt_callback Callback. + * @param {Event=} opt_event Event. + * @private + */ +ol.renderer.canvas.VectorRenderer.handleIconError_ = + function(opt_callback, opt_event) { + if (goog.isDef(opt_event)) { + var url = opt_event.target.getAttribute('src'); + ol.renderer.canvas.VectorRenderer.icons_[url] = null; + ol.renderer.canvas.VectorRenderer.handleIconLoad_(opt_callback, opt_event); + } +}; + + +/** + * @param {function()=} opt_callback Callback. + * @param {Event=} opt_event Event. + * @private + */ +ol.renderer.canvas.VectorRenderer.handleIconLoad_ = + function(opt_callback, opt_event) { + if (goog.isDef(opt_event)) { + var url = opt_event.target.getAttribute('src'); + ol.renderer.canvas.VectorRenderer.icons_[url] = + /** @type {HTMLImageElement} */ (opt_event.target); + } + if (goog.isDef(opt_callback)) { + opt_callback(); + } +}; + From acd4f50f15b9aecd35bf4e44398bc362d2410ae1 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 12 Mar 2013 10:23:38 +0100 Subject: [PATCH 178/180] Making the linter happy --- src/ol/renderer/canvas/canvasvectorrenderer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ol/renderer/canvas/canvasvectorrenderer.js b/src/ol/renderer/canvas/canvasvectorrenderer.js index da7ce21850..0a59388759 100644 --- a/src/ol/renderer/canvas/canvasvectorrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorrenderer.js @@ -24,6 +24,7 @@ goog.require('ol.style.ShapeType'); goog.require('ol.style.SymbolizerLiteral'); + /** * @constructor * @param {HTMLCanvasElement} canvas Target canvas. From 5e5e4d110a74218aec85bba68bc54135a9859a77 Mon Sep 17 00:00:00 2001 From: Marc Jansen Date: Mon, 11 Mar 2013 17:01:42 +0100 Subject: [PATCH 179/180] Use constant for CSS class 'ol-unselectable'. --- src/ol/control/attributioncontrol.js | 3 ++- src/ol/control/scalelinecontrol.js | 3 ++- src/ol/control/zoomcontrol.js | 6 ++++-- src/ol/ol.js | 8 ++++++++ src/ol/renderer/canvas/canvasmaprenderer.js | 3 ++- src/ol/renderer/dom/dommaprenderer.js | 3 ++- src/ol/renderer/webgl/webglmaprenderer.js | 3 ++- 7 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/ol/control/attributioncontrol.js b/src/ol/control/attributioncontrol.js index 23c5b6d241..7c7b34457c 100644 --- a/src/ol/control/attributioncontrol.js +++ b/src/ol/control/attributioncontrol.js @@ -8,6 +8,7 @@ goog.require('goog.dom.TagName'); goog.require('goog.events'); goog.require('goog.object'); goog.require('goog.style'); +goog.require('ol'); goog.require('ol.Attribution'); goog.require('ol.FrameState'); goog.require('ol.MapEvent'); @@ -30,7 +31,7 @@ ol.control.Attribution = function(opt_options) { this.ulElement_ = goog.dom.createElement(goog.dom.TagName.UL); var element = goog.dom.createDom(goog.dom.TagName.DIV, { - 'class': 'ol-attribution ol-unselectable' + 'class': 'ol-attribution ' + ol.CSS_CLASS_UNSELECTABLE }, this.ulElement_); goog.base(this, { diff --git a/src/ol/control/scalelinecontrol.js b/src/ol/control/scalelinecontrol.js index 8ba1ae5032..d3c41d2d47 100644 --- a/src/ol/control/scalelinecontrol.js +++ b/src/ol/control/scalelinecontrol.js @@ -3,6 +3,7 @@ goog.provide('ol.control.ScaleLineUnits'); goog.require('goog.dom'); goog.require('goog.style'); +goog.require('ol'); goog.require('ol.FrameState'); goog.require('ol.MapEvent'); goog.require('ol.MapEventType'); @@ -48,7 +49,7 @@ ol.control.ScaleLine = function(opt_options) { * @type {Element} */ this.element_ = goog.dom.createDom(goog.dom.TagName.DIV, { - 'class': 'ol-scale-line ol-unselectable' + 'class': 'ol-scale-line ' + ol.CSS_CLASS_UNSELECTABLE }, this.innerElement_); /** diff --git a/src/ol/control/zoomcontrol.js b/src/ol/control/zoomcontrol.js index ca3d316378..aa3585ccd5 100644 --- a/src/ol/control/zoomcontrol.js +++ b/src/ol/control/zoomcontrol.js @@ -6,6 +6,7 @@ goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.events'); goog.require('goog.events.EventType'); +goog.require('ol'); goog.require('ol.control.Control'); @@ -43,8 +44,9 @@ ol.control.Zoom = function(opt_options) { goog.events.EventType.CLICK ], this.handleOut_, false, this); - var element = goog.dom.createDom( - goog.dom.TagName.DIV, 'ol-zoom ol-unselectable', inElement, outElement); + var cssClasses = 'ol-zoom ' + ol.CSS_CLASS_UNSELECTABLE; + var element = goog.dom.createDom(goog.dom.TagName.DIV, cssClasses, inElement, + outElement); goog.base(this, { element: element, diff --git a/src/ol/ol.js b/src/ol/ol.js index 14a0e3d7b2..7f89641909 100644 --- a/src/ol/ol.js +++ b/src/ol/ol.js @@ -7,3 +7,11 @@ if (goog.DEBUG) { var logger = goog.debug.Logger.getLogger('ol'); logger.setLevel(goog.debug.Logger.Level.FINEST); } + + +/** + * The CSS class that we'll give the DOM elements to have them unselectable. + * + * @const {string} + */ +ol.CSS_CLASS_UNSELECTABLE = 'ol-unselectable'; diff --git a/src/ol/renderer/canvas/canvasmaprenderer.js b/src/ol/renderer/canvas/canvasmaprenderer.js index 24ee38307f..399bdc8e4d 100644 --- a/src/ol/renderer/canvas/canvasmaprenderer.js +++ b/src/ol/renderer/canvas/canvasmaprenderer.js @@ -6,6 +6,7 @@ goog.require('goog.array'); goog.require('goog.dom'); goog.require('goog.style'); goog.require('goog.vec.Mat4'); +goog.require('ol'); goog.require('ol.Size'); goog.require('ol.layer.ImageLayer'); goog.require('ol.layer.TileLayer'); @@ -38,7 +39,7 @@ ol.renderer.canvas.Map = function(container, map) { this.canvas_ = goog.dom.createElement(goog.dom.TagName.CANVAS); this.canvas_.height = this.canvasSize_.height; this.canvas_.width = this.canvasSize_.width; - this.canvas_.className = 'ol-unselectable'; + this.canvas_.className = ol.CSS_CLASS_UNSELECTABLE; goog.dom.insertChildAt(container, this.canvas_, 0); /** diff --git a/src/ol/renderer/dom/dommaprenderer.js b/src/ol/renderer/dom/dommaprenderer.js index a833d5fd91..c3f2dffb77 100644 --- a/src/ol/renderer/dom/dommaprenderer.js +++ b/src/ol/renderer/dom/dommaprenderer.js @@ -5,6 +5,7 @@ goog.require('goog.asserts'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.style'); +goog.require('ol'); goog.require('ol.layer.ImageLayer'); goog.require('ol.layer.TileLayer'); goog.require('ol.renderer.Map'); @@ -28,7 +29,7 @@ ol.renderer.dom.Map = function(container, map) { * @private */ this.layersPane_ = goog.dom.createElement(goog.dom.TagName.DIV); - this.layersPane_.className = 'ol-layers ol-unselectable'; + this.layersPane_.className = 'ol-layers ' + ol.CSS_CLASS_UNSELECTABLE; var style = this.layersPane_.style; style.position = 'absolute'; style.width = '100%'; diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index 867c2c3d2a..afdb14bb8f 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -11,6 +11,7 @@ goog.require('goog.events'); goog.require('goog.events.Event'); goog.require('goog.style'); goog.require('goog.webgl'); +goog.require('ol'); goog.require('ol.FrameState'); goog.require('ol.Size'); goog.require('ol.Tile'); @@ -122,7 +123,7 @@ ol.renderer.webgl.Map = function(container, map) { this.canvas_ = goog.dom.createElement(goog.dom.TagName.CANVAS); this.canvas_.height = container.clientHeight; this.canvas_.width = container.clientWidth; - this.canvas_.className = 'ol-unselectable'; + this.canvas_.className = ol.CSS_CLASS_UNSELECTABLE; goog.dom.insertChildAt(container, this.canvas_, 0); /** From 427c376fc6772e2db35b1bb4d56111ae5678012b Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 12 Mar 2013 12:57:22 +0100 Subject: [PATCH 180/180] Always render map when layers are added or removed --- src/ol/renderer/dom/dommaprenderer.js | 9 --------- src/ol/renderer/maprenderer.js | 2 ++ src/ol/renderer/webgl/webglmaprenderer.js | 22 ---------------------- 3 files changed, 2 insertions(+), 31 deletions(-) diff --git a/src/ol/renderer/dom/dommaprenderer.js b/src/ol/renderer/dom/dommaprenderer.js index a833d5fd91..45189915eb 100644 --- a/src/ol/renderer/dom/dommaprenderer.js +++ b/src/ol/renderer/dom/dommaprenderer.js @@ -46,15 +46,6 @@ ol.renderer.dom.Map = function(container, map) { goog.inherits(ol.renderer.dom.Map, ol.renderer.Map); -/** - * @inheritDoc - */ -ol.renderer.dom.Map.prototype.addLayer = function(layer) { - goog.base(this, 'addLayer', layer); - this.getMap().render(); -}; - - /** * @inheritDoc */ diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index dfd69e5464..9d18863c37 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -79,6 +79,7 @@ goog.inherits(ol.renderer.Map, goog.Disposable); ol.renderer.Map.prototype.addLayer = function(layer) { var layerRenderer = this.createLayerRenderer(layer); this.setLayerRenderer(layer, layerRenderer); + this.getMap().render(); }; @@ -224,6 +225,7 @@ ol.renderer.Map.prototype.handleLayersRemove = function(collectionEvent) { */ ol.renderer.Map.prototype.removeLayer = function(layer) { goog.dispose(this.removeLayerRenderer(layer)); + this.getMap().render(); }; diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index 867c2c3d2a..8e72a091b7 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -215,17 +215,6 @@ ol.renderer.webgl.Map = function(container, map) { goog.inherits(ol.renderer.webgl.Map, ol.renderer.Map); -/** - * @inheritDoc - */ -ol.renderer.webgl.Map.prototype.addLayer = function(layer) { - goog.base(this, 'addLayer', layer); - if (layer.getVisible()) { - this.getMap().render(); - } -}; - - /** * @param {ol.Tile} tile Tile. * @param {number} magFilter Mag filter. @@ -464,17 +453,6 @@ ol.renderer.webgl.Map.prototype.isTileTextureLoaded = function(tile) { }; -/** - * @inheritDoc - */ -ol.renderer.webgl.Map.prototype.removeLayer = function(layer) { - goog.base(this, 'removeLayer', layer); - if (layer.getVisible()) { - this.getMap().render(); - } -}; - - /** * @inheritDoc */