This commit is contained in:
zhongjin
2020-06-15 12:07:54 +08:00
parent 610ed21a90
commit a96ef233c9
444 changed files with 0 additions and 0 deletions
@@ -0,0 +1,159 @@
(function() {
/**
* this module implements all the features related to overlay geometries
* in leaflet: markers, polygons, lines and so on
*/
/**
* view for markers
*/
function PointView(geometryModel) {
var self = this;
// events to link
var events = [
'click',
'dblclick',
'mousedown',
'mouseover',
'mouseout',
'dragstart',
'drag',
'dragend'
];
this._eventHandlers = {};
this.model = geometryModel;
this.points = [];
var icon = {
iconUrl: this.model.get('iconUrl') || cdb.config.get('assets_url') + '/images/layout/default_marker.png',
iconAnchor: this.model.get('iconAnchor') || [11, 11]
};
this.geom = L.GeoJSON.geometryToLayer(geometryModel.get('geojson'), function(geojson, latLng) {
//TODO: create marker depending on the visualizacion options
var p = L.marker(latLng, {
icon: L.icon(icon)
});
var i;
for(i = 0; i < events.length; ++i) {
var e = events[i];
p.on(e, self._eventHandler(e));
}
return p;
});
this.bind('dragend', function(e, pos) {
geometryModel.set({
geojson: {
type: 'Point',
//geojson is lng,lat
coordinates: [pos[1], pos[0]]
}
});
});
}
PointView.prototype = new GeometryView();
PointView.prototype.edit = function() {
this.geom.dragging.enable();
};
/**
* returns a function to handle events fot evtType
*/
PointView.prototype._eventHandler = function(evtType) {
var self = this;
var h = this._eventHandlers[evtType];
if(!h) {
h = function(e) {
var latlng = e.target.getLatLng();
var s = [latlng.lat, latlng.lng];
self.trigger(evtType, e.originalEvent, s);
};
this._eventHandlers[evtType] = h;
}
return h;
};
/**
* view for other geometries (polygons/lines)
*/
function PathView(geometryModel) {
var self = this;
// events to link
var events = [
'click',
'dblclick',
'mousedown',
'mouseover',
'mouseout',
];
this._eventHandlers = {};
this.model = geometryModel;
this.points = [];
this.geom = L.GeoJSON.geometryToLayer(geometryModel.get('geojson'));
this.geom.setStyle(geometryModel.get('style'));
/*for(var i = 0; i < events.length; ++i) {
var e = events[i];
this.geom.on(e, self._eventHandler(e));
}*/
}
PathView.prototype = new GeometryView();
PathView.prototype._leafletLayers = function() {
// check if this is a multi-feature or single-feature
if (this.geom.getLayers) {
return this.geom.getLayers();
}
return [this.geom];
};
PathView.prototype.enableEdit = function() {
var self = this;
var layers = this._leafletLayers();
_.each(layers, function(g) {
g.setStyle(self.model.get('style'));
g.on('edit', function() {
self.model.set('geojson', self.geom.toGeoJSON().geometry);
}, self);
});
};
PathView.prototype.disableEdit = function() {
var self = this;
var layers = this._leafletLayers();
_.each(layers, function(g) {
g.off('edit', null, self);
});
};
PathView.prototype.edit = function(enable) {
var self = this;
var fn = enable ? 'enable': 'disable';
var layers = this._leafletLayers();
_.each(layers, function(g) {
g.editing[fn]();
enable ? self.enableEdit(): self.disableEdit();
});
};
cdb.geo.leaflet = cdb.geo.leaflet || {};
cdb.geo.leaflet.PointView = PointView;
cdb.geo.leaflet.PathView = PathView;
})();
@@ -0,0 +1,426 @@
/**
* leaflet implementation of a map
*/
(function() {
if(typeof(L) == "undefined")
return;
/**
* leatlef impl
*/
cdb.geo.LeafletMapView = cdb.geo.MapView.extend({
initialize: function() {
_.bindAll(this, '_addLayer', '_removeLayer', '_setZoom', '_setCenter', '_setView');
cdb.geo.MapView.prototype.initialize.call(this);
var self = this;
var center = this.map.get('center');
var mapConfig = {
zoomControl: false,
center: new L.LatLng(center[0], center[1]),
zoom: this.map.get('zoom'),
minZoom: this.map.get('minZoom'),
maxZoom: this.map.get('maxZoom')
};
if (this.map.get('bounding_box_ne')) {
//mapConfig.maxBounds = [this.map.get('bounding_box_ne'), this.map.get('bounding_box_sw')];
}
if (!this.options.map_object) {
this.map_leaflet = new L.Map(this.el, mapConfig);
// remove the "powered by leaflet"
this.map_leaflet.attributionControl.setPrefix('');
// Disable scrollwheel
if (this.map.get("scrollwheel") == false) this.map_leaflet.scrollWheelZoom.disable();
// Disable keyboard
if (this.map.get("keyboard") == false) this.map_leaflet.keyboard.disable();
// Disable dragging (also doubleClickZoom)
if (this.map.get("drag") == false) {
this.map_leaflet.dragging.disable();
this.map_leaflet.doubleClickZoom.disable();
}
} else {
this.map_leaflet = this.options.map_object;
this.setElement(this.map_leaflet.getContainer());
var c = self.map_leaflet.getCenter();
self._setModelProperty({ center: [c.lat, c.lng] });
self._setModelProperty({ zoom: self.map_leaflet.getZoom() });
// unset bounds to not change mapbounds
self.map.unset('view_bounds_sw', { silent: true });
self.map.unset('view_bounds_ne', { silent: true });
}
this.map.bind('set_view', this._setView, this);
this.map.layers.bind('add', this._addLayer, this);
this.map.layers.bind('remove', this._removeLayer, this);
this.map.layers.bind('reset', this._addLayers, this);
this.map.layers.bind('change:type', this._swicthLayerView, this);
this.map.geometries.bind('add', this._addGeometry, this);
this.map.geometries.bind('remove', this._removeGeometry, this);
this._bindModel();
this._addLayers();
this.setAttribution();
this.map_leaflet.on('layeradd', function(lyr) {
this.trigger('layeradd', lyr, self);
}, this);
this.map_leaflet.on('zoomstart', function() {
self.trigger('zoomstart');
});
this.map_leaflet.on('click', function(e) {
self.trigger('click', e.originalEvent, [e.latlng.lat, e.latlng.lng]);
});
this.map_leaflet.on('dblclick', function(e) {
self.trigger('dblclick', e.originalEvent);
});
this.map_leaflet.on('zoomend', function() {
self._setModelProperty({
zoom: self.map_leaflet.getZoom()
});
self.trigger('zoomend');
}, this);
this.map_leaflet.on('move', function() {
var c = self.map_leaflet.getCenter();
self._setModelProperty({ center: [c.lat, c.lng] });
});
this.map_leaflet.on('dragend', function() {
var c = self.map_leaflet.getCenter();
this.trigger('dragend', [c.lat, c.lng]);
}, this);
this.map_leaflet.on('drag', function() {
var c = self.map_leaflet.getCenter();
self._setModelProperty({
center: [c.lat, c.lng]
});
self.trigger('drag');
}, this);
this.map.bind('change:maxZoom', function() {
L.Util.setOptions(self.map_leaflet, { maxZoom: self.map.get('maxZoom') });
}, this);
this.map.bind('change:minZoom', function() {
L.Util.setOptions(self.map_leaflet, { minZoom: self.map.get('minZoom') });
}, this);
this.trigger('ready');
// looks like leaflet dont like to change the bounds just after the inicialization
var bounds = this.map.getViewBounds();
if (bounds) {
this.showBounds(bounds);
}
},
// this replaces the default functionality to search for
// already added views so they are not replaced
_addLayers: function() {
var self = this;
var oldLayers = this.layers;
this.layers = {};
function findLayerView(layer) {
var lv = _.find(oldLayers, function(layer_view) {
var m = layer_view.model;
return m.isEqual(layer);
});
return lv;
}
function canReused(layer) {
return self.map.layers.find(function(m) {
return m.isEqual(layer);
});
}
// remove all
for(var layer in oldLayers) {
var layer_view = oldLayers[layer];
if (!canReused(layer_view.model)) {
layer_view.remove();
}
}
this.map.layers.each(function(lyr) {
var lv = findLayerView(lyr);
if (!lv) {
self._addLayer(lyr);
} else {
lv.setModel(lyr);
self.layers[lyr.cid] = lv;
self.trigger('newLayerView', lv, lv.model, self);
}
});
},
clean: function() {
//see https://github.com/CloudMade/Leaflet/issues/1101
L.DomEvent.off(window, 'resize', this.map_leaflet._onResize, this.map_leaflet);
// remove layer views
for(var layer in this.layers) {
var layer_view = this.layers[layer];
layer_view.remove();
delete this.layers[layer];
}
// do not change by elder
cdb.core.View.prototype.clean.call(this);
},
_setKeyboard: function(model, z) {
if (z) {
this.map_leaflet.keyboard.enable();
} else {
this.map_leaflet.keyboard.disable();
}
},
_setScrollWheel: function(model, z) {
if (z) {
this.map_leaflet.scrollWheelZoom.enable();
} else {
this.map_leaflet.scrollWheelZoom.disable();
}
},
_setZoom: function(model, z) {
this._setView();
},
_setCenter: function(model, center) {
this._setView();
},
_setView: function() {
this.map_leaflet.setView(this.map.get("center"), this.map.get("zoom") || 0 );
},
_addGeomToMap: function(geom) {
var geo = cdb.geo.LeafletMapView.createGeometry(geom);
geo.geom.addTo(this.map_leaflet);
return geo;
},
_removeGeomFromMap: function(geo) {
this.map_leaflet.removeLayer(geo.geom);
},
createLayer: function(layer) {
return cdb.geo.LeafletMapView.createLayer(layer, this.map_leaflet);
},
_addLayer: function(layer, layers, opts) {
var self = this;
var lyr, layer_view;
layer_view = cdb.geo.LeafletMapView.createLayer(layer, this.map_leaflet);
if (!layer_view) {
return;
}
return this._addLayerToMap(layer_view, opts);
},
_addLayerToMap: function(layer_view, opts) {
var layer = layer_view.model;
this.layers[layer.cid] = layer_view;
cdb.geo.LeafletMapView.addLayerToMap(layer_view, this.map_leaflet);
// reorder layers
for(var i in this.layers) {
var lv = this.layers[i];
lv.setZIndex(lv.model.get('order'));
}
if(opts === undefined || !opts.silent) {
this.trigger('newLayerView', layer_view, layer_view.model, this);
}
return layer_view;
},
pixelToLatLon: function(pos) {
var point = this.map_leaflet.containerPointToLatLng([pos[0], pos[1]]);
return point;
},
latLonToPixel: function(latlon) {
var point = this.map_leaflet.latLngToLayerPoint(new L.LatLng(latlon[0], latlon[1]));
return this.map_leaflet.layerPointToContainerPoint(point);
},
// return the current bounds of the map view
getBounds: function() {
var b = this.map_leaflet.getBounds();
var sw = b.getSouthWest();
var ne = b.getNorthEast();
return [
[sw.lat, sw.lng],
[ne.lat, ne.lng]
];
},
setAttribution: function() {
var attributionControl = this._getAttributionControl();
// Save the attributions that were in the map the first time a new layer
// is added and the attributions of the map have changed
if (!this._originalAttributions) {
this._originalAttributions = Object.keys(attributionControl._attributions);
}
// Clear the attributions and re-add the original and custom attributions in
// the order we want
attributionControl._attributions = {};
var newAttributions = this._originalAttributions.concat(this.map.get('attribution'));
_.each(newAttributions, function(attribution) {
attributionControl.addAttribution(cdb.core.sanitize.html(attribution));
});
},
_getAttributionControl: function() {
if (this._attributionControl) {
return this._attributionControl;
}
this._attributionControl = this.map_leaflet.attributionControl;
if (!this._attributionControl) {
this._attributionControl = L.control.attribution({ prefix: '' });
this.map_leaflet.addControl(this._attributionControl);
}
return this._attributionControl;
},
getSize: function() {
return this.map_leaflet.getSize();
},
panBy: function(p) {
this.map_leaflet.panBy(new L.Point(p.x, p.y));
},
setCursor: function(cursor) {
$(this.map_leaflet.getContainer()).css('cursor', cursor);
},
getNativeMap: function() {
return this.map_leaflet;
},
invalidateSize: function() {
// there is a race condition in leaflet. If size is invalidated
// and at the same time the center is set the final center is displaced
// so set pan to false so the map is not moved and then force the map
// to be at the place it should be
this.map_leaflet.invalidateSize({ pan: false })//, animate: false });
this.map_leaflet.setView(this.map.get("center"), this.map.get("zoom") || 0, {
animate: false
});
}
}, {
layerTypeMap: {
"tiled": cdb.geo.LeafLetTiledLayerView,
"wms": cdb.geo.LeafLetWMSLayerView,
"cartodb": cdb.geo.LeafLetLayerCartoDBView,
"carto": cdb.geo.LeafLetLayerCartoDBView,
"plain": cdb.geo.LeafLetPlainLayerView,
// Substitutes the GMaps baselayer w/ an equivalent Leaflet tiled layer, since not supporting Gmaps anymore
"gmapsbase": cdb.geo.LeafLetGmapsTiledLayerView,
"layergroup": cdb.geo.LeafLetCartoDBLayerGroupView,
"namedmap": cdb.geo.LeafLetCartoDBNamedMapView,
"torque": function(layer, map) {
return new cdb.geo.LeafLetTorqueLayer(layer, map);
}
},
createLayer: function(layer, map) {
var layer_view = null;
var layerClass = this.layerTypeMap[layer.get('type').toLowerCase()];
if (layerClass) {
try {
layer_view = new layerClass(layer, map);
} catch(e) {
cdb.log.error("MAP: error creating '" + layer.get('type') + "' layer -> " + e.message);
}
} else {
cdb.log.error("MAP: " + layer.get('type') + " can't be created");
}
return layer_view;
},
addLayerToMap: function(layer_view, map, pos) {
map.addLayer(layer_view.leafletLayer);
if(pos !== undefined) {
if (layer_view.setZIndex) {
layer_view.setZIndex(pos);
}
}
},
/**
* create the view for the geometry model
*/
createGeometry: function(geometryModel) {
if(geometryModel.isPoint()) {
return new cdb.geo.leaflet.PointView(geometryModel);
}
return new cdb.geo.leaflet.PathView(geometryModel);
}
});
// set the image path in order to be able to get leaflet icons
// code adapted from leaflet
L.Icon.Default.imagePath = (function () {
var scripts = document.getElementsByTagName('script'),
leafletRe = /\/?cartodb[\-\._]?([\w\-\._]*)\.js\??/;
var i, len, src, matches;
for (i = 0, len = scripts.length; i < len; i++) {
src = scripts[i].src;
matches = src.match(leafletRe);
if (matches) {
var bits = src.split('/')
delete bits[bits.length - 1];
return bits.join('/') + 'themes/css/images';
}
}
}());
})();
@@ -0,0 +1,61 @@
(function() {
/**
* base layer for all leaflet layers
*/
var LeafLetLayerView = function(layerModel, leafletLayer, leafletMap) {
this.leafletLayer = leafletLayer;
this.leafletMap = leafletMap;
this.model = layerModel;
this.setModel(layerModel);
this.type = layerModel.get('type') || layerModel.get('kind');
this.type = this.type.toLowerCase();
};
_.extend(LeafLetLayerView.prototype, Backbone.Events);
_.extend(LeafLetLayerView.prototype, {
setModel: function(model) {
if (this.model) {
this.model.unbind('change', this._modelUpdated, this);
}
this.model = model;
this.model.bind('change', this._modelUpdated, this);
},
/**
* remove layer from the map and unbind events
*/
remove: function() {
this.leafletMap.removeLayer(this.leafletLayer);
this.trigger('remove', this);
this.model.unbind(null, null, this);
this.unbind();
},
/*
show: function() {
this.leafletLayer.setOpacity(1.0);
},
hide: function() {
this.leafletLayer.setOpacity(0.0);
},
*/
/**
* reload the tiles
*/
reload: function() {
this.leafletLayer.redraw();
}
});
cdb.geo.LeafLetLayerView = LeafLetLayerView;
})();
@@ -0,0 +1,163 @@
(function() {
if(typeof(L) == "undefined")
return;
L.CartoDBLayer = L.CartoDBGroupLayer.extend({
options: {
query: "SELECT * FROM {{table_name}}",
opacity: 0.99,
attribution: cdb.config.get('cartodb_attributions'),
debug: false,
visible: true,
added: false,
extra_params: {},
layer_definition_version: '1.0.0'
},
initialize: function (options) {
L.Util.setOptions(this, options);
if (!options.table_name || !options.user_name || !options.tile_style) {
throw ('cartodb-leaflet needs at least a CartoDB table name, user_name and tile_style');
}
L.CartoDBGroupLayer.prototype.initialize.call(this, {
layer_definition: {
version: this.options.layer_definition_version,
layers: [{
type: 'cartodb',
options: this._getLayerDefinition(),
infowindow: this.options.infowindow
}]
}
});
this.setOptions(this.options);
},
setQuery: function(layer, sql) {
if(sql === undefined) {
sql = layer;
layer = 0;
}
sql = sql || 'select * from ' + this.options.table_name;
LayerDefinition.prototype.setQuery.call(this, layer, sql);
},
/**
* Returns if the layer is visible or not
*/
isVisible: function() {
return this.visible;
},
/**
* Returns if the layer belongs to the map
*/
isAdded: function() {
return this.options.added;
}
});
/**
* leatlet cartodb layer
*/
var LeafLetLayerCartoDBView = L.CartoDBLayer.extend({
//var LeafLetLayerCartoDBView = function(layerModel, leafletMap) {
initialize: function(layerModel, leafletMap) {
var self = this;
_.bindAll(this, 'featureOut', 'featureOver', 'featureClick');
var opts = _.clone(layerModel.attributes);
opts.map = leafletMap;
var // preserve the user's callbacks
_featureOver = opts.featureOver,
_featureOut = opts.featureOut,
_featureClick = opts.featureClick;
opts.featureOver = function() {
_featureOver && _featureOver.apply(this, arguments);
self.featureOver && self.featureOver.apply(this, arguments);
};
opts.featureOut = function() {
_featureOut && _featureOut.apply(this, arguments);
self.featureOut && self.featureOut.apply(this, arguments);
};
opts.featureClick = function() {
_featureClick && _featureClick.apply(this, arguments);
self.featureClick && self.featureClick.apply(opts, arguments);
};
layerModel.bind('change:visible', function() {
self.model.get('visible') ? self.show(): self.hide();
}, this);
L.CartoDBLayer.prototype.initialize.call(this, opts);
cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap);
},
_modelUpdated: function() {
var attrs = _.clone(this.model.attributes);
this.leafletLayer.setOptions(attrs);
},
featureOver: function(e, latlon, pixelPos, data) {
// dont pass leaflet lat/lon
this.trigger('featureOver', e, [latlon.lat, latlon.lng], pixelPos, data, 0);
},
featureOut: function(e) {
this.trigger('featureOut', e, 0);
},
featureClick: function(e, latlon, pixelPos, data) {
// dont pass leaflet lat/lon
this.trigger('featureClick', e, [latlon.lat, latlon.lng], pixelPos, data, 0);
},
reload: function() {
this.model.invalidate();
//this.redraw();
},
error: function(e) {
this.trigger('error', e?e.error:'unknown error');
this.model.trigger('tileError', e?e.error:'unknown error');
},
tilesOk: function(e) {
this.model.trigger('tileOk');
},
includes: [
cdb.geo.LeafLetLayerView.prototype,
Backbone.Events
]
});
/*_.extend(L.CartoDBLayer.prototype, CartoDBLayerCommon.prototype);
_.extend(
LeafLetLayerCartoDBView.prototype,
cdb.geo.LeafLetLayerView.prototype,
L.CartoDBLayer.prototype,
Backbone.Events, // be sure this is here to not use the on/off from leaflet
*/
cdb.geo.LeafLetLayerCartoDBView = LeafLetLayerCartoDBView;
})();
@@ -0,0 +1,455 @@
(function() {
if(typeof(L) == "undefined")
return;
L.CartoDBGroupLayerBase = L.TileLayer.extend({
interactionClass: wax.leaf.interaction,
includes: [
cdb.geo.LeafLetLayerView.prototype,
//LayerDefinition.prototype,
CartoDBLayerCommon.prototype
],
options: {
opacity: 0.99,
attribution: cdb.config.get('cartodb_attributions'),
debug: false,
visible: true,
added: false,
tiler_domain: "carto.com",
tiler_port: "80",
tiler_protocol: "http",
sql_api_domain: "carto.com",
sql_api_port: "80",
sql_api_protocol: "http",
maxZoom: 30, // default leaflet zoom level for a layers is 18, raise it
extra_params: {
},
cdn_url: null,
subdomains: null
},
initialize: function (options) {
options = options || {};
// Set options
L.Util.setOptions(this, options);
// Some checks
if (!options.layer_definition && !options.sublayers) {
throw new Error('cartodb-leaflet needs at least the layer_definition or sublayer list');
}
if(!options.layer_definition) {
this.options.layer_definition = LayerDefinition.layerDefFromSubLayers(options.sublayers);
}
LayerDefinition.call(this, this.options.layer_definition, this.options);
this.fire = this.trigger;
CartoDBLayerCommon.call(this);
L.TileLayer.prototype.initialize.call(this);
this.interaction = [];
this.addProfiling();
},
addProfiling: function() {
this.bind('tileloadstart', function(e) {
var s = this.tileStats || (this.tileStats = {});
s[e.tile.src] = cartodb.core.Profiler.metric('cartodb-js.tile.png.load.time').start();
});
var finish = function(e) {
var s = this.tileStats && this.tileStats[e.tile.src];
s && s.end();
};
this.bind('tileload', finish);
this.bind('tileerror', function(e) {
cartodb.core.Profiler.metric('cartodb-js.tile.png.error').inc();
finish(e);
});
},
// overwrite getTileUrl in order to
// support different tiles subdomains in tilejson way
getTileUrl: function (tilePoint) {
var EMPTY_GIF = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
this._adjustTilePoint(tilePoint);
var tiles = [EMPTY_GIF];
if(this.tilejson) {
tiles = this.tilejson.tiles;
}
var index = (tilePoint.x + tilePoint.y) % tiles.length;
return L.Util.template(tiles[index], L.Util.extend({
z: this._getZoomForUrl(),
x: tilePoint.x,
y: tilePoint.y
}, this.options));
},
/**
* Change opacity of the layer
* @params {Integer} New opacity
*/
setOpacity: function(opacity) {
if (isNaN(opacity) || opacity>1 || opacity<0) {
throw new Error(opacity + ' is not a valid value');
}
// Leaflet only accepts 0-0.99... Weird!
this.options.opacity = Math.min(opacity, 0.99);
if (this.options.visible) {
L.TileLayer.prototype.setOpacity.call(this, this.options.opacity);
this.fire('updated');
}
},
/**
* When Leaflet adds the layer... go!
* @params {map}
*/
onAdd: function(map) {
var self = this;
this.options.map = map;
// Add cartodb logo
if (this.options.cartodb_logo != false)
cdb.geo.common.CartoDBLogo.addWadus({ left:8, bottom:8 }, 0, map._container);
this.__update(function() {
// if while the layer was processed in the server is removed
// it should not be added to the map
var id = L.stamp(self);
if (!map._layers[id]) {
return;
}
L.TileLayer.prototype.onAdd.call(self, map);
self.fire('added');
self.options.added = true;
});
},
getAttribution: function() {
return cdb.core.sanitize.html(this.options.attribution);
},
/**
* When removes the layer, destroy interactivity if exist
*/
onRemove: function(map) {
if(this.options.added) {
this.options.added = false;
L.TileLayer.prototype.onRemove.call(this, map);
}
},
/**
* Update CartoDB layer
* generates a new url for tiles and refresh leaflet layer
* do not collide with leaflet _update
*/
__update: function(done) {
var self = this;
this.fire('updated');
this.fire('loading');
var map = this.options.map;
this.getTiles(function(urls, err) {
if(urls) {
self.tilejson = urls;
self.setUrl(self.tilejson.tiles[0]);
// manage interaction
self._reloadInteraction();
self.ok && self.ok();
done && done();
} else {
self.error && self.error(err);
done && done();
}
});
},
_checkLayer: function() {
if (!this.options.added) {
throw new Error('the layer is not still added to the map');
}
},
/**
* Set a new layer attribution
* @params {String} New attribution string
*/
setAttribution: function(attribution) {
this._checkLayer();
// Remove old one
this.map.attributionControl.removeAttribution(
cdb.core.sanitize.html(this.options.attribution)
);
// Change text
this.map.attributionControl.addAttribution(
cdb.core.sanitize.html(attribution)
);
// Set new attribution in the options
this.options.attribution = attribution;
// Change in the layer
this.tilejson.attribution = this.options.attribution;
this.fire('updated');
},
/**
* Bind events for wax interaction
* @param {Object} Layer map object
* @param {Event} Wax event
*/
_manageOnEvents: function(map, o) {
var layer_point = this._findPos(map,o);
if (!layer_point || isNaN(layer_point.x) || isNaN(layer_point.y)) {
// If layer_point doesn't contain x and y,
// we can't calculate event map position
return false;
}
var latlng = map.layerPointToLatLng(layer_point);
var event_type = o.e.type.toLowerCase();
var screenPos = map.layerPointToContainerPoint(layer_point);
switch (event_type) {
case 'mousemove':
if (this.options.featureOver) {
return this.options.featureOver(o.e,latlng, screenPos, o.data, o.layer);
}
break;
case 'click':
case 'touchend':
case 'touchmove': // for some reason android browser does not send touchend
case 'mspointerup':
case 'pointerup':
case 'pointermove':
if (this.options.featureClick) {
this.options.featureClick(o.e,latlng, screenPos, o.data, o.layer);
}
break;
default:
break;
}
},
/**
* Bind off event for wax interaction
*/
_manageOffEvents: function(map, o) {
if (this.options.featureOut) {
return this.options.featureOut && this.options.featureOut(o.e, o.layer);
}
},
/**
* Get the Leaflet Point of the event
* @params {Object} Map object
* @params {Object} Wax event object
*/
_findPos: function (map, o) {
var curleft = 0;
var curtop = 0;
var obj = map.getContainer();
var x, y;
if (o.e.changedTouches && o.e.changedTouches.length > 0) {
x = o.e.changedTouches[0].clientX + window.scrollX;
y = o.e.changedTouches[0].clientY + window.scrollY;
} else {
x = o.e.clientX;
y = o.e.clientY;
}
// If the map is fixed at the top of the window, we can't use offsetParent
// cause there might be some scrolling that we need to take into account.
if (obj.offsetParent && obj.offsetTop > 0) {
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
} while (obj = obj.offsetParent);
var point = this._newPoint(
x - curleft, y - curtop);
} else {
var rect = obj.getBoundingClientRect();
var scrollX = (window.scrollX || window.pageXOffset);
var scrollY = (window.scrollY || window.pageYOffset);
var point = this._newPoint(
(o.e.clientX? o.e.clientX: x) - rect.left - obj.clientLeft - scrollX,
(o.e.clientY? o.e.clientY: y) - rect.top - obj.clientTop - scrollY);
}
return map.containerPointToLayerPoint(point);
},
/**
* Creates an instance of a Leaflet Point
*/
_newPoint: function(x, y) {
return new L.Point(x, y);
}
});
L.CartoDBGroupLayer = L.CartoDBGroupLayerBase.extend({
includes: [
LayerDefinition.prototype,
],
_modelUpdated: function() {
this.setLayerDefinition(this.model.get('layer_definition'));
}
});
function layerView(base) {
var layerViewClass = base.extend({
includes: [
cdb.geo.LeafLetLayerView.prototype,
Backbone.Events
],
initialize: function(layerModel, leafletMap) {
var self = this;
var hovers = [];
var opts = _.clone(layerModel.attributes);
opts.map = leafletMap;
var // preserve the user's callbacks
_featureOver = opts.featureOver,
_featureOut = opts.featureOut,
_featureClick = opts.featureClick;
var previousEvent;
var eventTimeout = -1;
opts.featureOver = function(e, latlon, pxPos, data, layer) {
if (!hovers[layer]) {
self.trigger('layerenter', e, latlon, pxPos, data, layer);
}
hovers[layer] = 1;
_featureOver && _featureOver.apply(this, arguments);
self.featureOver && self.featureOver.apply(self, arguments);
// if the event is the same than before just cancel the event
// firing because there is a layer on top of it
if (e.timeStamp === previousEvent) {
clearTimeout(eventTimeout);
}
eventTimeout = setTimeout(function() {
self.trigger('mouseover', e, latlon, pxPos, data, layer);
self.trigger('layermouseover', e, latlon, pxPos, data, layer);
}, 0);
previousEvent = e.timeStamp;
};
opts.featureOut = function(m, layer) {
if (hovers[layer]) {
self.trigger('layermouseout', layer);
}
hovers[layer] = 0;
if(!_.any(hovers)) {
self.trigger('mouseout');
}
_featureOut && _featureOut.apply(this, arguments);
self.featureOut && self.featureOut.apply(self, arguments);
};
opts.featureClick = _.debounce(function() {
_featureClick && _featureClick.apply(self, arguments);
self.featureClick && self.featureClick.apply(self, arguments);
}, 10);
base.prototype.initialize.call(this, opts);
cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap);
},
featureOver: function(e, latlon, pixelPos, data, layer) {
// dont pass leaflet lat/lon
this.trigger('featureOver', e, [latlon.lat, latlon.lng], pixelPos, data, layer);
},
featureOut: function(e, layer) {
this.trigger('featureOut', e, layer);
},
featureClick: function(e, latlon, pixelPos, data, layer) {
// dont pass leaflet lat/lon
this.trigger('featureClick', e, [latlon.lat, latlon.lng], pixelPos, data, layer);
},
error: function(e) {
this.trigger('error', e ? (e.errors || e) : 'unknown error');
this.model.trigger('error', e?e.errors:'unknown error');
},
ok: function(e) {
this.model.trigger('tileOk');
},
onLayerDefinitionUpdated: function() {
this.__update();
}
});
return layerViewClass;
}
L.NamedMap = L.CartoDBGroupLayerBase.extend({
includes: [
cdb.geo.LeafLetLayerView.prototype,
NamedMap.prototype,
CartoDBLayerCommon.prototype
],
initialize: function (options) {
options = options || {};
// Set options
L.Util.setOptions(this, options);
// Some checks
if (!options.named_map && !options.sublayers) {
throw new Error('cartodb-leaflet needs at least the named_map');
}
NamedMap.call(this, this.options.named_map, this.options);
this.fire = this.trigger;
CartoDBLayerCommon.call(this);
L.TileLayer.prototype.initialize.call(this);
this.interaction = [];
this.addProfiling();
},
_modelUpdated: function() {
this.setLayerDefinition(this.model.get('named_map'));
}
});
cdb.geo.LeafLetCartoDBLayerGroupView = layerView(L.CartoDBGroupLayer);
cdb.geo.LeafLetCartoDBNamedMapView = layerView(L.NamedMap);
})();
@@ -0,0 +1,63 @@
(function() {
if(typeof(L) == "undefined")
return;
var stamenSubstitute = function stamenSubstitute(type) {
return {
url: 'http://{s}.basemaps.cartocdn.com/'+ type +'_all/{z}/{x}/{y}.png',
subdomains: 'abcd',
minZoom: 0,
maxZoom: 18,
attribution: 'Map designs by <a href="http://stamen.com/">Stamen</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, Provided by <a href="https://carto.com">CARTO</a>'
};
};
var nokiaSubstitute = function nokiaSubstitute(type) {
return {
url: 'https://{s}.maps.nlp.nokia.com/maptile/2.1/maptile/newest/'+ type +'.day/{z}/{x}/{y}/256/png8?lg=eng&token=A7tBPacePg9Mj_zghvKt9Q&app_id=KuYppsdXZznpffJsKT24',
subdomains: '1234',
minZoom: 0,
maxZoom: 21,
attribution: '©2012 Nokia <a href="http://here.net/services/terms" target="_blank">Terms of use</a>'
};
};
var substitutes = {
roadmap: nokiaSubstitute('normal'),
gray_roadmap: stamenSubstitute('light'),
dark_roadmap: stamenSubstitute('dark'),
hybrid: nokiaSubstitute('hybrid'),
terrain: nokiaSubstitute('terrain'),
satellite: nokiaSubstitute('satellite')
};
var LeafLetGmapsTiledLayerView = L.TileLayer.extend({
initialize: function(layerModel, leafletMap) {
var substitute = substitutes[layerModel.get('base_type')];
L.TileLayer.prototype.initialize.call(this, substitute.url, {
tms: false,
attribution: substitute.attribution,
minZoom: substitute.minZoom,
maxZoom: substitute.maxZoom,
subdomains: substitute.subdomains,
errorTileUrl: '',
opacity: 1
});
cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap);
}
});
_.extend(LeafLetGmapsTiledLayerView.prototype, cdb.geo.LeafLetLayerView.prototype, {
_modelUpdated: function() {
// do nothing, this map type does not support updating
}
});
cdb.geo.LeafLetGmapsTiledLayerView = LeafLetGmapsTiledLayerView;
})();
@@ -0,0 +1,51 @@
(function() {
if(typeof(L) == "undefined")
return;
/**
* this is a dummy layer class that modifies the leaflet DOM element background
* instead of creating a layer with div
*/
var LeafLetPlainLayerView = L.Class.extend({
includes: L.Mixin.Events,
initialize: function(layerModel, leafletMap) {
cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap);
},
onAdd: function() {
this.redraw();
},
onRemove: function() {
var div = this.leafletMap.getContainer()
div.style.background = 'none';
},
_modelUpdated: function() {
this.redraw();
},
redraw: function() {
var div = this.leafletMap.getContainer()
div.style.backgroundColor = this.model.get('color') || '#FFF';
if (this.model.get('image')) {
var st = 'transparent url(' + this.model.get('image') + ') repeat center center';
div.style.background = st
}
},
// this method
setZIndex: function() {
}
});
_.extend(LeafLetPlainLayerView.prototype, cdb.geo.LeafLetLayerView.prototype);
cdb.geo.LeafLetPlainLayerView = LeafLetPlainLayerView;
})();
@@ -0,0 +1,50 @@
(function() {
if(typeof(L) == "undefined")
return;
var LeafLetTiledLayerView = L.TileLayer.extend({
initialize: function(layerModel, leafletMap) {
var tmpLayer = {
tms: layerModel.get('tms'),
attribution: layerModel.get('attribution'),
minZoom: layerModel.get('minZoom'),
maxZoom: layerModel.get('maxZoom'),
subdomains: layerModel.get('subdomains') || 'abc',
errorTileUrl: layerModel.get('errorTileUrl'),
opacity: layerModel.get('opacity')
};
if ( layerModel.get('tileSize') ) {
tmpLayer.tileSize = layerModel.get('tileSize');
}
if ( layerModel.get('zoomOffset') ) {
tmpLayer.zoomOffset = layerModel.get('zoomOffset');
}
L.TileLayer.prototype.initialize.call(this, layerModel.get('urlTemplate'), tmpLayer);
cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap);
}
});
_.extend(LeafLetTiledLayerView.prototype, cdb.geo.LeafLetLayerView.prototype, {
_modelUpdated: function() {
_.defaults(this.leafletLayer.options, _.clone(this.model.attributes));
this.leafletLayer.options.subdomains = this.model.get('subdomains') || 'abc';
this.leafletLayer.options.attribution = this.model.get('attribution');
this.leafletLayer.options.maxZoom = this.model.get('maxZoom');
this.leafletLayer.options.minZoom = this.model.get('minZoom');
// set url and reload
this.leafletLayer.setUrl(this.model.get('urlTemplate'));
}
});
cdb.geo.LeafLetTiledLayerView = LeafLetTiledLayerView;
})();
@@ -0,0 +1,48 @@
(function() {
if(typeof(L) == "undefined")
return;
var LeafLetWMSLayerView = L.TileLayer.WMS.extend({
initialize: function(layerModel, leafletMap) {
var tmpLayer = {
attribution: layerModel.get('attribution'),
layers: layerModel.get('layers'),
format: layerModel.get('format'),
transparent: layerModel.get('transparent'),
minZoom: layerModel.get('minZomm'),
maxZoom: layerModel.get('maxZoom'),
subdomains: layerModel.get('subdomains') || 'abc',
errorTileUrl: layerModel.get('errorTileUrl'),
opacity: layerModel.get('opacity')
};
if ( layerModel.get('tileSize') ) {
tmpLayer.tileSize = layerModel.get('tileSize');
}
if ( layerModel.get('zoomOffset') ) {
tmpLayer.zoomOffset = layerModel.get('zoomOffset');
}
L.TileLayer.WMS.prototype.initialize.call(this, layerModel.get('urlTemplate'), tmpLayer);
cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap);
}
});
_.extend(LeafLetWMSLayerView.prototype, cdb.geo.LeafLetLayerView.prototype, {
_modelUpdated: function() {
_.defaults(this.leafletLayer.options, _.clone(this.model.attributes));
this.leafletLayer.setUrl(this.model.get('urlTemplate'));
}
});
cdb.geo.LeafLetWMSLayerView = LeafLetWMSLayerView;
})();
@@ -0,0 +1,110 @@
(function() {
if(typeof(L) === "undefined")
return;
/**
* leaflet torque layer
*/
var LeafLetTorqueLayer = L.TorqueLayer.extend({
initialize: function(layerModel, leafletMap) {
var extra = layerModel.get('extra_params');
var query = this._getQuery(layerModel);
// initialize the base layers
L.TorqueLayer.prototype.initialize.call(this, {
table: layerModel.get('table_name'),
user: layerModel.get('user_name'),
column: layerModel.get('property'),
blendmode: layerModel.get('torque-blend-mode'),
resolution: 1,
//TODO: manage time columns
countby: 'count(cartodb_id)',
sql_api_domain: layerModel.get('sql_api_domain'),
sql_api_protocol: layerModel.get('sql_api_protocol'),
sql_api_port: layerModel.get('sql_api_port'),
tiler_protocol: layerModel.get('tiler_protocol'),
tiler_domain: layerModel.get('tiler_domain'),
tiler_port: layerModel.get('tiler_port'),
maps_api_template: layerModel.get('maps_api_template'),
stat_tag: layerModel.get('stat_tag'),
animationDuration: layerModel.get('torque-duration'),
steps: layerModel.get('torque-steps'),
sql: query,
visible: layerModel.get('visible'),
extra_params: {
api_key: extra ? extra.map_key: ''
},
cartodb_logo: layerModel.get('cartodb_logo'),
attribution: layerModel.get('attribution'),
cartocss: layerModel.get('cartocss') || layerModel.get('tile_style'),
named_map: layerModel.get('named_map'),
auth_token: layerModel.get('auth_token'),
no_cdn: layerModel.get('no_cdn'),
dynamic_cdn: layerModel.get('dynamic_cdn'),
loop: layerModel.get('loop') === false? false: true,
instanciateCallback: function() {
var cartocss = layerModel.get('cartocss') || layerModel.get('tile_style');
return '_cdbct_' + cdb.core.util.uniqueCallbackName(cartocss + query);
}
});
cdb.geo.LeafLetLayerView.call(this, layerModel, this, leafletMap);
// match leaflet events with backbone events
this.fire = this.trigger;
//this.setCartoCSS(layerModel.get('tile_style'));
if (layerModel.get('visible')) {
this.play();
}
this.bind('tilesLoaded', function() {
this.trigger('load');
}, this);
this.bind('tilesLoading', function() {
this.trigger('loading');
}, this);
},
onAdd: function(map) {
L.TorqueLayer.prototype.onAdd.apply(this, [map]);
// Add CartoDB logo
if (this.options.cartodb_logo != false)
cdb.geo.common.CartoDBLogo.addWadus({ left:8, bottom:8 }, 0, map._container)
},
_getQuery: function(layerModel) {
var query = layerModel.get('query');
var qw = layerModel.get('query_wrapper');
if(qw) {
query = _.template(qw)({ sql: query || ('select * from ' + layerModel.get('table_name')) });
}
return query;
},
_modelUpdated: function(model) {
var changed = this.model.changedAttributes();
if(changed === false) return;
changed.tile_style && this.setCartoCSS(this.model.get('tile_style'));
if ('query' in changed || 'query_wrapper' in changed) {
this.setSQL(this._getQuery(this.model));
}
if ('visible' in changed)
this.model.get('visible') ? this.show(): this.hide();
}
});
_.extend(LeafLetTorqueLayer.prototype, cdb.geo.LeafLetLayerView.prototype);
cdb.geo.LeafLetTorqueLayer = LeafLetTorqueLayer;
})();