diff --git a/index.html b/index.html index 96bd401..7ab384a 100644 --- a/index.html +++ b/index.html @@ -363,14 +363,16 @@

Report Recovery

- - + + + + diff --git a/js/leaflet-markers-canvas.min.js b/js/leaflet-markers-canvas.min.js deleted file mode 100644 index a2d0e36..0000000 --- a/js/leaflet-markers-canvas.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("leaflet"),require("rbush")):"function"==typeof define&&define.amd?define(["leaflet","rbush"],t):t((e=e||self).L,e.RBush)}(this,function(o,e){"use strict";o=o&&Object.prototype.hasOwnProperty.call(o,"default")?o.default:o;var t={_map:null,_canvas:null,_context:null,_markers:[],_markersTree:new(e=e&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e),_positionsTree:new e,_icons:{},addTo:function(e){return e.addLayer(this),this},getBounds:function(){var t=new o.LatLngBounds;return this._markers.forEach(function(e){t.extend(e.getLatLng())}),t},redraw:function(){this._redraw(!0)},clear:function(){this._positionsTree=new e,this._markersTree=new e,this._markers=[],this._redraw(!0)},addMarker:function(e){var t=this._addMarker(e),i=t.markerBox,e=t.positionBox,t=t.isVisible;i&&t&&this._markersTree.insert(i),e&&this._positionsTree.insert(e)},addMarkers:function(e){var s=this,a=[],r=[];e.forEach(function(e){var t=s._addMarker(e),i=t.markerBox,e=t.positionBox,t=t.isVisible;i&&t&&a.push(i),e&&r.push(e)}),this._markersTree.load(a),this._positionsTree.load(r)},removeMarker:function(e){var t=e.getLatLng(),i=this._map.getBounds().contains(t),e={minX:t.lng,minY:t.lat,maxX:t.lng,maxY:t.lat,marker:e};this._positionsTree.remove(e,function(e,t){return e.marker._leaflet_id===t.marker._leaflet_id}),i&&this._redraw(!0)},initialize:function(e){o.Util.setOptions(this,e)},onAdd:function(e){this._map=e,this._initCanvas(),this.getPane().appendChild(this._canvas),e.on("moveend",this._reset,this),e.on("resize",this._reset,this),e.on("click",this._fire,this),e.on("mousemove",this._fire,this),e._zoomAnimated&&e.on("zoomanim",this._animateZoom,this)},onRemove:function(e){this.getPane().removeChild(this._canvas),e.off("click",this._fire,this),e.off("mousemove",this._fire,this),e.off("moveend",this._reset,this),e.off("resize",this._reset,this),e._zoomAnimated&&e.off("zoomanim",this._animateZoom,this)},setOptions:function(e){return o.Util.setOptions(this,e),this.redraw()},_initCanvas:function(){var e=this._map.getSize(),t=e.x,i=e.y,e=this._map.options.zoomAnimation&&o.Browser.any3d;this._canvas=o.DomUtil.create("canvas","leaflet-markers-canvas-layer leaflet-layer"),this._canvas.width=t,this._canvas.height=i,this._context=this._canvas.getContext("2d"),o.DomUtil.addClass(this._canvas,"leaflet-zoom-"+(e?"animated":"hide"))},_addMarker:function(e){if("markerPane"!==e.options.pane||!e.options.icon)return console.error("This is not a marker",e),{markerBox:null,positionBox:null,isVisible:null};e._map=this._map,o.Util.stamp(e);var t=e.getLatLng(),i=this._map.getBounds().contains(t),s=this._map.latLngToContainerPoint(t),a=s.x,r=s.y,n=e.options.icon.options,s=n.iconSize,n=n.iconAnchor,n={minX:a-n[0],minY:r-n[1],maxX:a+s[0]-n[0],maxY:r+s[1]-n[1],marker:e},t={minX:t.lng,minY:t.lat,maxX:t.lng,maxY:t.lat,marker:e};return i&&this._drawMarker(e,{x:a,y:r}),this._markers.push(e),{markerBox:n,positionBox:t,isVisible:i}},_drawMarker:function(e,t){var s=this,i=t.x,a=t.y,r=e.options.icon.options.iconUrl;e.image?this._drawImage(e,{x:i,y:a}):this._icons[r]?(e.image=this._icons[r].image,this._icons[r].isLoaded?this._drawImage(e,{x:i,y:a}):this._icons[r].elements.push({marker:e,x:i,y:a})):((t=new Image).src=r,e.image=t,this._icons[r]={image:t,isLoaded:!1,elements:[{marker:e,x:i,y:a}]},t.onload=function(){s._icons[r].isLoaded=!0,s._icons[r].elements.forEach(function(e){var t=e.marker,i=e.x,e=e.y;s._drawImage(t,{x:i,y:e})})})},_drawImage:function(e,t){var i=t.x,s=t.y,a=e.options.icon.options,r=a.rotationAngle,t=a.iconAnchor,a=a.iconSize,r=r||0;this._context.save(),this._context.translate(i,s),this._context.rotate(r*Math.PI/180),this._context.drawImage(e.image,-t[0],-t[1],a[0],a[1]),this._context.restore()},_redraw:function(e){var r,n=this;e&&this._context.clearRect(0,0,this._canvas.width,this._canvas.height),this._map&&this._positionsTree&&(e={minX:(e=this._map.getBounds()).getWest(),minY:e.getSouth(),maxX:e.getEast(),maxY:e.getNorth()},r=[],this._positionsTree.search(e).forEach(function(e){var t=e.marker,i=t.getLatLng(),s=n._map.latLngToContainerPoint(i),a=s.x,e=s.y,i=t.options.icon.options,s=i.iconSize,i=i.iconAnchor,i={minX:a-i[0],minY:e-i[1],maxX:a+s[0]-i[0],maxY:e+s[1]-i[1],marker:t};r.push(i),n._drawMarker(t,{x:a,y:e})}),this._markersTree.clear(),this._markersTree.load(r))},_reset:function(){var e=this._map.containerPointToLayerPoint([0,0]);o.DomUtil.setPosition(this._canvas,e);var t=this._map.getSize(),e=t.x,t=t.y;this._canvas.width=e,this._canvas.height=t,this._redraw()},_fire:function(e){var t,i;this._markersTree&&(t=(i=e.containerPoint).x,i=i.y,(i=this._markersTree.search({minX:t,minY:i,maxX:t,maxY:i}))&&i.length?(this._map._container.style.cursor="pointer",i=i[0].marker,"click"===e.type&&i.listens("click")&&i.fire("click"),"mousemove"===e.type&&(this._mouseOverMarker&&this._mouseOverMarker!==i&&this._mouseOverMarker.listens("mouseout")&&this._mouseOverMarker.fire("mouseout"),this._mouseOverMarker&&this._mouseOverMarker===i||(this._mouseOverMarker=i).listens("mouseover")&&i.fire("mouseover"))):(this._map._container.style.cursor="","mousemove"===e.type&&this._mouseOverMarker&&(this._mouseOverMarker.listens("mouseout")&&this._mouseOverMarker.fire("mouseout"),delete this._mouseOverMarker)))},_animateZoom:function(e){var t=this._map.getZoomScale(e.zoom),e=this._map._latLngBoundsToNewLayerBounds(this._map.getBounds(),e.zoom,e.center).min;o.DomUtil.setTransform(this._canvas,e,t)}};o.MarkersCanvas=o.Layer.extend(t)}); diff --git a/js/leaflet.canvas-markers.js b/js/leaflet.canvas-markers.js new file mode 100644 index 0000000..ba26b22 --- /dev/null +++ b/js/leaflet.canvas-markers.js @@ -0,0 +1,458 @@ +'use strict'; + +function layerFactory (L) { + + function extend (Parent, props) { + var NewClass = function () { + if (this.init) { this.init.apply(this, arguments); } + }; + var proto = L.Util.create(Parent.prototype); + L.Util.extend(proto, props); + NewClass.prototype = proto; + return NewClass; + } + + var Common = extend(rbush, { + init: function () { + rbush.apply(this, arguments); + this._batch = []; + }, + insert: function (item, batch) { + if (batch) { + this._batch.push(item); + return this; + } + return rbush.prototype.insert.call(this, item); + }, + flush: function () { + this.load(this._batch); + this._batch.length = 0; + return this; + } + }); + + var LatLngsIndex = extend(Common, { + toBBox: function (marker) { + var ll = marker._latlng; + return {minX: ll.lng, minY: ll.lat, maxX: ll.lng, maxY: ll.lat}; + }, + compareMinX: function (a, b) { return a._latlng.lng - b._latlng.lng; }, + compareMinY: function (a, b) { return a._latlng.lat - b._latlng.lat; }, + searchIn: function (bounds) { + return this.search({ + minX: bounds.getWest(), + minY: bounds.getSouth(), + maxX: bounds.getEast(), + maxY: bounds.getNorth() + }); + }, + init: function () { + Common.prototype.init.apply(this, arguments); + this._dirty = 0; + this._total = 0; + }, + // If we are 10% individual inserts\removals, reconstruct lookup for efficiency + cleanup: function () { + if (this._dirty / this._total >= .1) { + var all = this.all(); + this.clear(); + this._dirty = 0; + this.load(all); + } + return this; + }, + insert: function () { + this._dirty++; + this._total++; + return Common.prototype.insert.apply(this, arguments); + }, + remove: function () { + this._total--; + return Common.prototype.remove.apply(this, arguments); + }, + clear: function () { + this._dirty = 0; + this._total = 0; + return rbush.prototype.clear.apply(this); + } + }); + + var PointsIndex = extend(Common, { + toBBox: function (marker) { + var iconSize = marker.options.icon.options.iconSize; + var pos = marker._point; + var adj_x = iconSize[0] / 2; + var adj_y = iconSize[1] / 2; + return { + minX: pos.x - adj_x, + minY: pos.y - adj_y, + maxX: pos.x + adj_x, + maxY: pos.y + adj_y, + }; + }, + compareMinX: function (a, b) { return a._point.x - b._point.x; }, + compareMinY: function (a, b) { return a._point.y - b._point.y; }, + searchBy: function (point) { + return this.search({ + minX: point.x, minY: point.y, maxX: point.x, maxY: point.y + }); + } + }); + + var CanvasIconLayer = L.Layer.extend({ // todo inherit from L.Renderer or L.Canvas + + options: L.Canvas.prototype.options, + + initialize: function (options) { + L.Renderer.prototype.initialize.call(this, options); + // _pointsIdx contains Points of markers currently displaying on map + this._pointsIdx = new PointsIndex(); + // _latlngsIdx contains Lat\Long coordinates of all markers in layer. + this._latlngsIdx = new LatLngsIndex(); + }, + + onAdd: function () { + L.Renderer.prototype.onAdd.call(this); + L.DomUtil.toBack(this._container); + }, + + _initContainer: function () { + L.Canvas.prototype._initContainer.call(this); + this._hideContainer(true); + }, + + onRemove: function () { + L.Renderer.prototype.onRemove.call(this); + }, + + _destroyContainer: function () { + L.Canvas.prototype._destroyContainer.call(this); + this._pointsIdx.clear(); + }, + + getEvents: function () { // todo use L.Renderer.prototype.getEvents + var events = { + viewreset: this._reset, + zoom: this._onZoom, + moveend: this._update, + click: this._onClick, + mousemove: this._onMouseMove, + mouseout: this._handleMouseOut + }; + if (this._zoomAnimated) { + events.zoomanim = this._onAnimZoom; + } + return events; + }, + + _onAnimZoom: function (ev) { + L.Renderer.prototype._onAnimZoom.call(this, ev); + }, + + _onZoom: function () { + L.Renderer.prototype._onZoom.call(this); + }, + + _updateTransform: function (center, zoom) { + L.Renderer.prototype._updateTransform.call(this, center, zoom); + }, + + _updatePaths: L.Util.falseFn, // stub for L.Renderer onAdd/onRemove + + _update: function () { + L.Canvas.prototype._update.call(this); + this._draw(); + }, + + _reset: function () { + this._update(); + this._updateTransform(this._center, this._zoom); + }, + + _redraw: function () { + L.Canvas.prototype._redraw.call(this); + }, + + _clear: function () { + L.Canvas.prototype._clear.call(this); + }, + + _draw: function () { + var bounds = this._redrawBounds; + if (bounds) { + var size = bounds.getSize(); + this._ctx.beginPath(); + this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y); + this._ctx.clip(); + } + this._drawing = true; + this._latlngsIdx.cleanup(); + var mapBounds = this._map.getBounds().pad(this.options.padding); + + // Only re-draw what we are showing on the map. + var isEmpty = true; + this._latlngsIdx.searchIn(mapBounds).forEach(function (marker) { + // Readjust Point Map + if (!marker._map) { marker._map = this._map; } // todo ??implement proper handling in (on)add*/remove* + this._drawMarker(marker); + this._pointsIdx.insert(marker,true); + isEmpty = false; + }, this); + this._drawing = false; + // Clear rBush & Bulk Load for performance + this._pointsIdx.clear().flush(); + this._hideContainer(isEmpty); + }, + + _drawMarker: function (marker) { + marker._point = this._map.latLngToLayerPoint(marker.getLatLng()); + this._imageLookup = this._imageLookup || {}; + + var iconUrl = marker.options.icon.options.iconUrl; + var queued = this._imageLookup[iconUrl]; + if (!marker.canvas_img) { + if (queued) { + marker.canvas_img = queued.img; + if (queued.loaded) { + this._drawImage(marker); + } else { + queued.queue.push(marker); + } + } else { + var img = new Image(); + img.src = iconUrl; + marker.canvas_img = img; + queued = { + loaded: false, + img: img, + queue: [marker] + }; + this._imageLookup[iconUrl] = queued; + img.onload = function () { + queued.loaded = true; + queued.queue.forEach(function (_marker) { + if (this.hasLayer(_marker)) { + this._drawImage(_marker); + } + }, this); + }.bind(this); + } + } else if (queued.loaded) { // image may be not loaded / bad url + this._drawImage(marker); + } + }, + + _drawImage: function (marker) { + var options = marker.options.icon.options; + var pos = marker._point.subtract(options.iconAnchor); + this._ctx.drawImage( + marker.canvas_img, + pos.x, + pos.y, + options.iconSize[0], + options.iconSize[1] + ); + }, + + _onClick: function (e) { + var point = e.layerPoint || this._map.mouseEventToLayerPoint(e), clickedLayer; + + var layer_intersect = this._pointsIdx && this._pointsIdx.searchBy(point); + if (layer_intersect) { + layer_intersect.forEach(function (layer) { + if (layer.options.interactive && !this._map._draggableMoved(layer)) { + clickedLayer = layer; + } + }, this); + } + if (clickedLayer) { + L.DomEvent.fakeStop(e); + this._fireEvent([clickedLayer], e); + } + }, + + _onMouseMove: function (e) { + if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; } + + var point = e.layerPoint || this._map.mouseEventToLayerPoint(e); + this._handleMouseHover(e, point); + }, + + _handleMouseHover: function (e, point) { + if (this._mouseHoverThrottled) { + return; + } + var candidateHoveredLayer; + var layer_intersect = this._pointsIdx && this._pointsIdx.searchBy(point); + if (layer_intersect) { + layer_intersect.forEach(function (layer) { + if (layer.options.interactive) { + candidateHoveredLayer = layer; + } + }, this); + } + + if (candidateHoveredLayer !== this._hoveredLayer) { + this._handleMouseOut(e); + + if (candidateHoveredLayer) { + L.DomUtil.addClass(this._container, 'leaflet-interactive'); // change cursor + this._fireEvent([candidateHoveredLayer], e, 'mouseover'); + this._hoveredLayer = candidateHoveredLayer; + } + } + + if (this._hoveredLayer) { + this._fireEvent([this._hoveredLayer], e); + } + + this._mouseHoverThrottled = true; + setTimeout(L.bind(function () { + this._mouseHoverThrottled = false; + }, this), 32); + }, + + _handleMouseOut: function (e) { + L.Canvas.prototype._handleMouseOut.call(this,e); + }, + + _fireEvent: function (layers, e, type) { + if (e.layerPoint) { + layers[0].fire(type || e.type, e, true); + return; + } + L.Canvas.prototype._fireEvent.call(this, layers, e, type); + }, + + _addMarker: function (marker, latlng, isDisplaying, batch) { + if (!(marker instanceof L.Marker)) { + throw new Error("Layer isn't a marker"); + } + marker._map = this._map; // Needed for pop-up & tooltip to work + L.Util.stamp(marker); + marker.addEventParent(this); + + if (isDisplaying) { + this._drawMarker(marker); + this._pointsIdx.insert(marker, batch); + this._hideContainer(false); + } + this._latlngsIdx.insert(marker, batch); + }, + + // Adds single layer at a time. Less efficient for rBush + addMarker: function (marker, groupID) { + groupID = groupID ? groupID.toString() : '0'; + this._groupIDs = this._groupIDs || {}; + + var latlng = marker.getLatLng(); + var isDisplaying = this._map && this._map.getBounds().pad(this.options.padding).contains(latlng); + this._addMarker(marker, latlng, isDisplaying); + this._groupIDs[groupID] = (this._groupIDs[groupID] || 0) + 1; + marker._canvasGroupID = groupID; + return this; + }, + + addLayer: function (layer, groupID) { + return this.addMarker(layer,groupID); + }, + + // Multiple layers at a time for rBush performance + addMarkers: function (markers, groupID) { + groupID = groupID ? groupID.toString() : '0'; + this._groupIDs = this._groupIDs || {}; + this._groupIDs[groupID] = this._groupIDs[groupID] || 0; + + var mapBounds = this._map && this._map.getBounds().pad(this.options.padding); + markers.forEach(function (marker) { + var latlng = marker.getLatLng(); + var isDisplaying = mapBounds && mapBounds.contains(latlng); + this._addMarker(marker, latlng, isDisplaying, true); + this._groupIDs[groupID]++; + marker._canvasGroupID = groupID; + }, this); + this._pointsIdx.flush(); + this._latlngsIdx.flush(); + return this; + }, + + addLayers: function (layers, groupID) { + return this.addMarkers(layers,groupID); + }, + + removeGroups: function (groupIDs) { + groupIDs.forEach(function (groupID) { + this._removeGroup(groupID); + }, this); + this._redraw(); + return this; + }, + + removeGroup: function (groupID) { + this._removeGroup(groupID); + this._redraw(); + return this; + }, + + _removeGroup: function (groupID) { + groupID = groupID.toString(); + if (!this._groupIDs[groupID]) { return; } + delete this._groupIDs[groupID]; + this._latlngsIdx.all().filter(function (marker) { + return marker._canvasGroupID === groupID; + }).forEach(function (el) { + this.removeMarker(el, false, true); + }, this); + }, + + removeMarker: function (marker, redraw, hasLayer) { + if (!hasLayer && !this.hasLayer(marker)) { return; } + this._latlngsIdx.remove(marker); + + if (redraw && this._map && + this._map.getBounds().pad(this.options.padding).contains(marker.getLatLng())) { + this._redraw(); + } + marker.removeEventParent(this); + return this; + }, + + removeLayer: function (layer) { + return this.removeMarker(layer, true); + }, + /* + removeLayers: function (layers) { + layers.forEach(function (el) { + this.removeMarker(el, false); + }, this); + this._redraw(); + return this; + }, + */ + clearLayers: function () { + this._latlngsIdx.clear(); + this._pointsIdx.clear(); + this._clear(); + return this; + }, + + hasLayer: function (layer) { + // return this._latlngsIdx.all().indexOf(layer) !== -1; + return layer._eventParents[L.Util.stamp(this)]; // !! to cut corners + }, + + _hideContainer: function (hide) { + if (this._isEmpty === hide) { return; } + this._isEmpty = hide; + this._container.style.display = hide ? 'none' : 'initial'; + } + }); + + L.canvasIconLayer = function (options) { + return new CanvasIconLayer(options); + }; + + return CanvasIconLayer; +} + +module.exports = layerFactory; diff --git a/js/rbush.js b/js/rbush.js index 4b40722..bf604e5 100644 --- a/js/rbush.js +++ b/js/rbush.js @@ -1,475 +1,467 @@ -(function (global, factory) { -typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : -typeof define === 'function' && define.amd ? define(factory) : -(global = global || self, global.RBush = factory()); -}(this, function () { 'use strict'; +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.rbush = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o left) { - if (right - left > 600) { - var n = right - left + 1; - var m = k - left + 1; - var z = Math.log(n); - var s = 0.5 * Math.exp(2 * z / 3); - var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); - var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); - var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); - quickselectStep(arr, k, newLeft, newRight, compare); - } - - var t = arr[k]; - var i = left; - var j = right; +module.exports = rbush; +module.exports.default = rbush; - swap(arr, left, k); - if (compare(arr[right], t) > 0) { swap(arr, left, right); } +var quickselect = require('quickselect'); - while (i < j) { - swap(arr, i, j); - i++; - j--; - while (compare(arr[i], t) < 0) { i++; } - while (compare(arr[j], t) > 0) { j--; } - } +function rbush(maxEntries, format) { + if (!(this instanceof rbush)) return new rbush(maxEntries, format); - if (compare(arr[left], t) === 0) { swap(arr, left, j); } - else { - j++; - swap(arr, j, right); - } + // max entries in a node is 9 by default; min node fill is 40% for best performance + this._maxEntries = Math.max(4, maxEntries || 9); + this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4)); - if (j <= k) { left = j + 1; } - if (k <= j) { right = j - 1; } + if (format) { + this._initFormat(format); } -} -function swap(arr, i, j) { - var tmp = arr[i]; - arr[i] = arr[j]; - arr[j] = tmp; + this.clear(); } -function defaultCompare(a, b) { - return a < b ? -1 : a > b ? 1 : 0; -} +rbush.prototype = { -var RBush = function RBush(maxEntries) { - if ( maxEntries === void 0 ) maxEntries = 9; + all: function () { + return this._all(this.data, []); + }, - // max entries in a node is 9 by default; min node fill is 40% for best performance - this._maxEntries = Math.max(4, maxEntries); - this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4)); - this.clear(); -}; + search: function (bbox) { -RBush.prototype.all = function all () { - return this._all(this.data, []); -}; + var node = this.data, + result = [], + toBBox = this.toBBox; -RBush.prototype.search = function search (bbox) { - var node = this.data; - var result = []; + if (!intersects(bbox, node)) return result; - if (!intersects(bbox, node)) { return result; } + var nodesToSearch = [], + i, len, child, childBBox; - var toBBox = this.toBBox; - var nodesToSearch = []; + while (node) { + for (i = 0, len = node.children.length; i < len; i++) { - while (node) { - for (var i = 0; i < node.children.length; i++) { - var child = node.children[i]; - var childBBox = node.leaf ? toBBox(child) : child; + child = node.children[i]; + childBBox = node.leaf ? toBBox(child) : child; - if (intersects(bbox, childBBox)) { - if (node.leaf) { result.push(child); } - else if (contains(bbox, childBBox)) { this._all(child, result); } - else { nodesToSearch.push(child); } + if (intersects(bbox, childBBox)) { + if (node.leaf) result.push(child); + else if (contains(bbox, childBBox)) this._all(child, result); + else nodesToSearch.push(child); + } } + node = nodesToSearch.pop(); } - node = nodesToSearch.pop(); - } - return result; -}; + return result; + }, + + collides: function (bbox) { -RBush.prototype.collides = function collides (bbox) { - var node = this.data; + var node = this.data, + toBBox = this.toBBox; - if (!intersects(bbox, node)) { return false; } + if (!intersects(bbox, node)) return false; - var nodesToSearch = []; - while (node) { - for (var i = 0; i < node.children.length; i++) { - var child = node.children[i]; - var childBBox = node.leaf ? this.toBBox(child) : child; + var nodesToSearch = [], + i, len, child, childBBox; - if (intersects(bbox, childBBox)) { - if (node.leaf || contains(bbox, childBBox)) { return true; } - nodesToSearch.push(child); + while (node) { + for (i = 0, len = node.children.length; i < len; i++) { + + child = node.children[i]; + childBBox = node.leaf ? toBBox(child) : child; + + if (intersects(bbox, childBBox)) { + if (node.leaf || contains(bbox, childBBox)) return true; + nodesToSearch.push(child); + } } + node = nodesToSearch.pop(); } - node = nodesToSearch.pop(); - } - return false; -}; + return false; + }, -RBush.prototype.load = function load (data) { - if (!(data && data.length)) { return this; } + load: function (data) { + if (!(data && data.length)) return this; - if (data.length < this._minEntries) { - for (var i = 0; i < data.length; i++) { - this.insert(data[i]); + if (data.length < this._minEntries) { + for (var i = 0, len = data.length; i < len; i++) { + this.insert(data[i]); + } + return this; } - return this; - } - // recursively build the tree with the given data from scratch using OMT algorithm - var node = this._build(data.slice(), 0, data.length - 1, 0); + // recursively build the tree with the given data from scratch using OMT algorithm + var node = this._build(data.slice(), 0, data.length - 1, 0); + + if (!this.data.children.length) { + // save as is if tree is empty + this.data = node; - if (!this.data.children.length) { - // save as is if tree is empty - this.data = node; + } else if (this.data.height === node.height) { + // split root if trees have the same height + this._splitRoot(this.data, node); - } else if (this.data.height === node.height) { - // split root if trees have the same height - this._splitRoot(this.data, node); + } else { + if (this.data.height < node.height) { + // swap trees if inserted one is bigger + var tmpNode = this.data; + this.data = node; + node = tmpNode; + } - } else { - if (this.data.height < node.height) { - // swap trees if inserted one is bigger - var tmpNode = this.data; - this.data = node; - node = tmpNode; + // insert the small tree into the large tree at appropriate level + this._insert(node, this.data.height - node.height - 1, true); } - // insert the small tree into the large tree at appropriate level - this._insert(node, this.data.height - node.height - 1, true); - } - - return this; -}; + return this; + }, -RBush.prototype.insert = function insert (item) { - if (item) { this._insert(item, this.data.height - 1); } - return this; -}; + insert: function (item) { + if (item) this._insert(item, this.data.height - 1); + return this; + }, -RBush.prototype.clear = function clear () { - this.data = createNode([]); - return this; -}; + clear: function () { + this.data = createNode([]); + return this; + }, -RBush.prototype.remove = function remove (item, equalsFn) { - if (!item) { return this; } + remove: function (item, equalsFn) { + if (!item) return this; - var node = this.data; - var bbox = this.toBBox(item); - var path = []; - var indexes = []; - var i, parent, goingUp; + var node = this.data, + bbox = this.toBBox(item), + path = [], + indexes = [], + i, parent, index, goingUp; - // depth-first iterative tree traversal - while (node || path.length) { + // depth-first iterative tree traversal + while (node || path.length) { - if (!node) { // go up - node = path.pop(); - parent = path[path.length - 1]; - i = indexes.pop(); - goingUp = true; - } + if (!node) { // go up + node = path.pop(); + parent = path[path.length - 1]; + i = indexes.pop(); + goingUp = true; + } - if (node.leaf) { // check current node - var index = findItem(item, node.children, equalsFn); + if (node.leaf) { // check current node + index = findItem(item, node.children, equalsFn); - if (index !== -1) { - // item found, remove the item and condense tree upwards - node.children.splice(index, 1); - path.push(node); - this._condense(path); - return this; + if (index !== -1) { + // item found, remove the item and condense tree upwards + node.children.splice(index, 1); + path.push(node); + this._condense(path); + return this; + } } - } - if (!goingUp && !node.leaf && contains(node, bbox)) { // go down - path.push(node); - indexes.push(i); - i = 0; - parent = node; - node = node.children[0]; + if (!goingUp && !node.leaf && contains(node, bbox)) { // go down + path.push(node); + indexes.push(i); + i = 0; + parent = node; + node = node.children[0]; - } else if (parent) { // go right - i++; - node = parent.children[i]; - goingUp = false; + } else if (parent) { // go right + i++; + node = parent.children[i]; + goingUp = false; - } else { node = null; } // nothing found - } + } else node = null; // nothing found + } - return this; -}; + return this; + }, -RBush.prototype.toBBox = function toBBox (item) { return item; }; + toBBox: function (item) { return item; }, -RBush.prototype.compareMinX = function compareMinX (a, b) { return a.minX - b.minX; }; -RBush.prototype.compareMinY = function compareMinY (a, b) { return a.minY - b.minY; }; + compareMinX: compareNodeMinX, + compareMinY: compareNodeMinY, -RBush.prototype.toJSON = function toJSON () { return this.data; }; + toJSON: function () { return this.data; }, -RBush.prototype.fromJSON = function fromJSON (data) { - this.data = data; - return this; -}; + fromJSON: function (data) { + this.data = data; + return this; + }, -RBush.prototype._all = function _all (node, result) { - var nodesToSearch = []; - while (node) { - if (node.leaf) { result.push.apply(result, node.children); } - else { nodesToSearch.push.apply(nodesToSearch, node.children); } + _all: function (node, result) { + var nodesToSearch = []; + while (node) { + if (node.leaf) result.push.apply(result, node.children); + else nodesToSearch.push.apply(nodesToSearch, node.children); - node = nodesToSearch.pop(); - } - return result; -}; + node = nodesToSearch.pop(); + } + return result; + }, -RBush.prototype._build = function _build (items, left, right, height) { + _build: function (items, left, right, height) { - var N = right - left + 1; - var M = this._maxEntries; - var node; + var N = right - left + 1, + M = this._maxEntries, + node; - if (N <= M) { - // reached leaf level; return leaf - node = createNode(items.slice(left, right + 1)); - calcBBox(node, this.toBBox); - return node; - } + if (N <= M) { + // reached leaf level; return leaf + node = createNode(items.slice(left, right + 1)); + calcBBox(node, this.toBBox); + return node; + } - if (!height) { - // target height of the bulk-loaded tree - height = Math.ceil(Math.log(N) / Math.log(M)); + if (!height) { + // target height of the bulk-loaded tree + height = Math.ceil(Math.log(N) / Math.log(M)); - // target number of root entries to maximize storage utilization - M = Math.ceil(N / Math.pow(M, height - 1)); - } + // target number of root entries to maximize storage utilization + M = Math.ceil(N / Math.pow(M, height - 1)); + } - node = createNode([]); - node.leaf = false; - node.height = height; + node = createNode([]); + node.leaf = false; + node.height = height; - // split the items into M mostly square tiles + // split the items into M mostly square tiles - var N2 = Math.ceil(N / M); - var N1 = N2 * Math.ceil(Math.sqrt(M)); + var N2 = Math.ceil(N / M), + N1 = N2 * Math.ceil(Math.sqrt(M)), + i, j, right2, right3; - multiSelect(items, left, right, N1, this.compareMinX); + multiSelect(items, left, right, N1, this.compareMinX); - for (var i = left; i <= right; i += N1) { + for (i = left; i <= right; i += N1) { - var right2 = Math.min(i + N1 - 1, right); + right2 = Math.min(i + N1 - 1, right); - multiSelect(items, i, right2, N2, this.compareMinY); + multiSelect(items, i, right2, N2, this.compareMinY); - for (var j = i; j <= right2; j += N2) { + for (j = i; j <= right2; j += N2) { - var right3 = Math.min(j + N2 - 1, right2); + right3 = Math.min(j + N2 - 1, right2); - // pack each entry recursively - node.children.push(this._build(items, j, right3, height - 1)); + // pack each entry recursively + node.children.push(this._build(items, j, right3, height - 1)); + } } - } - calcBBox(node, this.toBBox); + calcBBox(node, this.toBBox); - return node; -}; + return node; + }, -RBush.prototype._chooseSubtree = function _chooseSubtree (bbox, node, level, path) { - while (true) { - path.push(node); + _chooseSubtree: function (bbox, node, level, path) { - if (node.leaf || path.length - 1 === level) { break; } + var i, len, child, targetNode, area, enlargement, minArea, minEnlargement; - var minArea = Infinity; - var minEnlargement = Infinity; - var targetNode = (void 0); + while (true) { + path.push(node); - for (var i = 0; i < node.children.length; i++) { - var child = node.children[i]; - var area = bboxArea(child); - var enlargement = enlargedArea(bbox, child) - area; + if (node.leaf || path.length - 1 === level) break; - // choose entry with the least area enlargement - if (enlargement < minEnlargement) { - minEnlargement = enlargement; - minArea = area < minArea ? area : minArea; - targetNode = child; + minArea = minEnlargement = Infinity; - } else if (enlargement === minEnlargement) { - // otherwise choose one with the smallest area - if (area < minArea) { - minArea = area; + for (i = 0, len = node.children.length; i < len; i++) { + child = node.children[i]; + area = bboxArea(child); + enlargement = enlargedArea(bbox, child) - area; + + // choose entry with the least area enlargement + if (enlargement < minEnlargement) { + minEnlargement = enlargement; + minArea = area < minArea ? area : minArea; targetNode = child; + + } else if (enlargement === minEnlargement) { + // otherwise choose one with the smallest area + if (area < minArea) { + minArea = area; + targetNode = child; + } } } + + node = targetNode || node.children[0]; } - node = targetNode || node.children[0]; - } + return node; + }, - return node; -}; + _insert: function (item, level, isNode) { -RBush.prototype._insert = function _insert (item, level, isNode) { - var bbox = isNode ? item : this.toBBox(item); - var insertPath = []; + var toBBox = this.toBBox, + bbox = isNode ? item : toBBox(item), + insertPath = []; - // find the best node for accommodating the item, saving all nodes along the path too - var node = this._chooseSubtree(bbox, this.data, level, insertPath); + // find the best node for accommodating the item, saving all nodes along the path too + var node = this._chooseSubtree(bbox, this.data, level, insertPath); - // put the item into the node - node.children.push(item); - extend(node, bbox); + // put the item into the node + node.children.push(item); + extend(node, bbox); - // split on node overflow; propagate upwards if necessary - while (level >= 0) { - if (insertPath[level].children.length > this._maxEntries) { - this._split(insertPath, level); - level--; - } else { break; } - } + // split on node overflow; propagate upwards if necessary + while (level >= 0) { + if (insertPath[level].children.length > this._maxEntries) { + this._split(insertPath, level); + level--; + } else break; + } - // adjust bboxes along the insertion path - this._adjustParentBBoxes(bbox, insertPath, level); -}; + // adjust bboxes along the insertion path + this._adjustParentBBoxes(bbox, insertPath, level); + }, -// split overflowed node into two -RBush.prototype._split = function _split (insertPath, level) { - var node = insertPath[level]; - var M = node.children.length; - var m = this._minEntries; + // split overflowed node into two + _split: function (insertPath, level) { - this._chooseSplitAxis(node, m, M); + var node = insertPath[level], + M = node.children.length, + m = this._minEntries; - var splitIndex = this._chooseSplitIndex(node, m, M); + this._chooseSplitAxis(node, m, M); - var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex)); - newNode.height = node.height; - newNode.leaf = node.leaf; + var splitIndex = this._chooseSplitIndex(node, m, M); - calcBBox(node, this.toBBox); - calcBBox(newNode, this.toBBox); + var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex)); + newNode.height = node.height; + newNode.leaf = node.leaf; - if (level) { insertPath[level - 1].children.push(newNode); } - else { this._splitRoot(node, newNode); } -}; + calcBBox(node, this.toBBox); + calcBBox(newNode, this.toBBox); -RBush.prototype._splitRoot = function _splitRoot (node, newNode) { - // split root node - this.data = createNode([node, newNode]); - this.data.height = node.height + 1; - this.data.leaf = false; - calcBBox(this.data, this.toBBox); -}; + if (level) insertPath[level - 1].children.push(newNode); + else this._splitRoot(node, newNode); + }, + + _splitRoot: function (node, newNode) { + // split root node + this.data = createNode([node, newNode]); + this.data.height = node.height + 1; + this.data.leaf = false; + calcBBox(this.data, this.toBBox); + }, -RBush.prototype._chooseSplitIndex = function _chooseSplitIndex (node, m, M) { - var index; - var minOverlap = Infinity; - var minArea = Infinity; + _chooseSplitIndex: function (node, m, M) { - for (var i = m; i <= M - m; i++) { - var bbox1 = distBBox(node, 0, i, this.toBBox); - var bbox2 = distBBox(node, i, M, this.toBBox); + var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index; - var overlap = intersectionArea(bbox1, bbox2); - var area = bboxArea(bbox1) + bboxArea(bbox2); + minOverlap = minArea = Infinity; - // choose distribution with minimum overlap - if (overlap < minOverlap) { - minOverlap = overlap; - index = i; + for (i = m; i <= M - m; i++) { + bbox1 = distBBox(node, 0, i, this.toBBox); + bbox2 = distBBox(node, i, M, this.toBBox); - minArea = area < minArea ? area : minArea; + overlap = intersectionArea(bbox1, bbox2); + area = bboxArea(bbox1) + bboxArea(bbox2); - } else if (overlap === minOverlap) { - // otherwise choose distribution with minimum area - if (area < minArea) { - minArea = area; + // choose distribution with minimum overlap + if (overlap < minOverlap) { + minOverlap = overlap; index = i; + + minArea = area < minArea ? area : minArea; + + } else if (overlap === minOverlap) { + // otherwise choose distribution with minimum area + if (area < minArea) { + minArea = area; + index = i; + } } } - } - return index || M - m; -}; + return index; + }, -// sorts node children by the best axis for split -RBush.prototype._chooseSplitAxis = function _chooseSplitAxis (node, m, M) { - var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX; - var compareMinY = node.leaf ? this.compareMinY : compareNodeMinY; - var xMargin = this._allDistMargin(node, m, M, compareMinX); - var yMargin = this._allDistMargin(node, m, M, compareMinY); + // sorts node children by the best axis for split + _chooseSplitAxis: function (node, m, M) { - // if total distributions margin value is minimal for x, sort by minX, - // otherwise it's already sorted by minY - if (xMargin < yMargin) { node.children.sort(compareMinX); } -}; + var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX, + compareMinY = node.leaf ? this.compareMinY : compareNodeMinY, + xMargin = this._allDistMargin(node, m, M, compareMinX), + yMargin = this._allDistMargin(node, m, M, compareMinY); -// total margin of all possible split distributions where each node is at least m full -RBush.prototype._allDistMargin = function _allDistMargin (node, m, M, compare) { - node.children.sort(compare); + // if total distributions margin value is minimal for x, sort by minX, + // otherwise it's already sorted by minY + if (xMargin < yMargin) node.children.sort(compareMinX); + }, - var toBBox = this.toBBox; - var leftBBox = distBBox(node, 0, m, toBBox); - var rightBBox = distBBox(node, M - m, M, toBBox); - var margin = bboxMargin(leftBBox) + bboxMargin(rightBBox); + // total margin of all possible split distributions where each node is at least m full + _allDistMargin: function (node, m, M, compare) { - for (var i = m; i < M - m; i++) { - var child = node.children[i]; - extend(leftBBox, node.leaf ? toBBox(child) : child); - margin += bboxMargin(leftBBox); - } + node.children.sort(compare); - for (var i$1 = M - m - 1; i$1 >= m; i$1--) { - var child$1 = node.children[i$1]; - extend(rightBBox, node.leaf ? toBBox(child$1) : child$1); - margin += bboxMargin(rightBBox); - } + var toBBox = this.toBBox, + leftBBox = distBBox(node, 0, m, toBBox), + rightBBox = distBBox(node, M - m, M, toBBox), + margin = bboxMargin(leftBBox) + bboxMargin(rightBBox), + i, child; - return margin; -}; + for (i = m; i < M - m; i++) { + child = node.children[i]; + extend(leftBBox, node.leaf ? toBBox(child) : child); + margin += bboxMargin(leftBBox); + } -RBush.prototype._adjustParentBBoxes = function _adjustParentBBoxes (bbox, path, level) { - // adjust bboxes along the given tree path - for (var i = level; i >= 0; i--) { - extend(path[i], bbox); - } -}; + for (i = M - m - 1; i >= m; i--) { + child = node.children[i]; + extend(rightBBox, node.leaf ? toBBox(child) : child); + margin += bboxMargin(rightBBox); + } + + return margin; + }, + + _adjustParentBBoxes: function (bbox, path, level) { + // adjust bboxes along the given tree path + for (var i = level; i >= 0; i--) { + extend(path[i], bbox); + } + }, + + _condense: function (path) { + // go through the path, removing empty nodes and updating bboxes + for (var i = path.length - 1, siblings; i >= 0; i--) { + if (path[i].children.length === 0) { + if (i > 0) { + siblings = path[i - 1].children; + siblings.splice(siblings.indexOf(path[i]), 1); + + } else this.clear(); -RBush.prototype._condense = function _condense (path) { - // go through the path, removing empty nodes and updating bboxes - for (var i = path.length - 1, siblings = (void 0); i >= 0; i--) { - if (path[i].children.length === 0) { - if (i > 0) { - siblings = path[i - 1].children; - siblings.splice(siblings.indexOf(path[i]), 1); + } else calcBBox(path[i], this.toBBox); + } + }, + + _initFormat: function (format) { + // data format (minX, minY, maxX, maxY accessors) + + // uses eval-type function compilation instead of just accepting a toBBox function + // because the algorithms are very sensitive to sorting functions performance, + // so they should be dead simple and without inner calls - } else { this.clear(); } + var compareArr = ['return a', ' - b', ';']; - } else { calcBBox(path[i], this.toBBox); } + this.compareMinX = new Function('a', 'b', compareArr.join(format[0])); + this.compareMinY = new Function('a', 'b', compareArr.join(format[1])); + + this.toBBox = new Function('a', + 'return {minX: a' + format[0] + + ', minY: a' + format[1] + + ', maxX: a' + format[2] + + ', maxY: a' + format[3] + '};'); } }; function findItem(item, items, equalsFn) { - if (!equalsFn) { return items.indexOf(item); } + if (!equalsFn) return items.indexOf(item); for (var i = 0; i < items.length; i++) { - if (equalsFn(item, items[i])) { return i; } + if (equalsFn(item, items[i])) return i; } return -1; } @@ -481,14 +473,14 @@ function calcBBox(node, toBBox) { // min bounding rectangle of node children from k to p-1 function distBBox(node, k, p, toBBox, destNode) { - if (!destNode) { destNode = createNode(null); } + if (!destNode) destNode = createNode(null); destNode.minX = Infinity; destNode.minY = Infinity; destNode.maxX = -Infinity; destNode.maxY = -Infinity; - for (var i = k; i < p; i++) { - var child = node.children[i]; + for (var i = k, child; i < p; i++) { + child = node.children[i]; extend(destNode, node.leaf ? toBBox(child) : child); } @@ -515,10 +507,10 @@ function enlargedArea(a, b) { } function intersectionArea(a, b) { - var minX = Math.max(a.minX, b.minX); - var minY = Math.max(a.minY, b.minY); - var maxX = Math.min(a.maxX, b.maxX); - var maxY = Math.min(a.maxY, b.maxY); + var minX = Math.max(a.minX, b.minX), + minY = Math.max(a.minY, b.minY), + maxX = Math.min(a.maxX, b.maxX), + maxY = Math.min(a.maxY, b.maxY); return Math.max(0, maxX - minX) * Math.max(0, maxY - minY); @@ -554,21 +546,81 @@ function createNode(children) { // combines selection algorithm with binary divide & conquer approach function multiSelect(arr, left, right, n, compare) { - var stack = [left, right]; + var stack = [left, right], + mid; while (stack.length) { right = stack.pop(); left = stack.pop(); - if (right - left <= n) { continue; } + if (right - left <= n) continue; - var mid = left + Math.ceil((right - left) / n / 2) * n; + mid = left + Math.ceil((right - left) / n / 2) * n; quickselect(arr, mid, left, right, compare); stack.push(left, mid, mid, right); } } -return RBush; +},{"quickselect":2}],2:[function(require,module,exports){ +'use strict'; + +module.exports = quickselect; +module.exports.default = quickselect; + +function quickselect(arr, k, left, right, compare) { + quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare); +}; + +function quickselectStep(arr, k, left, right, compare) { + + while (right > left) { + if (right - left > 600) { + var n = right - left + 1; + var m = k - left + 1; + var z = Math.log(n); + var s = 0.5 * Math.exp(2 * z / 3); + var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); + var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); + var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); + quickselectStep(arr, k, newLeft, newRight, compare); + } + + var t = arr[k]; + var i = left; + var j = right; + + swap(arr, left, k); + if (compare(arr[right], t) > 0) swap(arr, left, right); + + while (i < j) { + swap(arr, i, j); + i++; + j--; + while (compare(arr[i], t) < 0) i++; + while (compare(arr[j], t) > 0) j--; + } + + if (compare(arr[left], t) === 0) swap(arr, left, j); + else { + j++; + swap(arr, j, right); + } + + if (j <= k) left = j + 1; + if (k <= j) right = j - 1; + } +} + +function swap(arr, i, j) { + var tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; +} + +function defaultCompare(a, b) { + return a < b ? -1 : a > b ? 1 : 0; +} -})); +},{}]},{},[1])(1) +}); \ No newline at end of file diff --git a/js/tracker.js b/js/tracker.js index 164c7f4..9eaa382 100644 --- a/js/tracker.js +++ b/js/tracker.js @@ -415,7 +415,7 @@ function load() { if(currentPosition) updateCurrentPosition(currentPosition.lat, currentPosition.lon); //Receiver canvas - receiverCanvas = new L.MarkersCanvas(); + receiverCanvas = new L.canvasIconLayer(); receiverCanvas.addTo(map); // initalize nite overlay @@ -442,19 +442,20 @@ function load() { if (vehicles[key]["horizon_circle"]["_map"]) { try { - var horizonwidth = vehicles[key]["horizon_circle"].getElement().getBoundingClientRect()["width"]; - var subhorizonwidth = vehicles[key]["subhorizon_circle"].getElement().getBoundingClientRect()["width"]; - if (horizonwidth != 0 && horizonwidth < 28) { + var zoom = map.getZoom(); + var horizonzoom = (Math.abs(Math.log(vehicles[key]["horizon_circle"].getRadius()/2000000)/0.75)); + var subhorizonzoom = (Math.abs(Math.log(vehicles[key]["subhorizon_circle"].getRadius()/2000000)/0.75)); + if (horizonzoom > zoom) { map.removeLayer(vehicles[key]["horizon_circle_title"]); } else { map.addLayer(vehicles[key]["horizon_circle_title"]); } - if (subhorizonwidth != 0 && subhorizonwidth < 28) { + if (subhorizonzoom > zoom) { map.removeLayer(vehicles[key]["subhorizon_circle_title"]); } else { map.addLayer(vehicles[key]["subhorizon_circle_title"]); } - } catch(e){}; + } catch(e){console.log(e);}; } } } @@ -1935,6 +1936,7 @@ function addPosition(position) { fillColor: '#00F', fillOpacity: 0, opacity: 0.6, + interactive: false, }); horizon_circle_title_icon = new L.DivIcon({ @@ -1943,7 +1945,8 @@ function addPosition(position) { }); horizon_circle_title = new L.Marker(point, { - icon: horizon_circle_title_icon + icon: horizon_circle_title_icon, + interactive: false, }); if (!offline.get("opt_hide_horizon")) { @@ -1965,15 +1968,17 @@ function addPosition(position) { fillColor: '#0F0', fillOpacity: 0, opacity: 0.8, + interactive: false, }); subhorizon_circle_title_icon = new L.DivIcon({ className: "subhorizon_circle_title", - html: 'km' + html: 'km', }); subhorizon_circle_title = new L.Marker(point, { - icon: subhorizon_circle_title_icon + icon: subhorizon_circle_title_icon, + interactive: false, }); if (!offline.get("opt_hide_horizon")) {