diff --git a/examples/cartodb.html b/examples/cartodb.html new file mode 100644 index 0000000000..6d3fb99ebc --- /dev/null +++ b/examples/cartodb.html @@ -0,0 +1,26 @@ +--- +template: example.html +title: CartoDB source example +shortdesc: Example of a cartodb map. +docs: > + A simple map with customized Attribution control. +tags: "simple, openstreetmap, attribution" +--- +
+
+
+
+
+ +
+
+
diff --git a/examples/cartodb.js b/examples/cartodb.js new file mode 100644 index 0000000000..8822a7ebf9 --- /dev/null +++ b/examples/cartodb.js @@ -0,0 +1,48 @@ +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.layer.Tile'); +goog.require('ol.source.CartoDB'); +goog.require('ol.source.OSM'); + +var mapConfig = { + 'layers': [{ + 'type': 'cartodb', + 'options': { + 'cartocss_version': '2.1.1', + 'cartocss': '#layer { polygon-fill: #F00; }', + 'sql': 'select * from european_countries_e where area > 0' + } + }] +}; + +var cartoDBSource = new ol.source.CartoDB({ + account: 'documentation', + config: mapConfig +}); + +var map = new ol.Map({ + layers: [ + new ol.layer.Tile({ + source: new ol.source.OSM() + }), + new ol.layer.Tile({ + source: cartoDBSource + }) + ], + target: 'map', + view: new ol.View({ + center: [0, 0], + zoom: 2 + }) +}); + +function setArea(n) { + mapConfig.layers[0].options.sql = + 'select * from european_countries_e where area > ' + n; + cartoDBSource.setConfig(mapConfig); +} + + +document.getElementById('country-area').addEventListener('change', function() { + setArea(this.value); +}); diff --git a/externs/olx.js b/externs/olx.js index 3178bea698..16dfcbab43 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -6209,6 +6209,104 @@ olx.source.XYZOptions.prototype.urls; */ olx.source.XYZOptions.prototype.wrapX; +/** + * @typedef {{attributions: (Array.|undefined), + * crossOrigin: (null|string|undefined), + * logo: (string|olx.LogoOptions|undefined), + * projection: ol.proj.ProjectionLike, + * maxZoom: (number|undefined), + * minZoom: (number|undefined), + * wrapX: (boolean|undefined), + * config: (Object|undefined), + * map: string, + * account: (string|undefined)}} + * @api + */ +olx.source.CartoDBOptions; + + +/** + * Attributions. + * @type {Array.|undefined} + * @api stable + */ +olx.source.CartoDBOptions.prototype.attributions; + + +/** + * The `crossOrigin` attribute for loaded images. Note that you must provide a + * `crossOrigin` value if you are using the WebGL renderer or if you want to + * access pixel data with the Canvas renderer. See + * {@link https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image} + * for more detail. + * @type {null|string|undefined} + * @api stable + */ +olx.source.CartoDBOptions.prototype.crossOrigin; + + +/** + * Logo. + * @type {string|olx.LogoOptions|undefined} + * @api stable + */ +olx.source.CartoDBOptions.prototype.logo; + + +/** + * Projection. Default is `EPSG:3857`. + * @type {ol.proj.ProjectionLike} + * @api + */ +olx.source.CartoDBOptions.prototype.projection; + + +/** + * Optional max zoom level. Default is `18`. + * @type {number|undefined} + * @api + */ +olx.source.CartoDBOptions.prototype.maxZoom; + + +/** + * Whether to wrap the world horizontally. Default is `true`. + * @type {boolean|undefined} + * @api + */ +olx.source.CartoDBOptions.prototype.wrapX; + + +/** + * If using anonymous maps, the CartoDB config to use. See + * {@link http://docs.cartodb.com/cartodb-platform/maps-api.html#anonymous-maps} + * for more detail. + * If using named maps, a key-value lookup with the template parameters. + * See {@link http://docs.cartodb.com/cartodb-platform/maps-api.html#named-maps} + * for more detail. + * @type {Object|undefined} + * @api + */ +olx.source.CartoDBOptions.prototype.config; + + +/** + * If using named maps, this will be the name of the template to load. + * See {@link http://docs.cartodb.com/cartodb-platform/maps-api.html#named-maps} + * for more detail. + * @type {boolean|undefined} + * @api + */ +olx.source.CartoDBOptions.prototype.map; + + +/** + * CartoDB account name + * @type {string} + * @api + */ +olx.source.CartoDBOptions.prototype.account; + /** * @typedef {{attributions: (olx.source.AttributionOption|undefined), diff --git a/src/ol/source/cartodb.js b/src/ol/source/cartodb.js new file mode 100644 index 0000000000..d41af2a54f --- /dev/null +++ b/src/ol/source/cartodb.js @@ -0,0 +1,127 @@ +goog.provide('ol.source.CartoDB'); + +goog.require('goog.asserts'); +goog.require('goog.events'); +goog.require('goog.net.EventType'); +goog.require('goog.net.XhrIo'); +goog.require('goog.net.XhrIo.ResponseType'); +goog.require('ol.source.XYZ'); + + +/** + * @classdesc + * Layer source for the CartoDB tiles. + * + * @constructor + * @extends {ol.source.XYZ} + * @param {olx.source.CartoDBOptions} options CartoDB options. + * @api + */ +ol.source.CartoDB = function(options) { + this.account_ = options.account; + this.mapId_ = options.map || ''; + this.config_ = options.config || {}; + this.templateCache_ = {}; + delete options.map; + goog.base(this, options); + this.initializeMap_(); +}; +goog.inherits(ol.source.CartoDB, ol.source.XYZ); + + +/** + * Returns the current config. + * @return {Object} The current configuration. + * @api + */ +ol.source.CartoDB.prototype.getConfig = function() { + return this.config_; +}; + + +/** + * Updates the carto db config. + * @param {Object} config a key-value lookup. Values will replace current values + * in the config. + * @api + */ +ol.source.CartoDB.prototype.updateConfig = function(config) { + for (var key in config) { + this.config_[key] = config[key]; + } + this.initializeMap_(); +}; + + +/** + * Sets the CartoDB config + * @param {Object} config In the case of anonymous maps, a CartoDB configuration + * object. + * If using named maps, a key-value lookup with the template parameters. + */ +ol.source.CartoDB.prototype.setConfig = function(config) { + this.config_ = config || {}; + this.initializeMap_(); +}; + + +/** + * Issue a request to initialize the CartoDB map. + * @private + */ +ol.source.CartoDB.prototype.initializeMap_ = function() { + var paramHash = JSON.stringify(this.config_); + if (this.templateCache_[paramHash]) { + this.applyTemplate_(this.templateCache_[paramHash]); + return; + } + var protocol = window.location.protocol; + var mapUrl = protocol + '//' + this.account_ + + '.cartodb.com/api/v1/map'; + + if (this.mapId_) { + mapUrl += '/named/' + this.mapId_; + } + + var xhrIo = new goog.net.XhrIo(); + xhrIo.setResponseType(goog.net.XhrIo.ResponseType.TEXT); + xhrIo.setWithCredentials(false); + goog.events.listen(xhrIo, goog.net.EventType.COMPLETE, + this.handleInitResponse_.bind(this, paramHash)); + xhrIo.send(mapUrl, + 'POST', + JSON.stringify(this.config_), + {'Content-Type': 'application/json'}); +}; + + +/** + * Handle map initialization response. + * @param {string} paramHash a hash representing the parameter set that was used + * for the request + * @param {Event} event Event. + * @private + */ +ol.source.CartoDB.prototype.handleInitResponse_ = function(paramHash, event) { + var xhrIo = event.target; + goog.asserts.assertInstanceof(xhrIo, goog.net.XhrIo, + 'event.target/xhrIo is an instance of goog.net.XhrIo'); + var data = xhrIo.getResponseJson(); + if (xhrIo.isSuccess()) { + this.applyTemplate_(data); + } + this.templateCache_[paramHash] = data; +}; + + +/** + * Apply the new tile urls returned by carto db + * @param {Object} data Result of carto db call. + * @private + */ +ol.source.CartoDB.prototype.applyTemplate_ = function(data) { + var layerId = data['layergroupid']; + var tilesUrl = 'https://' + data['cdn_url']['https'] + '/' + this.account_ + + '/api/v1/map/' + layerId + '/{z}/{x}/{y}.png'; + this.setUrl(tilesUrl); +}; diff --git a/test/spec/ol/source/cartodbsource.test.js b/test/spec/ol/source/cartodbsource.test.js new file mode 100644 index 0000000000..9f62f6981c --- /dev/null +++ b/test/spec/ol/source/cartodbsource.test.js @@ -0,0 +1,18 @@ +goog.provide('ol.test.source.CartoDBSource'); + +goog.require('ol.source.CartoDB'); +goog.require('ol.source.XYZ'); + +describe('ol.source.CartoDB', function() { + + describe('constructor', function() { + it('returns a CartoDB source', function() { + var source = new ol.source.CartoDB({ + map: 'example', + config: {} + }); + expect(source).to.be.a(ol.source.XYZ); + expect(source).to.be.a(ol.source.CartoDB); + }); + }); +});