L.Control.MinZoomIndicator = L.Control.extend({ options: { position: 'bottomleft', }, /** * map: layerId -> zoomlevel */ _layers: {}, /** TODO check if nessesary */ initialize: function (options) { L.Util.setOptions(this, options); this._layers = new Object(); }, /** * adds a layer with minzoom information to this._layers */ _addLayer: function(layer) { var minzoom = 15; if (layer.options.minzoom) { minzoom = layer.options.minzoom; } this._layers[layer._leaflet_id] = minzoom; this._updateBox(null); }, /** * removes a layer from this._layers */ _removeLayer: function(layer) { this._layers[layer._leaflet_id] = null; this._updateBox(null); }, _getMinZoomLevel: function() { var minZoomlevel=-1; for(var key in this._layers) { if ((this._layers[key] != null)&&(this._layers[key] > minZoomlevel)) { minZoomlevel = this._layers[key]; } } return minZoomlevel; }, onAdd: function (map) { this._map = map; map.zoomIndicator = this; var className = this.className; var container = this._container = L.DomUtil.create('div', className); map.on('moveend', this._updateBox, this); this._updateBox(null); // L.DomEvent.disableClickPropagation(container); return container; }, onRemove: function(map) { L.Control.prototype.onRemove.call(this, map); map.off({ 'moveend': this._updateBox }, this); this._map = null; }, _updateBox: function (event) { //console.log("map moved -> update Container..."); if (event != null) { L.DomEvent.preventDefault(event); } var minzoomlevel = this._getMinZoomLevel(); if (minzoomlevel == -1) { this._container.innerHTML = this.options.minZoomMessageNoLayer; }else{ this._container.innerHTML = this.options.minZoomMessage .replace(/CURRENTZOOM/, this._map.getZoom()) .replace(/MINZOOMLEVEL/, minzoomlevel); } if (this._map.getZoom() >= minzoomlevel) { this._container.style.display = 'none'; }else{ this._container.style.display = 'block'; } }, className : 'leaflet-control-minZoomIndicator' }); L.LatLngBounds.prototype.toOverpassBBoxString = function (){ var a = this._southWest, b = this._northEast; return [a.lat, a.lng, b.lat, b.lng].join(","); } L.OverPassLayer = L.FeatureGroup.extend({ options: { debug: false, minzoom: 15, endpoint: "http://overpass-api.de/api/", query: "(node(BBOX)[organic];node(BBOX)[second_hand];);out qt;", callback: function(data) { for(var i = 0; i < data.elements.length; i++) { var e = data.elements[i]; if (e.id in this.instance._ids) return; this.instance._ids[e.id] = true; var pos; if (e.type == "node") { pos = new L.LatLng(e.lat, e.lon); } else { pos = new L.LatLng(e.center.lat, e.center.lon); } var popup = this.instance._poiInfo(e.tags,e.id); var circle = L.circle(pos, 50, { color: 'green', fillColor: '#3f0', fillOpacity: 0.5 }) .bindPopup(popup); this.instance.addLayer(circle); } }, beforeRequest: function() { if (this.options.debug) { console.debug('about to query the OverPassAPI'); } }, afterRequest: function() { if (this.options.debug) { console.debug('all queries have finished!'); } }, minZoomIndicatorOptions: { position: 'bottomleft', minZoomMessageNoLayer: "no layer assigned", minZoomMessage: "current Zoom-Level: CURRENTZOOM all data at Level: MINZOOMLEVEL" }, }, initialize: function (options) { L.Util.setOptions(this, options); this._layers = {}; // save position of the layer or any options from the constructor this._ids = {}; this._requested = {}; }, _poiInfo: function(tags,id) { var link = document.createElement("a"); link.href = "http://www.openstreetmap.org/edit?editor=id&node=" + id; link.appendChild(document.createTextNode("Edit this entry in iD")); var table = document.createElement('table'); for (var key in tags){ var row = table.insertRow(0); row.insertCell(0).appendChild(document.createTextNode(key)); row.insertCell(1).appendChild(document.createTextNode(tags[key])); } var div = document.createElement("div") div.appendChild(link); div.appendChild(table); return div; }, /** * splits the current view in uniform bboxes to allow caching */ long2tile: function (lon,zoom) { return (Math.floor((lon+180)/360*Math.pow(2,zoom))); }, lat2tile: function (lat,zoom) { return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom))); }, tile2long: function (x,z) { return (x/Math.pow(2,z)*360-180); }, tile2lat: function (y,z) { var n=Math.PI-2*Math.PI*y/Math.pow(2,z); return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n)))); }, _view2BBoxes: function(l,b,r,t) { //console.log(l+"\t"+b+"\t"+r+"\t"+t); //this.addBBox(l,b,r,t); //console.log("calc bboxes"); var requestZoomLevel= 14; //get left tile index var lidx = this.long2tile(l,requestZoomLevel); var ridx = this.long2tile(r,requestZoomLevel); var tidx = this.lat2tile(t,requestZoomLevel); var bidx = this.lat2tile(b,requestZoomLevel); //var result; var result = new Array(); for (var x=lidx; x<=ridx; x++) { for (var y=tidx; y<=bidx; y++) {//in tiles tidx<=bidx var left = Math.round(this.tile2long(x,requestZoomLevel)*1000000)/1000000; var right = Math.round(this.tile2long(x+1,requestZoomLevel)*1000000)/1000000; var top = Math.round(this.tile2lat(y,requestZoomLevel)*1000000)/1000000; var bottom = Math.round(this.tile2lat(y+1,requestZoomLevel)*1000000)/1000000; //console.log(left+"\t"+bottom+"\t"+right+"\t"+top); //this.addBBox(left,bottom,right,top); //console.log("http://osm.org?bbox="+left+","+bottom+","+right+","+top); result.push( new L.LatLngBounds(new L.LatLng(bottom, left),new L.LatLng(top, right))); } } //console.log(result); return result; }, addBBox: function (l,b,r,t) { var polygon = L.polygon([ [t, l], [b, l], [b, r], [t, r] ]).addTo(this._map); }, onMoveEnd: function () { if (this.options.debug) { console.debug("load Pois"); } //console.log(this._map.getBounds()); if (this._map.getZoom() >= this.options.minzoom) { //var bboxList = new Array(this._map.getBounds()); var bboxList = this._view2BBoxes( this._map.getBounds()._southWest.lng, this._map.getBounds()._southWest.lat, this._map.getBounds()._northEast.lng, this._map.getBounds()._northEast.lat); // controls the after/before (Request) callbacks var finishedCount = 0; var queryCount = bboxList.length; var beforeRequest = true; for (var i = 0; i < bboxList.length; i++) { var bbox = bboxList[i]; var x = bbox._southWest.lng; var y = bbox._northEast.lat; if ((x in this._requested) && (y in this._requested[x]) && (this._requested[x][y] == true)) { queryCount--; continue; } if (!(x in this._requested)) { this._requested[x] = {}; } this._requested[x][y] = true; var queryWithMapCoordinates = this.options.query.replace(/(BBOX)/g, bbox.toOverpassBBoxString()); var url = this.options.endpoint + "interpreter?data=[out:json];" + queryWithMapCoordinates; if (beforeRequest) { this.options.beforeRequest.call(this); beforeRequest = false; } var self = this; var request = new XMLHttpRequest(); request.open("GET", url, true); request.onload = function() { if (this.status >= 200 && this.status < 400) { var reference = {instance: self}; self.options.callback.call(reference, JSON.parse(this.response)); if (self.options.debug) { console.debug('queryCount: ' + queryCount + ' - finishedCount: ' + finishedCount); } if (++finishedCount == queryCount) { self.options.afterRequest.call(self); } } }; request.send(); } } }, onAdd: function (map) { this._map = map; if (map.zoomIndicator) { this._zoomControl = map.zoomIndicator; this._zoomControl._addLayer(this); }else{ this._zoomControl = new L.Control.MinZoomIndicator(this.options.minZoomIndicatorOptions); map.addControl(this._zoomControl); this._zoomControl._addLayer(this); } this.onMoveEnd(); if (this.options.query.indexOf("(BBOX)") != -1) { map.on('moveend', this.onMoveEnd, this); } if (this.options.debug) { console.debug("add layer"); } }, onRemove: function (map) { if (this.options.debug) { console.debug("remove layer"); } L.LayerGroup.prototype.onRemove.call(this, map); this._ids = {}; this._requested = {}; this._zoomControl._removeLayer(this); map.off({ 'moveend': this.onMoveEnd }, this); this._map = null; }, getData: function () { if (this.options.debug) { console.debug(this._data); } return this._data; } }); //FIXME no idea why the browser crashes with this code //L.OverPassLayer = function (options) { // return new L.OverPassLayer(options); //};