L.Control.ResetView = L.Control.extend({ statics: { ICON: 'url(images/reset-view.png)', TITLE: "Reset view" }, options: { position: 'topleft' }, initialize: function (bounds, options) { // Accept function as argument to bounds this.getBounds = typeof(bounds) == 'function' ? bounds : function () { return bounds; }; L.Util.setOptions(this, options); }, onAdd: function (map) { if (map.resetviewControl) { map.removeControl(map.resetviewControl); } map.resetviewControl = this; var container = L.DomUtil.create('div', 'leaflet-control-zoom leaflet-bar'); var link = L.DomUtil.create('a', 'leaflet-control-zoom-out leaflet-bar-part', container); link.href = '#'; link.title = L.Control.ResetView.TITLE; link.style.backgroundImage = L.Control.ResetView.ICON; L.DomEvent.addListener(link, 'click', L.DomEvent.stopPropagation) .addListener(link, 'click', L.DomEvent.preventDefault) .addListener(link, 'click', L.Util.bind(function () { map.fitBounds(this.getBounds()); }, this)); return container; } }); L.Map.DjangoMap = L.Map.extend({ initialize: function (id, options) { // Merge compatible options // (can be undefined) var djoptions = options.djoptions; options.zoom = djoptions.zoom; options.center = djoptions.center; if (!isNaN(parseInt(djoptions.minzoom, 10))) options.minZoom = djoptions.minzoom; if (!isNaN(parseInt(djoptions.maxzoom, 10))) options.maxZoom = djoptions.maxzoom; // Translate to native options options = L.Util.extend(options, this._projectionOptions(djoptions)); if (djoptions.extent) { options.maxBounds = djoptions.extent; } L.Map.prototype.initialize.call(this, id, options); this._djAddLayers(); this._djSetupControls(); if (djoptions.fitextent && djoptions.extent && !(djoptions.center || djoptions.zoom)) { this.fitBounds(options.maxBounds); } }, _projectionOptions: function (djoptions) { if (!djoptions.srid) return {}; var projopts = {}; var bbox = djoptions.tilesextent, maxResolution = computeMaxResolution(bbox); // See https://github.com/ajashton/TileCache/blob/master/tilecache/TileCache/Layer.py#L197 var resolutions = []; for (var z = 0; z < 20; z++) { resolutions.push(maxResolution / Math.pow(2, z)); } if (L.CRS['EPSG' + djoptions.srid] !== undefined) { var crs = L.CRS['EPSG' + djoptions.srid]; } else { var crs = new L.Proj.CRS('EPSG:' + djoptions.srid, proj4.defs['EPSG:' + djoptions.srid], { origin: [bbox[0], bbox[3]], resolutions: resolutions } ); } return { crs: crs, scale: crs.scale, continuousWorld: true }; function computeMaxResolution(bbox) { // See https://github.com/ajashton/TileCache/blob/master/tilecache/TileCache/Layer.py#L185-L196 var size = 256, width = bbox[2] - bbox[0], height = bbox[3] - bbox[1]; var aspect = Math.floor(Math.max(width, height) / Math.min(width, height) + 0.5); return Math.max(width, height) / (size * aspect); } }, _djAddLayers: function () { var layers = this.options.djoptions.layers; var overlays = this.options.djoptions.overlays || []; var continuousWorld = this.options.continuousWorld; if (!layers || !layers.length) { // No layers, we're done (ignoring overlays) return; } if (layers.length == 1 && overlays.length == 0) { var layer = l2d(layers[0]); // Make the only layer match the map max/min_zoom layer.options = L.Util.extend(layer.options, { minZoom: this.options.minZoom, maxZoom: this.options.maxZoom }); L.tileLayer(layer.url, layer.options).addTo(this); return; } this.layerscontrol = L.control.layers().addTo(this); for (var i = 0, n = layers.length; i < n; i++) { var layer = l2d(layers[i]), l = L.tileLayer(layer.url, layer.options); this.layerscontrol.addBaseLayer(l, layer.name); // Show first one as default if (i === 0) l.addTo(this); } for (var i = 0, n = overlays.length; i < n; i++) { var layer = l2d(overlays[i]), l = L.tileLayer(layer.url, layer.options); this.layerscontrol.addOverlay(l, layer.name); } function l2d(l) { var options = {'continuousWorld': continuousWorld}; if (typeof l[2] === 'string') { // remain compatible with django-leaflet <= 0.15.0 options = L.Util.extend(options, {'attribution': l[2]}); } else { options = L.Util.extend(options, l[2]); } return {name: l[0], url: l[1], options: options}; } }, _djSetupControls: function () { // Attribution prefix ? if (this.attributionControl && this.options.djoptions.attributionprefix !== null) { this.attributionControl.setPrefix(this.options.djoptions.attributionprefix); } // Scale control ? if (this.options.djoptions.scale) { this.whenReady(function () { var scale_opt = this.options.djoptions.scale; var show_imperial = /both|imperial/.test(scale_opt); var show_metric = /both|metric/.test(scale_opt); new L.Control.Scale({imperial: show_imperial, metric: show_metric}).addTo(this); }, this); } // Minimap control ? if (this.options.djoptions.minimap) { for (var firstLayer in this._layers) break; var url = this._layers[firstLayer]._url; var layer = L.tileLayer(url); this.minimapcontrol = null; this.whenReady(function () { this.minimapcontrol = new L.Control.MiniMap(layer, {toggleDisplay: true}).addTo(this); }, this); } // ResetView control ? if (this.options.djoptions.resetview) { var bounds = this.options.djoptions.extent; if (bounds) { // Add reset view control this.whenReady(function () { new L.Control.ResetView(bounds).addTo(this); }, this); } } } }); L.Map.djangoMap = function (id, options) { var container = L.DomUtil.get(id); if (container._leaflet) // Already initialized return; var map = new L.Map.DjangoMap(id, options); if (options.globals) { // Register document maps, like window.forms :) window.maps = window.maps || []; window.maps.push(map); } if (options.callback === null) { /* * Deprecate django-leaflet < 0.7 default callback */ var defaultcb = window[id + 'Init']; if (typeof(defaultcb) == 'function') { options.callback = defaultcb; if (console) console.warn('DEPRECATED: Use of default callback ' + defaultcb.name + '() is deprecated (see documentation).'); } } /* * Trigger custom map:init Event */ triggerEvent(window, 'map:init', {id: id, map: map, options: options}); /* * Run callback if specified */ if (typeof(options.callback) == 'function') { options.callback(map, options); } return map; function triggerEvent(target, type, data) { if (typeof window.CustomEvent == 'function') { var evt = new CustomEvent(type, {detail: data}); target.dispatchEvent(evt); } else if (window.jQuery) { var evt = jQuery.Event(type); evt.detail = data; jQuery(target).trigger(evt); } } };