Some checks are pending
Render and Publish / build-deploy (push) Waiting to run
2785 lines
93 KiB
JavaScript
2785 lines
93 KiB
JavaScript
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports["default"] = undefined;
|
|
|
|
var _util = require("./util");
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
|
|
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
|
|
|
var ClusterLayerStore = /*#__PURE__*/function () {
|
|
function ClusterLayerStore(group) {
|
|
_classCallCheck(this, ClusterLayerStore);
|
|
|
|
this._layers = {};
|
|
this._group = group;
|
|
}
|
|
|
|
_createClass(ClusterLayerStore, [{
|
|
key: "add",
|
|
value: function add(layer, id) {
|
|
if (typeof id !== "undefined" && id !== null) {
|
|
if (this._layers[id]) {
|
|
this._group.removeLayer(this._layers[id]);
|
|
}
|
|
|
|
this._layers[id] = layer;
|
|
}
|
|
|
|
this._group.addLayer(layer);
|
|
}
|
|
}, {
|
|
key: "remove",
|
|
value: function remove(id) {
|
|
if (typeof id === "undefined" || id === null) {
|
|
return;
|
|
}
|
|
|
|
id = (0, _util.asArray)(id);
|
|
|
|
for (var i = 0; i < id.length; i++) {
|
|
if (this._layers[id[i]]) {
|
|
this._group.removeLayer(this._layers[id[i]]);
|
|
|
|
delete this._layers[id[i]];
|
|
}
|
|
}
|
|
}
|
|
}, {
|
|
key: "clear",
|
|
value: function clear() {
|
|
this._layers = {};
|
|
|
|
this._group.clearLayers();
|
|
}
|
|
}]);
|
|
|
|
return ClusterLayerStore;
|
|
}();
|
|
|
|
exports["default"] = ClusterLayerStore;
|
|
|
|
|
|
},{"./util":17}],2:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
|
|
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
|
|
|
var ControlStore = /*#__PURE__*/function () {
|
|
function ControlStore(map) {
|
|
_classCallCheck(this, ControlStore);
|
|
|
|
this._controlsNoId = [];
|
|
this._controlsById = {};
|
|
this._map = map;
|
|
}
|
|
|
|
_createClass(ControlStore, [{
|
|
key: "add",
|
|
value: function add(control, id, html) {
|
|
if (typeof id !== "undefined" && id !== null) {
|
|
if (this._controlsById[id]) {
|
|
this._map.removeControl(this._controlsById[id]);
|
|
}
|
|
|
|
this._controlsById[id] = control;
|
|
} else {
|
|
this._controlsNoId.push(control);
|
|
}
|
|
|
|
this._map.addControl(control);
|
|
}
|
|
}, {
|
|
key: "get",
|
|
value: function get(id) {
|
|
var control = null;
|
|
|
|
if (this._controlsById[id]) {
|
|
control = this._controlsById[id];
|
|
}
|
|
|
|
return control;
|
|
}
|
|
}, {
|
|
key: "remove",
|
|
value: function remove(id) {
|
|
if (this._controlsById[id]) {
|
|
var control = this._controlsById[id];
|
|
|
|
this._map.removeControl(control);
|
|
|
|
delete this._controlsById[id];
|
|
}
|
|
}
|
|
}, {
|
|
key: "clear",
|
|
value: function clear() {
|
|
for (var i = 0; i < this._controlsNoId.length; i++) {
|
|
var control = this._controlsNoId[i];
|
|
|
|
this._map.removeControl(control);
|
|
}
|
|
|
|
this._controlsNoId = [];
|
|
|
|
for (var key in this._controlsById) {
|
|
var _control = this._controlsById[key];
|
|
|
|
this._map.removeControl(_control);
|
|
}
|
|
|
|
this._controlsById = {};
|
|
}
|
|
}]);
|
|
|
|
return ControlStore;
|
|
}();
|
|
|
|
exports["default"] = ControlStore;
|
|
|
|
|
|
},{}],3:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.getCRS = getCRS;
|
|
|
|
var _leaflet = require("./global/leaflet");
|
|
|
|
var _leaflet2 = _interopRequireDefault(_leaflet);
|
|
|
|
var _proj4leaflet = require("./global/proj4leaflet");
|
|
|
|
var _proj4leaflet2 = _interopRequireDefault(_proj4leaflet);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
|
|
// Helper function to instanciate a ICRS instance.
|
|
function getCRS(crsOptions) {
|
|
var crs = _leaflet2["default"].CRS.EPSG3857; // Default Spherical Mercator
|
|
|
|
switch (crsOptions.crsClass) {
|
|
case "L.CRS.EPSG3857":
|
|
crs = _leaflet2["default"].CRS.EPSG3857;
|
|
break;
|
|
|
|
case "L.CRS.EPSG4326":
|
|
crs = _leaflet2["default"].CRS.EPSG4326;
|
|
break;
|
|
|
|
case "L.CRS.EPSG3395":
|
|
crs = _leaflet2["default"].CRS.EPSG3395;
|
|
break;
|
|
|
|
case "L.CRS.Simple":
|
|
crs = _leaflet2["default"].CRS.Simple;
|
|
break;
|
|
|
|
case "L.Proj.CRS":
|
|
if (crsOptions.options && crsOptions.options.bounds) {
|
|
crsOptions.options.bounds = _leaflet2["default"].bounds(crsOptions.options.bounds);
|
|
}
|
|
|
|
if (crsOptions.options && crsOptions.options.transformation) {
|
|
crsOptions.options.transformation = new _leaflet2["default"].Transformation(crsOptions.options.transformation[0], crsOptions.options.transformation[1], crsOptions.options.transformation[2], crsOptions.options.transformation[3]);
|
|
}
|
|
|
|
crs = new _proj4leaflet2["default"].CRS(crsOptions.code, crsOptions.proj4def, crsOptions.options);
|
|
break;
|
|
|
|
case "L.Proj.CRS.TMS":
|
|
if (crsOptions.options && crsOptions.options.bounds) {
|
|
crsOptions.options.bounds = _leaflet2["default"].bounds(crsOptions.options.bounds);
|
|
}
|
|
|
|
if (crsOptions.options && crsOptions.options.transformation) {
|
|
crsOptions.options.transformation = _leaflet2["default"].Transformation(crsOptions.options.transformation[0], crsOptions.options.transformation[1], crsOptions.options.transformation[2], crsOptions.options.transformation[3]);
|
|
} // L.Proj.CRS.TMS is deprecated as of Leaflet 1.x, fall back to L.Proj.CRS
|
|
//crs = new Proj4Leaflet.CRS.TMS(crsOptions.code, crsOptions.proj4def, crsOptions.projectedBounds, crsOptions.options);
|
|
|
|
|
|
crs = new _proj4leaflet2["default"].CRS(crsOptions.code, crsOptions.proj4def, crsOptions.options);
|
|
break;
|
|
}
|
|
|
|
return crs;
|
|
}
|
|
|
|
|
|
},{"./global/leaflet":10,"./global/proj4leaflet":11}],4:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports["default"] = undefined;
|
|
|
|
var _util = require("./util");
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
|
|
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
|
|
|
var DataFrame = /*#__PURE__*/function () {
|
|
function DataFrame() {
|
|
_classCallCheck(this, DataFrame);
|
|
|
|
this.columns = [];
|
|
this.colnames = [];
|
|
this.colstrict = [];
|
|
this.effectiveLength = 0;
|
|
this.colindices = {};
|
|
}
|
|
|
|
_createClass(DataFrame, [{
|
|
key: "_updateCachedProperties",
|
|
value: function _updateCachedProperties() {
|
|
var _this = this;
|
|
|
|
this.effectiveLength = 0;
|
|
this.colindices = {};
|
|
this.columns.forEach(function (column, i) {
|
|
_this.effectiveLength = Math.max(_this.effectiveLength, column.length);
|
|
_this.colindices[_this.colnames[i]] = i;
|
|
});
|
|
}
|
|
}, {
|
|
key: "_colIndex",
|
|
value: function _colIndex(colname) {
|
|
var index = this.colindices[colname];
|
|
if (typeof index === "undefined") return -1;
|
|
return index;
|
|
}
|
|
}, {
|
|
key: "col",
|
|
value: function col(name, values, strict) {
|
|
if (typeof name !== "string") throw new Error("Invalid column name \"" + name + "\"");
|
|
|
|
var index = this._colIndex(name);
|
|
|
|
if (arguments.length === 1) {
|
|
if (index < 0) return null;else return (0, _util.recycle)(this.columns[index], this.effectiveLength);
|
|
}
|
|
|
|
if (index < 0) {
|
|
index = this.colnames.length;
|
|
this.colnames.push(name);
|
|
}
|
|
|
|
this.columns[index] = (0, _util.asArray)(values);
|
|
this.colstrict[index] = !!strict; // TODO: Validate strictness (ensure lengths match up with other stricts)
|
|
|
|
this._updateCachedProperties();
|
|
|
|
return this;
|
|
}
|
|
}, {
|
|
key: "cbind",
|
|
value: function cbind(obj, strict) {
|
|
var _this2 = this;
|
|
|
|
Object.keys(obj).forEach(function (name) {
|
|
var coldata = obj[name];
|
|
|
|
_this2.col(name, coldata);
|
|
});
|
|
return this;
|
|
}
|
|
}, {
|
|
key: "get",
|
|
value: function get(row, col, missingOK) {
|
|
var _this3 = this;
|
|
|
|
if (row > this.effectiveLength) throw new Error("Row argument was out of bounds: " + row + " > " + this.effectiveLength);
|
|
var colIndex = -1;
|
|
|
|
if (typeof col === "undefined") {
|
|
var rowData = {};
|
|
this.colnames.forEach(function (name, i) {
|
|
rowData[name] = _this3.columns[i][row % _this3.columns[i].length];
|
|
});
|
|
return rowData;
|
|
} else if (typeof col === "string") {
|
|
colIndex = this._colIndex(col);
|
|
} else if (typeof col === "number") {
|
|
colIndex = col;
|
|
}
|
|
|
|
if (colIndex < 0 || colIndex > this.columns.length) {
|
|
if (missingOK) return void 0;else throw new Error("Unknown column index: " + col);
|
|
}
|
|
|
|
return this.columns[colIndex][row % this.columns[colIndex].length];
|
|
}
|
|
}, {
|
|
key: "nrow",
|
|
value: function nrow() {
|
|
return this.effectiveLength;
|
|
}
|
|
}]);
|
|
|
|
return DataFrame;
|
|
}();
|
|
|
|
exports["default"] = DataFrame;
|
|
|
|
|
|
},{"./util":17}],5:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
var _leaflet = require("./global/leaflet");
|
|
|
|
var _leaflet2 = _interopRequireDefault(_leaflet);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
|
|
// In RMarkdown's self-contained mode, we don't have a way to carry around the
|
|
// images that Leaflet needs but doesn't load into the page. Instead, we'll use
|
|
// the unpkg CDN.
|
|
if (typeof _leaflet2["default"].Icon.Default.imagePath === "undefined") {
|
|
_leaflet2["default"].Icon.Default.imagePath = "https://unpkg.com/leaflet@1.3.1/dist/images/";
|
|
}
|
|
|
|
|
|
},{"./global/leaflet":10}],6:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
var _leaflet = require("./global/leaflet");
|
|
|
|
var _leaflet2 = _interopRequireDefault(_leaflet);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
|
|
// add texxtsize, textOnly, and style
|
|
_leaflet2["default"].Tooltip.prototype.options.textsize = "10px";
|
|
_leaflet2["default"].Tooltip.prototype.options.textOnly = false;
|
|
_leaflet2["default"].Tooltip.prototype.options.style = null; // copy original layout to not completely stomp it.
|
|
|
|
var initLayoutOriginal = _leaflet2["default"].Tooltip.prototype._initLayout;
|
|
|
|
_leaflet2["default"].Tooltip.prototype._initLayout = function () {
|
|
initLayoutOriginal.call(this);
|
|
this._container.style.fontSize = this.options.textsize;
|
|
|
|
if (this.options.textOnly) {
|
|
_leaflet2["default"].DomUtil.addClass(this._container, "leaflet-tooltip-text-only");
|
|
}
|
|
|
|
if (this.options.style) {
|
|
for (var property in this.options.style) {
|
|
this._container.style[property] = this.options.style[property];
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
},{"./global/leaflet":10}],7:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
var _leaflet = require("./global/leaflet");
|
|
|
|
var _leaflet2 = _interopRequireDefault(_leaflet);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
|
|
var protocolRegex = /^\/\//;
|
|
|
|
var upgrade_protocol = function upgrade_protocol(urlTemplate) {
|
|
if (protocolRegex.test(urlTemplate)) {
|
|
if (window.location.protocol === "file:") {
|
|
// if in a local file, support http
|
|
// http should auto upgrade if necessary
|
|
urlTemplate = "http:" + urlTemplate;
|
|
}
|
|
}
|
|
|
|
return urlTemplate;
|
|
};
|
|
|
|
var originalLTileLayerInitialize = _leaflet2["default"].TileLayer.prototype.initialize;
|
|
|
|
_leaflet2["default"].TileLayer.prototype.initialize = function (urlTemplate, options) {
|
|
urlTemplate = upgrade_protocol(urlTemplate);
|
|
originalLTileLayerInitialize.call(this, urlTemplate, options);
|
|
};
|
|
|
|
var originalLTileLayerWMSInitialize = _leaflet2["default"].TileLayer.WMS.prototype.initialize;
|
|
|
|
_leaflet2["default"].TileLayer.WMS.prototype.initialize = function (urlTemplate, options) {
|
|
urlTemplate = upgrade_protocol(urlTemplate);
|
|
originalLTileLayerWMSInitialize.call(this, urlTemplate, options);
|
|
};
|
|
|
|
|
|
},{"./global/leaflet":10}],8:[function(require,module,exports){
|
|
(function (global){(function (){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports["default"] = global.HTMLWidgets;
|
|
|
|
|
|
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{}],9:[function(require,module,exports){
|
|
(function (global){(function (){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports["default"] = global.jQuery;
|
|
|
|
|
|
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{}],10:[function(require,module,exports){
|
|
(function (global){(function (){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports["default"] = global.L;
|
|
|
|
|
|
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{}],11:[function(require,module,exports){
|
|
(function (global){(function (){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports["default"] = global.L.Proj;
|
|
|
|
|
|
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{}],12:[function(require,module,exports){
|
|
(function (global){(function (){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports["default"] = global.Shiny;
|
|
|
|
|
|
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{}],13:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
var _jquery = require("./global/jquery");
|
|
|
|
var _jquery2 = _interopRequireDefault(_jquery);
|
|
|
|
var _leaflet = require("./global/leaflet");
|
|
|
|
var _leaflet2 = _interopRequireDefault(_leaflet);
|
|
|
|
var _shiny = require("./global/shiny");
|
|
|
|
var _shiny2 = _interopRequireDefault(_shiny);
|
|
|
|
var _htmlwidgets = require("./global/htmlwidgets");
|
|
|
|
var _htmlwidgets2 = _interopRequireDefault(_htmlwidgets);
|
|
|
|
var _util = require("./util");
|
|
|
|
var _crs_utils = require("./crs_utils");
|
|
|
|
var _controlStore = require("./control-store");
|
|
|
|
var _controlStore2 = _interopRequireDefault(_controlStore);
|
|
|
|
var _layerManager = require("./layer-manager");
|
|
|
|
var _layerManager2 = _interopRequireDefault(_layerManager);
|
|
|
|
var _methods = require("./methods");
|
|
|
|
var _methods2 = _interopRequireDefault(_methods);
|
|
|
|
require("./fixup-default-icon");
|
|
|
|
require("./fixup-default-tooltip");
|
|
|
|
require("./fixup-url-protocol");
|
|
|
|
var _dataframe = require("./dataframe");
|
|
|
|
var _dataframe2 = _interopRequireDefault(_dataframe);
|
|
|
|
var _clusterLayerStore = require("./cluster-layer-store");
|
|
|
|
var _clusterLayerStore2 = _interopRequireDefault(_clusterLayerStore);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
|
|
window.LeafletWidget = {};
|
|
window.LeafletWidget.utils = {};
|
|
|
|
var methods = window.LeafletWidget.methods = _jquery2["default"].extend({}, _methods2["default"]);
|
|
|
|
window.LeafletWidget.DataFrame = _dataframe2["default"];
|
|
window.LeafletWidget.ClusterLayerStore = _clusterLayerStore2["default"];
|
|
window.LeafletWidget.utils.getCRS = _crs_utils.getCRS; // Send updated bounds back to app. Takes a leaflet event object as input.
|
|
|
|
function updateBounds(map) {
|
|
var id = map.getContainer().id;
|
|
var bounds = map.getBounds();
|
|
|
|
_shiny2["default"].onInputChange(id + "_bounds", {
|
|
north: bounds.getNorthEast().lat,
|
|
east: bounds.getNorthEast().lng,
|
|
south: bounds.getSouthWest().lat,
|
|
west: bounds.getSouthWest().lng
|
|
});
|
|
|
|
_shiny2["default"].onInputChange(id + "_center", {
|
|
lng: map.getCenter().lng,
|
|
lat: map.getCenter().lat
|
|
});
|
|
|
|
_shiny2["default"].onInputChange(id + "_zoom", map.getZoom());
|
|
}
|
|
|
|
function preventUnintendedZoomOnScroll(map) {
|
|
// Prevent unwanted scroll capturing. Similar in purpose to
|
|
// https://github.com/CliffCloud/Leaflet.Sleep but with a
|
|
// different set of heuristics.
|
|
// The basic idea is that when a mousewheel/DOMMouseScroll
|
|
// event is seen, we disable scroll wheel zooming until the
|
|
// user moves their mouse cursor or clicks on the map. This
|
|
// is slightly trickier than just listening for mousemove,
|
|
// because mousemove is fired when the page is scrolled,
|
|
// even if the user did not physically move the mouse. We
|
|
// handle this by examining the mousemove event's screenX
|
|
// and screenY properties; if they change, we know it's a
|
|
// "true" move.
|
|
// lastScreen can never be null, but its x and y can.
|
|
var lastScreen = {
|
|
x: null,
|
|
y: null
|
|
};
|
|
(0, _jquery2["default"])(document).on("mousewheel DOMMouseScroll", "*", function (e) {
|
|
// Disable zooming (until the mouse moves or click)
|
|
map.scrollWheelZoom.disable(); // Any mousemove events at this screen position will be ignored.
|
|
|
|
lastScreen = {
|
|
x: e.originalEvent.screenX,
|
|
y: e.originalEvent.screenY
|
|
};
|
|
});
|
|
(0, _jquery2["default"])(document).on("mousemove", "*", function (e) {
|
|
// Did the mouse really move?
|
|
if (lastScreen.x !== null && e.screenX !== lastScreen.x || e.screenY !== lastScreen.y) {
|
|
// It really moved. Enable zooming.
|
|
map.scrollWheelZoom.enable();
|
|
lastScreen = {
|
|
x: null,
|
|
y: null
|
|
};
|
|
}
|
|
});
|
|
(0, _jquery2["default"])(document).on("mousedown", ".leaflet", function (e) {
|
|
// Clicking always enables zooming.
|
|
map.scrollWheelZoom.enable();
|
|
lastScreen = {
|
|
x: null,
|
|
y: null
|
|
};
|
|
});
|
|
}
|
|
|
|
_htmlwidgets2["default"].widget({
|
|
name: "leaflet",
|
|
type: "output",
|
|
factory: function factory(el, width, height) {
|
|
var map = null;
|
|
return {
|
|
// we need to store our map in our returned object.
|
|
getMap: function getMap() {
|
|
return map;
|
|
},
|
|
renderValue: function renderValue(data) {
|
|
// Create an appropriate CRS Object if specified
|
|
if (data && data.options && data.options.crs) {
|
|
data.options.crs = (0, _crs_utils.getCRS)(data.options.crs);
|
|
} // As per https://github.com/rstudio/leaflet/pull/294#discussion_r79584810
|
|
|
|
|
|
if (map) {
|
|
map.remove();
|
|
|
|
map = function () {
|
|
return;
|
|
}(); // undefine map
|
|
|
|
}
|
|
|
|
if (data.options.mapFactory && typeof data.options.mapFactory === "function") {
|
|
map = data.options.mapFactory(el, data.options);
|
|
} else {
|
|
map = _leaflet2["default"].map(el, data.options);
|
|
}
|
|
|
|
preventUnintendedZoomOnScroll(map); // Store some state in the map object
|
|
|
|
map.leafletr = {
|
|
// Has the map ever rendered successfully?
|
|
hasRendered: false,
|
|
// Data to be rendered when resize is called with area != 0
|
|
pendingRenderData: null
|
|
}; // Check if the map is rendered statically (no output binding)
|
|
|
|
if (_htmlwidgets2["default"].shinyMode && /\bshiny-bound-output\b/.test(el.className)) {
|
|
map.id = el.id; // Store the map on the element so we can find it later by ID
|
|
|
|
(0, _jquery2["default"])(el).data("leaflet-map", map); // When the map is clicked, send the coordinates back to the app
|
|
|
|
map.on("click", function (e) {
|
|
_shiny2["default"].onInputChange(map.id + "_click", {
|
|
lat: e.latlng.lat,
|
|
lng: e.latlng.lng,
|
|
".nonce": Math.random() // Force reactivity if lat/lng hasn't changed
|
|
|
|
});
|
|
});
|
|
var groupTimerId = null;
|
|
map.on("moveend", function (e) {
|
|
updateBounds(e.target);
|
|
}).on("layeradd layerremove", function (e) {
|
|
// If the layer that's coming or going is a group we created, tell
|
|
// the server.
|
|
if (map.layerManager.getGroupNameFromLayerGroup(e.layer)) {
|
|
// But to avoid chattiness, coalesce events
|
|
if (groupTimerId) {
|
|
clearTimeout(groupTimerId);
|
|
groupTimerId = null;
|
|
}
|
|
|
|
groupTimerId = setTimeout(function () {
|
|
groupTimerId = null;
|
|
|
|
_shiny2["default"].onInputChange(map.id + "_groups", map.layerManager.getVisibleGroups());
|
|
}, 100);
|
|
}
|
|
});
|
|
}
|
|
|
|
this.doRenderValue(data, map);
|
|
},
|
|
doRenderValue: function doRenderValue(data, map) {
|
|
// Leaflet does not behave well when you set up a bunch of layers when
|
|
// the map is not visible (width/height == 0). Popups get misaligned
|
|
// relative to their owning markers, and the fitBounds calculations
|
|
// are off. Therefore we wait until the map is actually showing to
|
|
// render the value (we rely on the resize() callback being invoked
|
|
// at the appropriate time).
|
|
if (el.offsetWidth === 0 || el.offsetHeight === 0) {
|
|
map.leafletr.pendingRenderData = data;
|
|
return;
|
|
}
|
|
|
|
map.leafletr.pendingRenderData = null; // Merge data options into defaults
|
|
|
|
var options = _jquery2["default"].extend({
|
|
zoomToLimits: "always"
|
|
}, data.options);
|
|
|
|
if (!map.layerManager) {
|
|
map.controls = new _controlStore2["default"](map);
|
|
map.layerManager = new _layerManager2["default"](map);
|
|
} else {
|
|
map.controls.clear();
|
|
map.layerManager.clear();
|
|
}
|
|
|
|
var explicitView = false;
|
|
|
|
if (data.setView) {
|
|
explicitView = true;
|
|
map.setView.apply(map, data.setView);
|
|
}
|
|
|
|
if (data.fitBounds) {
|
|
explicitView = true;
|
|
methods.fitBounds.apply(map, data.fitBounds);
|
|
}
|
|
|
|
if (data.flyTo) {
|
|
if (!explicitView && !map.leafletr.hasRendered) {
|
|
// must be done to give a initial starting point
|
|
map.fitWorld();
|
|
}
|
|
|
|
explicitView = true;
|
|
map.flyTo.apply(map, data.flyTo);
|
|
}
|
|
|
|
if (data.flyToBounds) {
|
|
if (!explicitView && !map.leafletr.hasRendered) {
|
|
// must be done to give a initial starting point
|
|
map.fitWorld();
|
|
}
|
|
|
|
explicitView = true;
|
|
methods.flyToBounds.apply(map, data.flyToBounds);
|
|
}
|
|
|
|
if (data.options.center) {
|
|
explicitView = true;
|
|
} // Returns true if the zoomToLimits option says that the map should be
|
|
// zoomed to map elements.
|
|
|
|
|
|
function needsZoom() {
|
|
return options.zoomToLimits === "always" || options.zoomToLimits === "first" && !map.leafletr.hasRendered;
|
|
}
|
|
|
|
if (!explicitView && needsZoom() && !map.getZoom()) {
|
|
if (data.limits && !_jquery2["default"].isEmptyObject(data.limits)) {
|
|
// Use the natural limits of what's being drawn on the map
|
|
// If the size of the bounding box is 0, leaflet gets all weird
|
|
var pad = 0.006;
|
|
|
|
if (data.limits.lat[0] === data.limits.lat[1]) {
|
|
data.limits.lat[0] = data.limits.lat[0] - pad;
|
|
data.limits.lat[1] = data.limits.lat[1] + pad;
|
|
}
|
|
|
|
if (data.limits.lng[0] === data.limits.lng[1]) {
|
|
data.limits.lng[0] = data.limits.lng[0] - pad;
|
|
data.limits.lng[1] = data.limits.lng[1] + pad;
|
|
}
|
|
|
|
map.fitBounds([[data.limits.lat[0], data.limits.lng[0]], [data.limits.lat[1], data.limits.lng[1]]]);
|
|
} else {
|
|
map.fitWorld();
|
|
}
|
|
}
|
|
|
|
for (var i = 0; data.calls && i < data.calls.length; i++) {
|
|
var call = data.calls[i];
|
|
if (methods[call.method]) methods[call.method].apply(map, call.args);else (0, _util.log)("Unknown method " + call.method);
|
|
}
|
|
|
|
map.leafletr.hasRendered = true;
|
|
|
|
if (_htmlwidgets2["default"].shinyMode) {
|
|
setTimeout(function () {
|
|
updateBounds(map);
|
|
}, 1);
|
|
}
|
|
},
|
|
resize: function resize(width, height) {
|
|
if (map) {
|
|
map.invalidateSize();
|
|
|
|
if (map.leafletr.pendingRenderData) {
|
|
this.doRenderValue(map.leafletr.pendingRenderData, map);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
if (_htmlwidgets2["default"].shinyMode) {
|
|
_shiny2["default"].addCustomMessageHandler("leaflet-calls", function (data) {
|
|
var id = data.id;
|
|
var el = document.getElementById(id);
|
|
var map = el ? (0, _jquery2["default"])(el).data("leaflet-map") : null;
|
|
|
|
if (!map) {
|
|
(0, _util.log)("Couldn't find map with id " + id);
|
|
return;
|
|
} // If the map has not rendered, stash the proposed `leafletProxy()` calls
|
|
// in `pendingRenderData.calls` to be run on display via `doRenderValue()`.
|
|
// This is necessary if the map has not been rendered.
|
|
// If new pendingRenderData is set via a new `leaflet()`, the previous calls will be discarded.
|
|
|
|
|
|
if (!map.leafletr.hasRendered) {
|
|
map.leafletr.pendingRenderData.calls = map.leafletr.pendingRenderData.calls.concat(data.calls);
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < data.calls.length; i++) {
|
|
var call = data.calls[i];
|
|
var args = call.args;
|
|
|
|
for (var _i = 0; _i < call.evals.length; _i++) {
|
|
window.HTMLWidgets.evaluateStringMember(args, call.evals[_i]);
|
|
}
|
|
|
|
if (call.dependencies) {
|
|
_shiny2["default"].renderDependencies(call.dependencies);
|
|
}
|
|
|
|
if (methods[call.method]) methods[call.method].apply(map, args);else (0, _util.log)("Unknown method " + call.method);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
},{"./cluster-layer-store":1,"./control-store":2,"./crs_utils":3,"./dataframe":4,"./fixup-default-icon":5,"./fixup-default-tooltip":6,"./fixup-url-protocol":7,"./global/htmlwidgets":8,"./global/jquery":9,"./global/leaflet":10,"./global/shiny":12,"./layer-manager":14,"./methods":15,"./util":17}],14:[function(require,module,exports){
|
|
(function (global){(function (){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports["default"] = undefined;
|
|
|
|
var _jquery = require("./global/jquery");
|
|
|
|
var _jquery2 = _interopRequireDefault(_jquery);
|
|
|
|
var _leaflet = require("./global/leaflet");
|
|
|
|
var _leaflet2 = _interopRequireDefault(_leaflet);
|
|
|
|
var _util = require("./util");
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
|
|
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
|
|
|
var LayerManager = /*#__PURE__*/function () {
|
|
function LayerManager(map) {
|
|
_classCallCheck(this, LayerManager);
|
|
|
|
this._map = map; // BEGIN layer indices
|
|
// {<groupname>: {<stamp>: layer}}
|
|
|
|
this._byGroup = {}; // {<categoryName>: {<stamp>: layer}}
|
|
|
|
this._byCategory = {}; // {<categoryName_layerId>: layer}
|
|
|
|
this._byLayerId = {}; // {<stamp>: {
|
|
// "group": <groupname>,
|
|
// "layerId": <layerId>,
|
|
// "category": <category>,
|
|
// "container": <container>
|
|
// }
|
|
// }
|
|
|
|
this._byStamp = {}; // {<crosstalkGroupName>: {<key>: [<stamp>, <stamp>, ...], ...}}
|
|
|
|
this._byCrosstalkGroup = {}; // END layer indices
|
|
// {<categoryName>: L.layerGroup}
|
|
|
|
this._categoryContainers = {}; // {<groupName>: L.layerGroup}
|
|
|
|
this._groupContainers = {};
|
|
}
|
|
|
|
_createClass(LayerManager, [{
|
|
key: "addLayer",
|
|
value: function addLayer(layer, category, layerId, group, ctGroup, ctKey) {
|
|
var _this = this;
|
|
|
|
// Was a group provided?
|
|
var hasId = typeof layerId === "string";
|
|
var grouped = typeof group === "string";
|
|
var stamp = _leaflet2["default"].Util.stamp(layer) + ""; // This will be the default layer group to add the layer to.
|
|
// We may overwrite this let before using it (i.e. if a group is assigned).
|
|
// This one liner creates the _categoryContainers[category] entry if it
|
|
// doesn't already exist.
|
|
|
|
var container = this._categoryContainers[category] = this._categoryContainers[category] || _leaflet2["default"].layerGroup().addTo(this._map);
|
|
|
|
var oldLayer = null;
|
|
|
|
if (hasId) {
|
|
// First, remove any layer with the same category and layerId
|
|
var prefixedLayerId = this._layerIdKey(category, layerId);
|
|
|
|
oldLayer = this._byLayerId[prefixedLayerId];
|
|
|
|
if (oldLayer) {
|
|
this._removeLayer(oldLayer);
|
|
} // Update layerId index
|
|
|
|
|
|
this._byLayerId[prefixedLayerId] = layer;
|
|
} // Update group index
|
|
|
|
|
|
if (grouped) {
|
|
this._byGroup[group] = this._byGroup[group] || {};
|
|
this._byGroup[group][stamp] = layer; // Since a group is assigned, don't add the layer to the category's layer
|
|
// group; instead, use the group's layer group.
|
|
// This one liner creates the _groupContainers[group] entry if it doesn't
|
|
// already exist.
|
|
|
|
container = this.getLayerGroup(group, true);
|
|
} // Update category index
|
|
|
|
|
|
this._byCategory[category] = this._byCategory[category] || {};
|
|
this._byCategory[category][stamp] = layer; // Update stamp index
|
|
|
|
var layerInfo = this._byStamp[stamp] = {
|
|
layer: layer,
|
|
group: group,
|
|
ctGroup: ctGroup,
|
|
ctKey: ctKey,
|
|
layerId: layerId,
|
|
category: category,
|
|
container: container,
|
|
hidden: false
|
|
}; // Update crosstalk group index
|
|
|
|
if (ctGroup) {
|
|
if (layer.setStyle) {
|
|
// Need to save this info so we know what to set opacity to later
|
|
layer.options.origOpacity = typeof layer.options.opacity !== "undefined" ? layer.options.opacity : 0.5;
|
|
layer.options.origFillOpacity = typeof layer.options.fillOpacity !== "undefined" ? layer.options.fillOpacity : 0.2;
|
|
}
|
|
|
|
var ctg = this._byCrosstalkGroup[ctGroup];
|
|
|
|
if (!ctg) {
|
|
ctg = this._byCrosstalkGroup[ctGroup] = {};
|
|
var crosstalk = global.crosstalk;
|
|
|
|
var handleFilter = function handleFilter(e) {
|
|
if (!e.value) {
|
|
var groupKeys = Object.keys(ctg);
|
|
|
|
for (var i = 0; i < groupKeys.length; i++) {
|
|
var key = groupKeys[i];
|
|
var _layerInfo = _this._byStamp[ctg[key]];
|
|
|
|
_this._setVisibility(_layerInfo, true);
|
|
}
|
|
} else {
|
|
var selectedKeys = {};
|
|
|
|
for (var _i = 0; _i < e.value.length; _i++) {
|
|
selectedKeys[e.value[_i]] = true;
|
|
}
|
|
|
|
var _groupKeys = Object.keys(ctg);
|
|
|
|
for (var _i2 = 0; _i2 < _groupKeys.length; _i2++) {
|
|
var _key = _groupKeys[_i2];
|
|
var _layerInfo2 = _this._byStamp[ctg[_key]];
|
|
|
|
_this._setVisibility(_layerInfo2, selectedKeys[_groupKeys[_i2]]);
|
|
}
|
|
}
|
|
};
|
|
|
|
var filterHandle = new crosstalk.FilterHandle(ctGroup);
|
|
filterHandle.on("change", handleFilter);
|
|
|
|
var handleSelection = function handleSelection(e) {
|
|
if (!e.value || !e.value.length) {
|
|
var groupKeys = Object.keys(ctg);
|
|
|
|
for (var i = 0; i < groupKeys.length; i++) {
|
|
var key = groupKeys[i];
|
|
var _layerInfo3 = _this._byStamp[ctg[key]];
|
|
|
|
_this._setOpacity(_layerInfo3, 1.0);
|
|
}
|
|
} else {
|
|
var selectedKeys = {};
|
|
|
|
for (var _i3 = 0; _i3 < e.value.length; _i3++) {
|
|
selectedKeys[e.value[_i3]] = true;
|
|
}
|
|
|
|
var _groupKeys2 = Object.keys(ctg);
|
|
|
|
for (var _i4 = 0; _i4 < _groupKeys2.length; _i4++) {
|
|
var _key2 = _groupKeys2[_i4];
|
|
var _layerInfo4 = _this._byStamp[ctg[_key2]];
|
|
|
|
_this._setOpacity(_layerInfo4, selectedKeys[_groupKeys2[_i4]] ? 1.0 : 0.2);
|
|
}
|
|
}
|
|
};
|
|
|
|
var selHandle = new crosstalk.SelectionHandle(ctGroup);
|
|
selHandle.on("change", handleSelection);
|
|
setTimeout(function () {
|
|
handleFilter({
|
|
value: filterHandle.filteredKeys
|
|
});
|
|
handleSelection({
|
|
value: selHandle.value
|
|
});
|
|
}, 100);
|
|
}
|
|
|
|
if (!ctg[ctKey]) ctg[ctKey] = [];
|
|
ctg[ctKey].push(stamp);
|
|
} // Add to container
|
|
|
|
|
|
if (!layerInfo.hidden) container.addLayer(layer);
|
|
return oldLayer;
|
|
}
|
|
}, {
|
|
key: "brush",
|
|
value: function brush(bounds, extraInfo) {
|
|
var _this2 = this;
|
|
|
|
/* eslint-disable no-console */
|
|
// For each Crosstalk group...
|
|
Object.keys(this._byCrosstalkGroup).forEach(function (ctGroupName) {
|
|
var ctg = _this2._byCrosstalkGroup[ctGroupName];
|
|
var selection = []; // ...iterate over each Crosstalk key (each of which may have multiple
|
|
// layers)...
|
|
|
|
Object.keys(ctg).forEach(function (ctKey) {
|
|
// ...and for each layer...
|
|
ctg[ctKey].forEach(function (stamp) {
|
|
var layerInfo = _this2._byStamp[stamp]; // ...if it's something with a point...
|
|
|
|
if (layerInfo.layer.getLatLng) {
|
|
// ... and it's inside the selection bounds...
|
|
// TODO: Use pixel containment, not lat/lng containment
|
|
if (bounds.contains(layerInfo.layer.getLatLng())) {
|
|
// ...add the key to the selection.
|
|
selection.push(ctKey);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
new global.crosstalk.SelectionHandle(ctGroupName).set(selection, extraInfo);
|
|
});
|
|
}
|
|
}, {
|
|
key: "unbrush",
|
|
value: function unbrush(extraInfo) {
|
|
Object.keys(this._byCrosstalkGroup).forEach(function (ctGroupName) {
|
|
new global.crosstalk.SelectionHandle(ctGroupName).clear(extraInfo);
|
|
});
|
|
}
|
|
}, {
|
|
key: "_setVisibility",
|
|
value: function _setVisibility(layerInfo, visible) {
|
|
if (layerInfo.hidden ^ visible) {
|
|
return;
|
|
} else if (visible) {
|
|
layerInfo.container.addLayer(layerInfo.layer);
|
|
layerInfo.hidden = false;
|
|
} else {
|
|
layerInfo.container.removeLayer(layerInfo.layer);
|
|
layerInfo.hidden = true;
|
|
}
|
|
}
|
|
}, {
|
|
key: "_setOpacity",
|
|
value: function _setOpacity(layerInfo, opacity) {
|
|
if (layerInfo.layer.setOpacity) {
|
|
layerInfo.layer.setOpacity(opacity);
|
|
} else if (layerInfo.layer.setStyle) {
|
|
layerInfo.layer.setStyle({
|
|
opacity: opacity * layerInfo.layer.options.origOpacity,
|
|
fillOpacity: opacity * layerInfo.layer.options.origFillOpacity
|
|
});
|
|
}
|
|
}
|
|
}, {
|
|
key: "getLayer",
|
|
value: function getLayer(category, layerId) {
|
|
return this._byLayerId[this._layerIdKey(category, layerId)];
|
|
}
|
|
}, {
|
|
key: "removeLayer",
|
|
value: function removeLayer(category, layerIds) {
|
|
var _this3 = this;
|
|
|
|
// Find layer info
|
|
_jquery2["default"].each((0, _util.asArray)(layerIds), function (i, layerId) {
|
|
var layer = _this3._byLayerId[_this3._layerIdKey(category, layerId)];
|
|
|
|
if (layer) {
|
|
_this3._removeLayer(layer);
|
|
}
|
|
});
|
|
}
|
|
}, {
|
|
key: "clearLayers",
|
|
value: function clearLayers(category) {
|
|
var _this4 = this;
|
|
|
|
// Find all layers in _byCategory[category]
|
|
var catTable = this._byCategory[category];
|
|
|
|
if (!catTable) {
|
|
return false;
|
|
} // Remove all layers. Make copy of keys to avoid mutating the collection
|
|
// behind the iterator you're accessing.
|
|
|
|
|
|
var stamps = [];
|
|
|
|
_jquery2["default"].each(catTable, function (k, v) {
|
|
stamps.push(k);
|
|
});
|
|
|
|
_jquery2["default"].each(stamps, function (i, stamp) {
|
|
_this4._removeLayer(stamp);
|
|
});
|
|
}
|
|
}, {
|
|
key: "getLayerGroup",
|
|
value: function getLayerGroup(group, ensureExists) {
|
|
var g = this._groupContainers[group];
|
|
|
|
if (ensureExists && !g) {
|
|
this._byGroup[group] = this._byGroup[group] || {};
|
|
g = this._groupContainers[group] = _leaflet2["default"].featureGroup();
|
|
g.groupname = group;
|
|
g.addTo(this._map);
|
|
}
|
|
|
|
return g;
|
|
}
|
|
}, {
|
|
key: "getGroupNameFromLayerGroup",
|
|
value: function getGroupNameFromLayerGroup(layerGroup) {
|
|
return layerGroup.groupname;
|
|
}
|
|
}, {
|
|
key: "getVisibleGroups",
|
|
value: function getVisibleGroups() {
|
|
var _this5 = this;
|
|
|
|
var result = [];
|
|
|
|
_jquery2["default"].each(this._groupContainers, function (k, v) {
|
|
if (_this5._map.hasLayer(v)) {
|
|
result.push(k);
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}
|
|
}, {
|
|
key: "getAllGroupNames",
|
|
value: function getAllGroupNames() {
|
|
var result = [];
|
|
|
|
_jquery2["default"].each(this._groupContainers, function (k, v) {
|
|
result.push(k);
|
|
});
|
|
|
|
return result;
|
|
}
|
|
}, {
|
|
key: "clearGroup",
|
|
value: function clearGroup(group) {
|
|
var _this6 = this;
|
|
|
|
// Find all layers in _byGroup[group]
|
|
var groupTable = this._byGroup[group];
|
|
|
|
if (!groupTable) {
|
|
return false;
|
|
} // Remove all layers. Make copy of keys to avoid mutating the collection
|
|
// behind the iterator you're accessing.
|
|
|
|
|
|
var stamps = [];
|
|
|
|
_jquery2["default"].each(groupTable, function (k, v) {
|
|
stamps.push(k);
|
|
});
|
|
|
|
_jquery2["default"].each(stamps, function (i, stamp) {
|
|
_this6._removeLayer(stamp);
|
|
});
|
|
}
|
|
}, {
|
|
key: "clear",
|
|
value: function clear() {
|
|
function clearLayerGroup(key, layerGroup) {
|
|
layerGroup.clearLayers();
|
|
} // Clear all indices and layerGroups
|
|
|
|
|
|
this._byGroup = {};
|
|
this._byCategory = {};
|
|
this._byLayerId = {};
|
|
this._byStamp = {};
|
|
this._byCrosstalkGroup = {};
|
|
|
|
_jquery2["default"].each(this._categoryContainers, clearLayerGroup);
|
|
|
|
this._categoryContainers = {};
|
|
|
|
_jquery2["default"].each(this._groupContainers, clearLayerGroup);
|
|
|
|
this._groupContainers = {};
|
|
}
|
|
}, {
|
|
key: "_removeLayer",
|
|
value: function _removeLayer(layer) {
|
|
var stamp;
|
|
|
|
if (typeof layer === "string") {
|
|
stamp = layer;
|
|
} else {
|
|
stamp = _leaflet2["default"].Util.stamp(layer);
|
|
}
|
|
|
|
var layerInfo = this._byStamp[stamp];
|
|
|
|
if (!layerInfo) {
|
|
return false;
|
|
}
|
|
|
|
layerInfo.container.removeLayer(stamp);
|
|
|
|
if (typeof layerInfo.group === "string") {
|
|
delete this._byGroup[layerInfo.group][stamp];
|
|
}
|
|
|
|
if (typeof layerInfo.layerId === "string") {
|
|
delete this._byLayerId[this._layerIdKey(layerInfo.category, layerInfo.layerId)];
|
|
}
|
|
|
|
delete this._byCategory[layerInfo.category][stamp];
|
|
delete this._byStamp[stamp];
|
|
|
|
if (layerInfo.ctGroup) {
|
|
var ctGroup = this._byCrosstalkGroup[layerInfo.ctGroup];
|
|
var layersForKey = ctGroup[layerInfo.ctKey];
|
|
var idx = layersForKey ? layersForKey.indexOf(stamp) : -1;
|
|
|
|
if (idx >= 0) {
|
|
if (layersForKey.length === 1) {
|
|
delete ctGroup[layerInfo.ctKey];
|
|
} else {
|
|
layersForKey.splice(idx, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, {
|
|
key: "_layerIdKey",
|
|
value: function _layerIdKey(category, layerId) {
|
|
return category + "\n" + layerId;
|
|
}
|
|
}]);
|
|
|
|
return LayerManager;
|
|
}();
|
|
|
|
exports["default"] = LayerManager;
|
|
|
|
|
|
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{"./global/jquery":9,"./global/leaflet":10,"./util":17}],15:[function(require,module,exports){
|
|
(function (global){(function (){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _jquery = require("./global/jquery");
|
|
|
|
var _jquery2 = _interopRequireDefault(_jquery);
|
|
|
|
var _leaflet = require("./global/leaflet");
|
|
|
|
var _leaflet2 = _interopRequireDefault(_leaflet);
|
|
|
|
var _shiny = require("./global/shiny");
|
|
|
|
var _shiny2 = _interopRequireDefault(_shiny);
|
|
|
|
var _htmlwidgets = require("./global/htmlwidgets");
|
|
|
|
var _htmlwidgets2 = _interopRequireDefault(_htmlwidgets);
|
|
|
|
var _util = require("./util");
|
|
|
|
var _crs_utils = require("./crs_utils");
|
|
|
|
var _dataframe = require("./dataframe");
|
|
|
|
var _dataframe2 = _interopRequireDefault(_dataframe);
|
|
|
|
var _clusterLayerStore = require("./cluster-layer-store");
|
|
|
|
var _clusterLayerStore2 = _interopRequireDefault(_clusterLayerStore);
|
|
|
|
var _mipmapper = require("./mipmapper");
|
|
|
|
var _mipmapper2 = _interopRequireDefault(_mipmapper);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
|
|
var methods = {};
|
|
exports["default"] = methods;
|
|
|
|
function mouseHandler(mapId, layerId, group, eventName, extraInfo) {
|
|
return function (e) {
|
|
if (!_htmlwidgets2["default"].shinyMode) return;
|
|
var latLng = e.target.getLatLng ? e.target.getLatLng() : e.latlng;
|
|
|
|
if (latLng) {
|
|
// retrieve only lat, lon values to remove prototype
|
|
// and extra parameters added by 3rd party modules
|
|
// these objects are for json serialization, not javascript
|
|
var latLngVal = _leaflet2["default"].latLng(latLng); // make sure it has consistent shape
|
|
|
|
|
|
latLng = {
|
|
lat: latLngVal.lat,
|
|
lng: latLngVal.lng
|
|
};
|
|
}
|
|
|
|
var eventInfo = _jquery2["default"].extend({
|
|
id: layerId,
|
|
".nonce": Math.random() // force reactivity
|
|
|
|
}, group !== null ? {
|
|
group: group
|
|
} : null, latLng, extraInfo);
|
|
|
|
_shiny2["default"].onInputChange(mapId + "_" + eventName, eventInfo);
|
|
};
|
|
}
|
|
|
|
methods.mouseHandler = mouseHandler;
|
|
|
|
methods.clearGroup = function (group) {
|
|
var _this = this;
|
|
|
|
_jquery2["default"].each((0, _util.asArray)(group), function (i, v) {
|
|
_this.layerManager.clearGroup(v);
|
|
});
|
|
};
|
|
|
|
methods.setView = function (center, zoom, options) {
|
|
this.setView(center, zoom, options);
|
|
};
|
|
|
|
methods.fitBounds = function (lat1, lng1, lat2, lng2, options) {
|
|
this.fitBounds([[lat1, lng1], [lat2, lng2]], options);
|
|
};
|
|
|
|
methods.flyTo = function (center, zoom, options) {
|
|
this.flyTo(center, zoom, options);
|
|
};
|
|
|
|
methods.flyToBounds = function (lat1, lng1, lat2, lng2, options) {
|
|
this.flyToBounds([[lat1, lng1], [lat2, lng2]], options);
|
|
};
|
|
|
|
methods.setMaxBounds = function (lat1, lng1, lat2, lng2) {
|
|
this.setMaxBounds([[lat1, lng1], [lat2, lng2]]);
|
|
};
|
|
|
|
methods.addPopups = function (lat, lng, popup, layerId, group, options) {
|
|
var _this2 = this;
|
|
|
|
var df = new _dataframe2["default"]().col("lat", lat).col("lng", lng).col("popup", popup).col("layerId", layerId).col("group", group).cbind(options);
|
|
|
|
var _loop = function _loop(i) {
|
|
if (_jquery2["default"].isNumeric(df.get(i, "lat")) && _jquery2["default"].isNumeric(df.get(i, "lng"))) {
|
|
(function () {
|
|
var popup = _leaflet2["default"].popup(df.get(i)).setLatLng([df.get(i, "lat"), df.get(i, "lng")]).setContent(df.get(i, "popup"));
|
|
|
|
var thisId = df.get(i, "layerId");
|
|
var thisGroup = df.get(i, "group");
|
|
this.layerManager.addLayer(popup, "popup", thisId, thisGroup);
|
|
}).call(_this2);
|
|
}
|
|
};
|
|
|
|
for (var i = 0; i < df.nrow(); i++) {
|
|
_loop(i);
|
|
}
|
|
};
|
|
|
|
methods.removePopup = function (layerId) {
|
|
this.layerManager.removeLayer("popup", layerId);
|
|
};
|
|
|
|
methods.clearPopups = function () {
|
|
this.layerManager.clearLayers("popup");
|
|
};
|
|
|
|
methods.addTiles = function (urlTemplate, layerId, group, options) {
|
|
this.layerManager.addLayer(_leaflet2["default"].tileLayer(urlTemplate, options), "tile", layerId, group);
|
|
};
|
|
|
|
methods.removeTiles = function (layerId) {
|
|
this.layerManager.removeLayer("tile", layerId);
|
|
};
|
|
|
|
methods.clearTiles = function () {
|
|
this.layerManager.clearLayers("tile");
|
|
};
|
|
|
|
methods.addWMSTiles = function (baseUrl, layerId, group, options) {
|
|
if (options && options.crs) {
|
|
options.crs = (0, _crs_utils.getCRS)(options.crs);
|
|
}
|
|
|
|
this.layerManager.addLayer(_leaflet2["default"].tileLayer.wms(baseUrl, options), "tile", layerId, group);
|
|
}; // Given:
|
|
// {data: ["a", "b", "c"], index: [0, 1, 0, 2]}
|
|
// returns:
|
|
// ["a", "b", "a", "c"]
|
|
|
|
|
|
function unpackStrings(iconset) {
|
|
if (!iconset) {
|
|
return iconset;
|
|
}
|
|
|
|
if (typeof iconset.index === "undefined") {
|
|
return iconset;
|
|
}
|
|
|
|
iconset.data = (0, _util.asArray)(iconset.data);
|
|
iconset.index = (0, _util.asArray)(iconset.index);
|
|
return _jquery2["default"].map(iconset.index, function (e, i) {
|
|
return iconset.data[e];
|
|
});
|
|
}
|
|
|
|
function addMarkers(map, df, group, clusterOptions, clusterId, markerFunc) {
|
|
(function () {
|
|
var _this3 = this;
|
|
|
|
var clusterGroup = this.layerManager.getLayer("cluster", clusterId),
|
|
cluster = clusterOptions !== null;
|
|
|
|
if (cluster && !clusterGroup) {
|
|
clusterGroup = _leaflet2["default"].markerClusterGroup.layerSupport(clusterOptions);
|
|
|
|
if (clusterOptions.freezeAtZoom) {
|
|
var freezeAtZoom = clusterOptions.freezeAtZoom;
|
|
delete clusterOptions.freezeAtZoom;
|
|
clusterGroup.freezeAtZoom(freezeAtZoom);
|
|
}
|
|
|
|
clusterGroup.clusterLayerStore = new _clusterLayerStore2["default"](clusterGroup);
|
|
}
|
|
|
|
var extraInfo = cluster ? {
|
|
clusterId: clusterId
|
|
} : {};
|
|
|
|
var _loop2 = function _loop2(i) {
|
|
if (_jquery2["default"].isNumeric(df.get(i, "lat")) && _jquery2["default"].isNumeric(df.get(i, "lng"))) {
|
|
(function () {
|
|
var marker = markerFunc(df, i);
|
|
var thisId = df.get(i, "layerId");
|
|
var thisGroup = cluster ? null : df.get(i, "group");
|
|
|
|
if (cluster) {
|
|
clusterGroup.clusterLayerStore.add(marker, thisId);
|
|
} else {
|
|
this.layerManager.addLayer(marker, "marker", thisId, thisGroup, df.get(i, "ctGroup", true), df.get(i, "ctKey", true));
|
|
}
|
|
|
|
var popup = df.get(i, "popup");
|
|
var popupOptions = df.get(i, "popupOptions");
|
|
|
|
if (popup !== null) {
|
|
if (popupOptions !== null) {
|
|
marker.bindPopup(popup, popupOptions);
|
|
} else {
|
|
marker.bindPopup(popup);
|
|
}
|
|
}
|
|
|
|
var label = df.get(i, "label");
|
|
var labelOptions = df.get(i, "labelOptions");
|
|
|
|
if (label !== null) {
|
|
if (labelOptions !== null) {
|
|
if (labelOptions.permanent) {
|
|
marker.bindTooltip(label, labelOptions).openTooltip();
|
|
} else {
|
|
marker.bindTooltip(label, labelOptions);
|
|
}
|
|
} else {
|
|
marker.bindTooltip(label);
|
|
}
|
|
}
|
|
|
|
marker.on("click", mouseHandler(this.id, thisId, thisGroup, "marker_click", extraInfo), this);
|
|
marker.on("mouseover", mouseHandler(this.id, thisId, thisGroup, "marker_mouseover", extraInfo), this);
|
|
marker.on("mouseout", mouseHandler(this.id, thisId, thisGroup, "marker_mouseout", extraInfo), this);
|
|
marker.on("dragend", mouseHandler(this.id, thisId, thisGroup, "marker_dragend", extraInfo), this);
|
|
}).call(_this3);
|
|
}
|
|
};
|
|
|
|
for (var i = 0; i < df.nrow(); i++) {
|
|
_loop2(i);
|
|
}
|
|
|
|
if (cluster) {
|
|
this.layerManager.addLayer(clusterGroup, "cluster", clusterId, group);
|
|
}
|
|
}).call(map);
|
|
}
|
|
|
|
methods.addGenericMarkers = addMarkers;
|
|
|
|
methods.addMarkers = function (lat, lng, icon, layerId, group, options, popup, popupOptions, clusterOptions, clusterId, label, labelOptions, crosstalkOptions) {
|
|
var icondf;
|
|
var getIcon;
|
|
|
|
if (icon) {
|
|
// Unpack icons
|
|
icon.iconUrl = unpackStrings(icon.iconUrl);
|
|
icon.iconRetinaUrl = unpackStrings(icon.iconRetinaUrl);
|
|
icon.shadowUrl = unpackStrings(icon.shadowUrl);
|
|
icon.shadowRetinaUrl = unpackStrings(icon.shadowRetinaUrl); // This cbinds the icon URLs and any other icon options; they're all
|
|
// present on the icon object.
|
|
|
|
icondf = new _dataframe2["default"]().cbind(icon); // Constructs an icon from a specified row of the icon dataframe.
|
|
|
|
getIcon = function getIcon(i) {
|
|
var opts = icondf.get(i);
|
|
|
|
if (!opts.iconUrl) {
|
|
return new _leaflet2["default"].Icon.Default();
|
|
} // Composite options (like points or sizes) are passed from R with each
|
|
// individual component as its own option. We need to combine them now
|
|
// into their composite form.
|
|
|
|
|
|
if (opts.iconWidth) {
|
|
opts.iconSize = [opts.iconWidth, opts.iconHeight];
|
|
}
|
|
|
|
if (opts.shadowWidth) {
|
|
opts.shadowSize = [opts.shadowWidth, opts.shadowHeight];
|
|
}
|
|
|
|
if (opts.iconAnchorX) {
|
|
opts.iconAnchor = [opts.iconAnchorX, opts.iconAnchorY];
|
|
}
|
|
|
|
if (opts.shadowAnchorX) {
|
|
opts.shadowAnchor = [opts.shadowAnchorX, opts.shadowAnchorY];
|
|
}
|
|
|
|
if (opts.popupAnchorX) {
|
|
opts.popupAnchor = [opts.popupAnchorX, opts.popupAnchorY];
|
|
}
|
|
|
|
return new _leaflet2["default"].Icon(opts);
|
|
};
|
|
}
|
|
|
|
if (!(_jquery2["default"].isEmptyObject(lat) || _jquery2["default"].isEmptyObject(lng)) || _jquery2["default"].isNumeric(lat) && _jquery2["default"].isNumeric(lng)) {
|
|
var df = new _dataframe2["default"]().col("lat", lat).col("lng", lng).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).cbind(options).cbind(crosstalkOptions || {});
|
|
if (icon) icondf.effectiveLength = df.nrow();
|
|
addMarkers(this, df, group, clusterOptions, clusterId, function (df, i) {
|
|
var options = df.get(i);
|
|
if (icon) options.icon = getIcon(i);
|
|
return _leaflet2["default"].marker([df.get(i, "lat"), df.get(i, "lng")], options);
|
|
});
|
|
}
|
|
};
|
|
|
|
methods.addAwesomeMarkers = function (lat, lng, icon, layerId, group, options, popup, popupOptions, clusterOptions, clusterId, label, labelOptions, crosstalkOptions) {
|
|
var icondf;
|
|
var getIcon;
|
|
|
|
if (icon) {
|
|
// This cbinds the icon URLs and any other icon options; they're all
|
|
// present on the icon object.
|
|
icondf = new _dataframe2["default"]().cbind(icon); // Constructs an icon from a specified row of the icon dataframe.
|
|
|
|
getIcon = function getIcon(i) {
|
|
var opts = icondf.get(i);
|
|
|
|
if (!opts) {
|
|
return new _leaflet2["default"].AwesomeMarkers.icon();
|
|
}
|
|
|
|
if (opts.squareMarker) {
|
|
opts.className = "awesome-marker awesome-marker-square";
|
|
}
|
|
|
|
return new _leaflet2["default"].AwesomeMarkers.icon(opts);
|
|
};
|
|
}
|
|
|
|
if (!(_jquery2["default"].isEmptyObject(lat) || _jquery2["default"].isEmptyObject(lng)) || _jquery2["default"].isNumeric(lat) && _jquery2["default"].isNumeric(lng)) {
|
|
var df = new _dataframe2["default"]().col("lat", lat).col("lng", lng).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).cbind(options).cbind(crosstalkOptions || {});
|
|
if (icon) icondf.effectiveLength = df.nrow();
|
|
addMarkers(this, df, group, clusterOptions, clusterId, function (df, i) {
|
|
var options = df.get(i);
|
|
if (icon) options.icon = getIcon(i);
|
|
return _leaflet2["default"].marker([df.get(i, "lat"), df.get(i, "lng")], options);
|
|
});
|
|
}
|
|
};
|
|
|
|
function addLayers(map, category, df, layerFunc) {
|
|
var _loop3 = function _loop3(i) {
|
|
(function () {
|
|
var layer = layerFunc(df, i);
|
|
|
|
if (!_jquery2["default"].isEmptyObject(layer)) {
|
|
var thisId = df.get(i, "layerId");
|
|
var thisGroup = df.get(i, "group");
|
|
this.layerManager.addLayer(layer, category, thisId, thisGroup, df.get(i, "ctGroup", true), df.get(i, "ctKey", true));
|
|
|
|
if (layer.bindPopup) {
|
|
var popup = df.get(i, "popup");
|
|
var popupOptions = df.get(i, "popupOptions");
|
|
|
|
if (popup !== null) {
|
|
if (popupOptions !== null) {
|
|
layer.bindPopup(popup, popupOptions);
|
|
} else {
|
|
layer.bindPopup(popup);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (layer.bindTooltip) {
|
|
var label = df.get(i, "label");
|
|
var labelOptions = df.get(i, "labelOptions");
|
|
|
|
if (label !== null) {
|
|
if (labelOptions !== null) {
|
|
layer.bindTooltip(label, labelOptions);
|
|
} else {
|
|
layer.bindTooltip(label);
|
|
}
|
|
}
|
|
}
|
|
|
|
layer.on("click", mouseHandler(this.id, thisId, thisGroup, category + "_click"), this);
|
|
layer.on("mouseover", mouseHandler(this.id, thisId, thisGroup, category + "_mouseover"), this);
|
|
layer.on("mouseout", mouseHandler(this.id, thisId, thisGroup, category + "_mouseout"), this);
|
|
var highlightStyle = df.get(i, "highlightOptions");
|
|
|
|
if (!_jquery2["default"].isEmptyObject(highlightStyle)) {
|
|
var defaultStyle = {};
|
|
|
|
_jquery2["default"].each(highlightStyle, function (k, v) {
|
|
if (k != "bringToFront" && k != "sendToBack") {
|
|
if (df.get(i, k)) {
|
|
defaultStyle[k] = df.get(i, k);
|
|
}
|
|
}
|
|
});
|
|
|
|
layer.on("mouseover", function (e) {
|
|
this.setStyle(highlightStyle);
|
|
|
|
if (highlightStyle.bringToFront) {
|
|
this.bringToFront();
|
|
}
|
|
});
|
|
layer.on("mouseout", function (e) {
|
|
this.setStyle(defaultStyle);
|
|
|
|
if (highlightStyle.sendToBack) {
|
|
this.bringToBack();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}).call(map);
|
|
};
|
|
|
|
for (var i = 0; i < df.nrow(); i++) {
|
|
_loop3(i);
|
|
}
|
|
}
|
|
|
|
methods.addGenericLayers = addLayers;
|
|
|
|
methods.addCircles = function (lat, lng, radius, layerId, group, options, popup, popupOptions, label, labelOptions, highlightOptions, crosstalkOptions) {
|
|
if (!(_jquery2["default"].isEmptyObject(lat) || _jquery2["default"].isEmptyObject(lng)) || _jquery2["default"].isNumeric(lat) && _jquery2["default"].isNumeric(lng)) {
|
|
var df = new _dataframe2["default"]().col("lat", lat).col("lng", lng).col("radius", radius).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).col("highlightOptions", highlightOptions).cbind(options).cbind(crosstalkOptions || {});
|
|
addLayers(this, "shape", df, function (df, i) {
|
|
if (_jquery2["default"].isNumeric(df.get(i, "lat")) && _jquery2["default"].isNumeric(df.get(i, "lng")) && _jquery2["default"].isNumeric(df.get(i, "radius"))) {
|
|
return _leaflet2["default"].circle([df.get(i, "lat"), df.get(i, "lng")], df.get(i, "radius"), df.get(i));
|
|
} else {
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
methods.addCircleMarkers = function (lat, lng, radius, layerId, group, options, clusterOptions, clusterId, popup, popupOptions, label, labelOptions, crosstalkOptions) {
|
|
if (!(_jquery2["default"].isEmptyObject(lat) || _jquery2["default"].isEmptyObject(lng)) || _jquery2["default"].isNumeric(lat) && _jquery2["default"].isNumeric(lng)) {
|
|
var df = new _dataframe2["default"]().col("lat", lat).col("lng", lng).col("radius", radius).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).cbind(crosstalkOptions || {}).cbind(options);
|
|
addMarkers(this, df, group, clusterOptions, clusterId, function (df, i) {
|
|
return _leaflet2["default"].circleMarker([df.get(i, "lat"), df.get(i, "lng")], df.get(i));
|
|
});
|
|
}
|
|
};
|
|
/*
|
|
* @param lat Array of arrays of latitude coordinates for polylines
|
|
* @param lng Array of arrays of longitude coordinates for polylines
|
|
*/
|
|
|
|
|
|
methods.addPolylines = function (polygons, layerId, group, options, popup, popupOptions, label, labelOptions, highlightOptions) {
|
|
if (polygons.length > 0) {
|
|
var df = new _dataframe2["default"]().col("shapes", polygons).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).col("highlightOptions", highlightOptions).cbind(options);
|
|
addLayers(this, "shape", df, function (df, i) {
|
|
var shapes = df.get(i, "shapes");
|
|
shapes = shapes.map(function (shape) {
|
|
return _htmlwidgets2["default"].dataframeToD3(shape[0]);
|
|
});
|
|
|
|
if (shapes.length > 1) {
|
|
return _leaflet2["default"].polyline(shapes, df.get(i));
|
|
} else {
|
|
return _leaflet2["default"].polyline(shapes[0], df.get(i));
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
methods.removeMarker = function (layerId) {
|
|
this.layerManager.removeLayer("marker", layerId);
|
|
};
|
|
|
|
methods.clearMarkers = function () {
|
|
this.layerManager.clearLayers("marker");
|
|
};
|
|
|
|
methods.removeMarkerCluster = function (layerId) {
|
|
this.layerManager.removeLayer("cluster", layerId);
|
|
};
|
|
|
|
methods.removeMarkerFromCluster = function (layerId, clusterId) {
|
|
var cluster = this.layerManager.getLayer("cluster", clusterId);
|
|
if (!cluster) return;
|
|
cluster.clusterLayerStore.remove(layerId);
|
|
};
|
|
|
|
methods.clearMarkerClusters = function () {
|
|
this.layerManager.clearLayers("cluster");
|
|
};
|
|
|
|
methods.removeShape = function (layerId) {
|
|
this.layerManager.removeLayer("shape", layerId);
|
|
};
|
|
|
|
methods.clearShapes = function () {
|
|
this.layerManager.clearLayers("shape");
|
|
};
|
|
|
|
methods.addRectangles = function (lat1, lng1, lat2, lng2, layerId, group, options, popup, popupOptions, label, labelOptions, highlightOptions) {
|
|
var df = new _dataframe2["default"]().col("lat1", lat1).col("lng1", lng1).col("lat2", lat2).col("lng2", lng2).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).col("highlightOptions", highlightOptions).cbind(options);
|
|
addLayers(this, "shape", df, function (df, i) {
|
|
if (_jquery2["default"].isNumeric(df.get(i, "lat1")) && _jquery2["default"].isNumeric(df.get(i, "lng1")) && _jquery2["default"].isNumeric(df.get(i, "lat2")) && _jquery2["default"].isNumeric(df.get(i, "lng2"))) {
|
|
return _leaflet2["default"].rectangle([[df.get(i, "lat1"), df.get(i, "lng1")], [df.get(i, "lat2"), df.get(i, "lng2")]], df.get(i));
|
|
} else {
|
|
return null;
|
|
}
|
|
});
|
|
};
|
|
/*
|
|
* @param lat Array of arrays of latitude coordinates for polygons
|
|
* @param lng Array of arrays of longitude coordinates for polygons
|
|
*/
|
|
|
|
|
|
methods.addPolygons = function (polygons, layerId, group, options, popup, popupOptions, label, labelOptions, highlightOptions) {
|
|
if (polygons.length > 0) {
|
|
var df = new _dataframe2["default"]().col("shapes", polygons).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).col("highlightOptions", highlightOptions).cbind(options);
|
|
addLayers(this, "shape", df, function (df, i) {
|
|
// This code used to use L.multiPolygon, but that caused
|
|
// double-click on a multipolygon to fail to zoom in on the
|
|
// map. Surprisingly, putting all the rings in a single
|
|
// polygon seems to still work; complicated multipolygons
|
|
// are still rendered correctly.
|
|
var shapes = df.get(i, "shapes").map(function (polygon) {
|
|
return polygon.map(_htmlwidgets2["default"].dataframeToD3);
|
|
}).reduce(function (acc, val) {
|
|
return acc.concat(val);
|
|
}, []);
|
|
return _leaflet2["default"].polygon(shapes, df.get(i));
|
|
});
|
|
}
|
|
};
|
|
|
|
methods.addGeoJSON = function (data, layerId, group, style) {
|
|
// This time, self is actually needed because the callbacks below need
|
|
// to access both the inner and outer senses of "this"
|
|
var self = this;
|
|
|
|
if (typeof data === "string") {
|
|
data = JSON.parse(data);
|
|
}
|
|
|
|
var globalStyle = _jquery2["default"].extend({}, style, data.style || {});
|
|
|
|
var gjlayer = _leaflet2["default"].geoJson(data, {
|
|
style: function style(feature) {
|
|
if (feature.style || feature.properties.style) {
|
|
return _jquery2["default"].extend({}, globalStyle, feature.style, feature.properties.style);
|
|
} else {
|
|
return globalStyle;
|
|
}
|
|
},
|
|
onEachFeature: function onEachFeature(feature, layer) {
|
|
var extraInfo = {
|
|
featureId: feature.id,
|
|
properties: feature.properties
|
|
};
|
|
var popup = feature.properties ? feature.properties.popup : null;
|
|
if (typeof popup !== "undefined" && popup !== null) layer.bindPopup(popup);
|
|
layer.on("click", mouseHandler(self.id, layerId, group, "geojson_click", extraInfo), this);
|
|
layer.on("mouseover", mouseHandler(self.id, layerId, group, "geojson_mouseover", extraInfo), this);
|
|
layer.on("mouseout", mouseHandler(self.id, layerId, group, "geojson_mouseout", extraInfo), this);
|
|
}
|
|
});
|
|
|
|
this.layerManager.addLayer(gjlayer, "geojson", layerId, group);
|
|
};
|
|
|
|
methods.removeGeoJSON = function (layerId) {
|
|
this.layerManager.removeLayer("geojson", layerId);
|
|
};
|
|
|
|
methods.clearGeoJSON = function () {
|
|
this.layerManager.clearLayers("geojson");
|
|
};
|
|
|
|
methods.addTopoJSON = function (data, layerId, group, style) {
|
|
// This time, self is actually needed because the callbacks below need
|
|
// to access both the inner and outer senses of "this"
|
|
var self = this;
|
|
|
|
if (typeof data === "string") {
|
|
data = JSON.parse(data);
|
|
}
|
|
|
|
var globalStyle = _jquery2["default"].extend({}, style, data.style || {});
|
|
|
|
var gjlayer = _leaflet2["default"].geoJson(null, {
|
|
style: function style(feature) {
|
|
if (feature.style || feature.properties.style) {
|
|
return _jquery2["default"].extend({}, globalStyle, feature.style, feature.properties.style);
|
|
} else {
|
|
return globalStyle;
|
|
}
|
|
},
|
|
onEachFeature: function onEachFeature(feature, layer) {
|
|
var extraInfo = {
|
|
featureId: feature.id,
|
|
properties: feature.properties
|
|
};
|
|
var popup = feature.properties.popup;
|
|
if (typeof popup !== "undefined" && popup !== null) layer.bindPopup(popup);
|
|
layer.on("click", mouseHandler(self.id, layerId, group, "topojson_click", extraInfo), this);
|
|
layer.on("mouseover", mouseHandler(self.id, layerId, group, "topojson_mouseover", extraInfo), this);
|
|
layer.on("mouseout", mouseHandler(self.id, layerId, group, "topojson_mouseout", extraInfo), this);
|
|
}
|
|
});
|
|
|
|
global.omnivore.topojson.parse(data, null, gjlayer);
|
|
this.layerManager.addLayer(gjlayer, "topojson", layerId, group);
|
|
};
|
|
|
|
methods.removeTopoJSON = function (layerId) {
|
|
this.layerManager.removeLayer("topojson", layerId);
|
|
};
|
|
|
|
methods.clearTopoJSON = function () {
|
|
this.layerManager.clearLayers("topojson");
|
|
};
|
|
|
|
methods.addControl = function (html, position, layerId, classes) {
|
|
function onAdd(map) {
|
|
var div = _leaflet2["default"].DomUtil.create("div", classes);
|
|
|
|
if (typeof layerId !== "undefined" && layerId !== null) {
|
|
div.setAttribute("id", layerId);
|
|
}
|
|
|
|
this._div = div; // It's possible for window.Shiny to be true but Shiny.initializeInputs to
|
|
// not be, when a static leaflet widget is included as part of the shiny
|
|
// UI directly (not through leafletOutput or uiOutput). In this case we
|
|
// don't do the normal Shiny stuff as that will all happen when Shiny
|
|
// itself loads and binds the entire doc.
|
|
|
|
if (window.Shiny && _shiny2["default"].initializeInputs) {
|
|
_shiny2["default"].renderHtml(html, this._div);
|
|
|
|
_shiny2["default"].initializeInputs(this._div);
|
|
|
|
_shiny2["default"].bindAll(this._div);
|
|
} else {
|
|
this._div.innerHTML = html;
|
|
}
|
|
|
|
return this._div;
|
|
}
|
|
|
|
function onRemove(map) {
|
|
if (window.Shiny && _shiny2["default"].unbindAll) {
|
|
_shiny2["default"].unbindAll(this._div);
|
|
}
|
|
}
|
|
|
|
var Control = _leaflet2["default"].Control.extend({
|
|
options: {
|
|
position: position
|
|
},
|
|
onAdd: onAdd,
|
|
onRemove: onRemove
|
|
});
|
|
|
|
this.controls.add(new Control(), layerId, html);
|
|
};
|
|
|
|
methods.addCustomControl = function (control, layerId) {
|
|
this.controls.add(control, layerId);
|
|
};
|
|
|
|
methods.removeControl = function (layerId) {
|
|
this.controls.remove(layerId);
|
|
};
|
|
|
|
methods.getControl = function (layerId) {
|
|
this.controls.get(layerId);
|
|
};
|
|
|
|
methods.clearControls = function () {
|
|
this.controls.clear();
|
|
};
|
|
|
|
methods.addLegend = function (options) {
|
|
var legend = _leaflet2["default"].control({
|
|
position: options.position
|
|
});
|
|
|
|
var gradSpan;
|
|
|
|
legend.onAdd = function (map) {
|
|
var div = _leaflet2["default"].DomUtil.create("div", options.className),
|
|
colors = options.colors,
|
|
labels = options.labels,
|
|
legendHTML = "";
|
|
|
|
if (options.type === "numeric") {
|
|
// # Formatting constants.
|
|
var singleBinHeight = 20; // The distance between tick marks, in px
|
|
|
|
var vMargin = 8; // If 1st tick mark starts at top of gradient, how
|
|
// many extra px are needed for the top half of the
|
|
// 1st label? (ditto for last tick mark/label)
|
|
|
|
var tickWidth = 4; // How wide should tick marks be, in px?
|
|
|
|
var labelPadding = 6; // How much distance to reserve for tick mark?
|
|
// (Must be >= tickWidth)
|
|
// # Derived formatting parameters.
|
|
// What's the height of a single bin, in percentage (of gradient height)?
|
|
// It might not just be 1/(n-1), if the gradient extends past the tick
|
|
// marks (which can be the case for pretty cut points).
|
|
|
|
var singleBinPct = (options.extra.p_n - options.extra.p_1) / (labels.length - 1); // Each bin is `singleBinHeight` high. How tall is the gradient?
|
|
|
|
var totalHeight = 1 / singleBinPct * singleBinHeight + 1; // How far should the first tick be shifted down, relative to the top
|
|
// of the gradient?
|
|
|
|
var tickOffset = singleBinHeight / singleBinPct * options.extra.p_1;
|
|
gradSpan = (0, _jquery2["default"])("<span/>").css({
|
|
"background": "linear-gradient(" + colors + ")",
|
|
"opacity": options.opacity,
|
|
"height": totalHeight + "px",
|
|
"width": "18px",
|
|
"display": "block",
|
|
"margin-top": vMargin + "px"
|
|
});
|
|
var leftDiv = (0, _jquery2["default"])("<div/>").css("float", "left"),
|
|
rightDiv = (0, _jquery2["default"])("<div/>").css("float", "left");
|
|
leftDiv.append(gradSpan);
|
|
(0, _jquery2["default"])(div).append(leftDiv).append(rightDiv).append((0, _jquery2["default"])("<br>")); // Have to attach the div to the body at this early point, so that the
|
|
// svg text getComputedTextLength() actually works, below.
|
|
|
|
document.body.appendChild(div);
|
|
var ns = "http://www.w3.org/2000/svg";
|
|
var svg = document.createElementNS(ns, "svg");
|
|
rightDiv.append(svg);
|
|
var g = document.createElementNS(ns, "g");
|
|
(0, _jquery2["default"])(g).attr("transform", "translate(0, " + vMargin + ")");
|
|
svg.appendChild(g); // max label width needed to set width of svg, and right-justify text
|
|
|
|
var maxLblWidth = 0; // Create tick marks and labels
|
|
|
|
_jquery2["default"].each(labels, function (i, label) {
|
|
var y = tickOffset + i * singleBinHeight + 0.5;
|
|
var thisLabel = document.createElementNS(ns, "text");
|
|
(0, _jquery2["default"])(thisLabel).text(labels[i]).attr("y", y).attr("dx", labelPadding).attr("dy", "0.5ex");
|
|
g.appendChild(thisLabel);
|
|
maxLblWidth = Math.max(maxLblWidth, thisLabel.getComputedTextLength());
|
|
var thisTick = document.createElementNS(ns, "line");
|
|
(0, _jquery2["default"])(thisTick).attr("x1", 0).attr("x2", tickWidth).attr("y1", y).attr("y2", y).attr("stroke-width", 1);
|
|
g.appendChild(thisTick);
|
|
}); // Now that we know the max label width, we can right-justify
|
|
|
|
|
|
(0, _jquery2["default"])(svg).find("text").attr("dx", labelPadding + maxLblWidth).attr("text-anchor", "end"); // Final size for <svg>
|
|
|
|
(0, _jquery2["default"])(svg).css({
|
|
width: maxLblWidth + labelPadding + "px",
|
|
height: totalHeight + vMargin * 2 + "px"
|
|
});
|
|
|
|
if (options.na_color && _jquery2["default"].inArray(options.na_label, labels) < 0) {
|
|
(0, _jquery2["default"])(div).append("<div><i style=\"" + "background:" + options.na_color + ";opacity:" + options.opacity + ";margin-right:" + labelPadding + "px" + ";\"></i>" + options.na_label + "</div>");
|
|
}
|
|
} else {
|
|
if (options.na_color && _jquery2["default"].inArray(options.na_label, labels) < 0) {
|
|
colors.push(options.na_color);
|
|
labels.push(options.na_label);
|
|
}
|
|
|
|
for (var i = 0; i < colors.length; i++) {
|
|
legendHTML += "<i style=\"background:" + colors[i] + ";opacity:" + options.opacity + "\"></i> " + labels[i] + "<br>";
|
|
}
|
|
|
|
div.innerHTML = legendHTML;
|
|
}
|
|
|
|
if (options.title) (0, _jquery2["default"])(div).prepend("<div style=\"margin-bottom:3px\"><strong>" + options.title + "</strong></div>");
|
|
return div;
|
|
};
|
|
|
|
if (options.group) {
|
|
// Auto generate a layerID if not provided
|
|
if (!options.layerId) {
|
|
options.layerId = _leaflet2["default"].Util.stamp(legend);
|
|
}
|
|
|
|
var map = this;
|
|
map.on("overlayadd", function (e) {
|
|
if (e.name === options.group) {
|
|
map.controls.add(legend, options.layerId);
|
|
}
|
|
});
|
|
map.on("overlayremove", function (e) {
|
|
if (e.name === options.group) {
|
|
map.controls.remove(options.layerId);
|
|
}
|
|
});
|
|
map.on("groupadd", function (e) {
|
|
if (e.name === options.group) {
|
|
map.controls.add(legend, options.layerId);
|
|
}
|
|
});
|
|
map.on("groupremove", function (e) {
|
|
if (e.name === options.group) {
|
|
map.controls.remove(options.layerId);
|
|
}
|
|
});
|
|
}
|
|
|
|
this.controls.add(legend, options.layerId);
|
|
};
|
|
|
|
methods.addLayersControl = function (baseGroups, overlayGroups, options) {
|
|
var _this4 = this;
|
|
|
|
// Only allow one layers control at a time
|
|
methods.removeLayersControl.call(this);
|
|
var firstLayer = true;
|
|
var base = {};
|
|
|
|
_jquery2["default"].each((0, _util.asArray)(baseGroups), function (i, g) {
|
|
var layer = _this4.layerManager.getLayerGroup(g, true);
|
|
|
|
if (layer) {
|
|
base[g] = layer; // Check if >1 base layers are visible; if so, hide all but the first one
|
|
|
|
if (_this4.hasLayer(layer)) {
|
|
if (firstLayer) {
|
|
firstLayer = false;
|
|
} else {
|
|
_this4.removeLayer(layer);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
var overlay = {};
|
|
|
|
_jquery2["default"].each((0, _util.asArray)(overlayGroups), function (i, g) {
|
|
var layer = _this4.layerManager.getLayerGroup(g, true);
|
|
|
|
if (layer) {
|
|
overlay[g] = layer;
|
|
}
|
|
});
|
|
|
|
this.currentLayersControl = _leaflet2["default"].control.layers(base, overlay, options);
|
|
this.addControl(this.currentLayersControl);
|
|
};
|
|
|
|
methods.removeLayersControl = function () {
|
|
if (this.currentLayersControl) {
|
|
this.removeControl(this.currentLayersControl);
|
|
this.currentLayersControl = null;
|
|
}
|
|
};
|
|
|
|
methods.addScaleBar = function (options) {
|
|
// Only allow one scale bar at a time
|
|
methods.removeScaleBar.call(this);
|
|
|
|
var scaleBar = _leaflet2["default"].control.scale(options).addTo(this);
|
|
|
|
this.currentScaleBar = scaleBar;
|
|
};
|
|
|
|
methods.removeScaleBar = function () {
|
|
if (this.currentScaleBar) {
|
|
this.currentScaleBar.remove();
|
|
this.currentScaleBar = null;
|
|
}
|
|
};
|
|
|
|
methods.hideGroup = function (group) {
|
|
var _this5 = this;
|
|
|
|
_jquery2["default"].each((0, _util.asArray)(group), function (i, g) {
|
|
var layer = _this5.layerManager.getLayerGroup(g, true);
|
|
|
|
if (layer) {
|
|
_this5.removeLayer(layer);
|
|
}
|
|
});
|
|
};
|
|
|
|
methods.showGroup = function (group) {
|
|
var _this6 = this;
|
|
|
|
_jquery2["default"].each((0, _util.asArray)(group), function (i, g) {
|
|
var layer = _this6.layerManager.getLayerGroup(g, true);
|
|
|
|
if (layer) {
|
|
_this6.addLayer(layer);
|
|
}
|
|
});
|
|
};
|
|
|
|
function setupShowHideGroupsOnZoom(map) {
|
|
if (map.leafletr._hasInitializedShowHideGroups) {
|
|
return;
|
|
}
|
|
|
|
map.leafletr._hasInitializedShowHideGroups = true;
|
|
|
|
function setVisibility(layer, visible, group) {
|
|
if (visible !== map.hasLayer(layer)) {
|
|
if (visible) {
|
|
map.addLayer(layer);
|
|
map.fire("groupadd", {
|
|
"name": group,
|
|
"layer": layer
|
|
});
|
|
} else {
|
|
map.removeLayer(layer);
|
|
map.fire("groupremove", {
|
|
"name": group,
|
|
"layer": layer
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
function showHideGroupsOnZoom() {
|
|
if (!map.layerManager) return;
|
|
var zoom = map.getZoom();
|
|
map.layerManager.getAllGroupNames().forEach(function (group) {
|
|
var layer = map.layerManager.getLayerGroup(group, false);
|
|
|
|
if (layer && typeof layer.zoomLevels !== "undefined") {
|
|
setVisibility(layer, layer.zoomLevels === true || layer.zoomLevels.indexOf(zoom) >= 0, group);
|
|
}
|
|
});
|
|
}
|
|
|
|
map.showHideGroupsOnZoom = showHideGroupsOnZoom;
|
|
map.on("zoomend", showHideGroupsOnZoom);
|
|
}
|
|
|
|
methods.setGroupOptions = function (group, options) {
|
|
var _this7 = this;
|
|
|
|
_jquery2["default"].each((0, _util.asArray)(group), function (i, g) {
|
|
var layer = _this7.layerManager.getLayerGroup(g, true); // This slightly tortured check is because 0 is a valid value for zoomLevels
|
|
|
|
|
|
if (typeof options.zoomLevels !== "undefined" && options.zoomLevels !== null) {
|
|
layer.zoomLevels = (0, _util.asArray)(options.zoomLevels);
|
|
}
|
|
});
|
|
|
|
setupShowHideGroupsOnZoom(this);
|
|
this.showHideGroupsOnZoom();
|
|
};
|
|
|
|
methods.addRasterImage = function (uri, bounds, opacity, attribution, layerId, group) {
|
|
// uri is a data URI containing an image. We want to paint this image as a
|
|
// layer at (top-left) bounds[0] to (bottom-right) bounds[1].
|
|
// We can't simply use ImageOverlay, as it uses bilinear scaling which looks
|
|
// awful as you zoom in (and sometimes shifts positions or disappears).
|
|
// Instead, we'll use a TileLayer.Canvas to draw pieces of the image.
|
|
// First, some helper functions.
|
|
// degree2tile converts latitude, longitude, and zoom to x and y tile
|
|
// numbers. The tile numbers returned can be non-integral, as there's no
|
|
// reason to expect that the lat/lng inputs are exactly on the border of two
|
|
// tiles.
|
|
//
|
|
// We'll use this to convert the bounds we got from the server, into coords
|
|
// in tile-space at a given zoom level. Note that once we do the conversion,
|
|
// we don't to do any more trigonometry to convert between pixel coordinates
|
|
// and tile coordinates; the source image pixel coords, destination canvas
|
|
// pixel coords, and tile coords all can be scaled linearly.
|
|
function degree2tile(lat, lng, zoom) {
|
|
// See http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
|
|
var latRad = lat * Math.PI / 180;
|
|
var n = Math.pow(2, zoom);
|
|
var x = (lng + 180) / 360 * n;
|
|
var y = (1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2 * n;
|
|
return {
|
|
x: x,
|
|
y: y
|
|
};
|
|
} // Given a range [from,to) and either one or two numbers, returns true if
|
|
// there is any overlap between [x,x1) and the range--or if x1 is omitted,
|
|
// then returns true if x is within [from,to).
|
|
|
|
|
|
function overlap(from, to, x,
|
|
/* optional */
|
|
x1) {
|
|
if (arguments.length == 3) x1 = x;
|
|
return x < to && x1 >= from;
|
|
}
|
|
|
|
function getCanvasSmoothingProperty(ctx) {
|
|
var candidates = ["imageSmoothingEnabled", "mozImageSmoothingEnabled", "webkitImageSmoothingEnabled", "msImageSmoothingEnabled"];
|
|
|
|
for (var i = 0; i < candidates.length; i++) {
|
|
if (typeof ctx[candidates[i]] !== "undefined") {
|
|
return candidates[i];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
} // Our general strategy is to:
|
|
// 1. Load the data URI in an Image() object, so we can get its pixel
|
|
// dimensions and the underlying image data. (We could have done this
|
|
// by not encoding as PNG at all but just send an array of RGBA values
|
|
// from the server, but that would inflate the JSON too much.)
|
|
// 2. Create a hidden canvas that we use just to extract the image data
|
|
// from the Image (using Context2D.getImageData()).
|
|
// 3. Create a TileLayer.Canvas and add it to the map.
|
|
// We want to synchronously create and attach the TileLayer.Canvas (so an
|
|
// immediate call to clearRasters() will be respected, for example), but
|
|
// Image loads its data asynchronously. Fortunately we can resolve this
|
|
// by putting TileLayer.Canvas into async mode, which will let us create
|
|
// and attach the layer but have it wait until the image is loaded before
|
|
// it actually draws anything.
|
|
// These are the variables that we will populate once the image is loaded.
|
|
|
|
|
|
var imgData = null; // 1d row-major array, four [0-255] integers per pixel
|
|
|
|
var imgDataMipMapper = null;
|
|
var w = null; // image width in pixels
|
|
|
|
var h = null; // image height in pixels
|
|
// We'll use this array to store callbacks that need to be invoked once
|
|
// imgData, w, and h have been resolved.
|
|
|
|
var imgDataCallbacks = []; // Consumers of imgData, w, and h can call this to be notified when data
|
|
// is available.
|
|
|
|
function getImageData(callback) {
|
|
if (imgData != null) {
|
|
// Must not invoke the callback immediately; it's too confusing and
|
|
// fragile to have a function invoke the callback *either* immediately
|
|
// or in the future. Better to be consistent here.
|
|
setTimeout(function () {
|
|
callback(imgData, w, h, imgDataMipMapper);
|
|
}, 0);
|
|
} else {
|
|
imgDataCallbacks.push(callback);
|
|
}
|
|
}
|
|
|
|
var img = new Image();
|
|
|
|
img.onload = function () {
|
|
// Save size
|
|
w = img.width;
|
|
h = img.height; // Create a dummy canvas to extract the image data
|
|
|
|
var imgDataCanvas = document.createElement("canvas");
|
|
imgDataCanvas.width = w;
|
|
imgDataCanvas.height = h;
|
|
imgDataCanvas.style.display = "none";
|
|
document.body.appendChild(imgDataCanvas);
|
|
var imgDataCtx = imgDataCanvas.getContext("2d");
|
|
imgDataCtx.drawImage(img, 0, 0); // Save the image data.
|
|
|
|
imgData = imgDataCtx.getImageData(0, 0, w, h).data;
|
|
imgDataMipMapper = new _mipmapper2["default"](img); // Done with the canvas, remove it from the page so it can be gc'd.
|
|
|
|
document.body.removeChild(imgDataCanvas); // Alert any getImageData callers who are waiting.
|
|
|
|
for (var i = 0; i < imgDataCallbacks.length; i++) {
|
|
imgDataCallbacks[i](imgData, w, h, imgDataMipMapper);
|
|
}
|
|
|
|
imgDataCallbacks = [];
|
|
};
|
|
|
|
img.src = uri;
|
|
|
|
var canvasTiles = _leaflet2["default"].gridLayer({
|
|
opacity: opacity,
|
|
attribution: attribution,
|
|
detectRetina: true,
|
|
async: true
|
|
}); // NOTE: The done() function MUST NOT be invoked until after the current
|
|
// tick; done() looks in Leaflet's tile cache for the current tile, and
|
|
// since it's still being constructed, it won't be found.
|
|
|
|
|
|
canvasTiles.createTile = function (tilePoint, done) {
|
|
var zoom = tilePoint.z;
|
|
|
|
var canvas = _leaflet2["default"].DomUtil.create("canvas");
|
|
|
|
var error; // setup tile width and height according to the options
|
|
|
|
var size = this.getTileSize();
|
|
canvas.width = size.x;
|
|
canvas.height = size.y;
|
|
getImageData(function (imgData, w, h, mipmapper) {
|
|
try {
|
|
// The Context2D we'll being drawing onto. It's always 256x256.
|
|
var ctx = canvas.getContext("2d"); // Convert our image data's top-left and bottom-right locations into
|
|
// x/y tile coordinates. This is essentially doing a spherical mercator
|
|
// projection, then multiplying by 2^zoom.
|
|
|
|
var topLeft = degree2tile(bounds[0][0], bounds[0][1], zoom);
|
|
var bottomRight = degree2tile(bounds[1][0], bounds[1][1], zoom); // The size of the image in x/y tile coordinates.
|
|
|
|
var extent = {
|
|
x: bottomRight.x - topLeft.x,
|
|
y: bottomRight.y - topLeft.y
|
|
}; // Short circuit if tile is totally disjoint from image.
|
|
|
|
if (!overlap(tilePoint.x, tilePoint.x + 1, topLeft.x, bottomRight.x)) return;
|
|
if (!overlap(tilePoint.y, tilePoint.y + 1, topLeft.y, bottomRight.y)) return; // The linear resolution of the tile we're drawing is always 256px per tile unit.
|
|
// If the linear resolution (in either direction) of the image is less than 256px
|
|
// per tile unit, then use nearest neighbor; otherwise, use the canvas's built-in
|
|
// scaling.
|
|
|
|
var imgRes = {
|
|
x: w / extent.x,
|
|
y: h / extent.y
|
|
}; // We can do the actual drawing in one of three ways:
|
|
// - Call drawImage(). This is easy and fast, and results in smooth
|
|
// interpolation (bilinear?). This is what we want when we are
|
|
// reducing the image from its native size.
|
|
// - Call drawImage() with imageSmoothingEnabled=false. This is easy
|
|
// and fast and gives us nearest-neighbor interpolation, which is what
|
|
// we want when enlarging the image. However, it's unsupported on many
|
|
// browsers (including QtWebkit).
|
|
// - Do a manual nearest-neighbor interpolation. This is what we'll fall
|
|
// back to when enlarging, and imageSmoothingEnabled isn't supported.
|
|
// In theory it's slower, but still pretty fast on my machine, and the
|
|
// results look the same AFAICT.
|
|
// Is imageSmoothingEnabled supported? If so, we can let canvas do
|
|
// nearest-neighbor interpolation for us.
|
|
|
|
var smoothingProperty = getCanvasSmoothingProperty(ctx);
|
|
|
|
if (smoothingProperty || imgRes.x >= 256 && imgRes.y >= 256) {
|
|
// Use built-in scaling
|
|
// Turn off anti-aliasing if necessary
|
|
if (smoothingProperty) {
|
|
ctx[smoothingProperty] = imgRes.x >= 256 && imgRes.y >= 256;
|
|
} // Don't necessarily draw with the full-size image; if we're
|
|
// downscaling, use the mipmapper to get a pre-downscaled image
|
|
// (see comments on Mipmapper class for why this matters).
|
|
|
|
|
|
mipmapper.getBySize(extent.x * 256, extent.y * 256, function (mip) {
|
|
// It's possible that the image will go off the edge of the canvas--
|
|
// that's OK, the canvas should clip appropriately.
|
|
ctx.drawImage(mip, // Convert abs tile coords to rel tile coords, then *256 to convert
|
|
// to rel pixel coords
|
|
(topLeft.x - tilePoint.x) * 256, (topLeft.y - tilePoint.y) * 256, // Always draw the whole thing and let canvas clip; so we can just
|
|
// convert from size in tile coords straight to pixels
|
|
extent.x * 256, extent.y * 256);
|
|
});
|
|
} else {
|
|
// Use manual nearest-neighbor interpolation
|
|
// Calculate the source image pixel coordinates that correspond with
|
|
// the top-left and bottom-right of this tile. (If the source image
|
|
// only partially overlaps the tile, we use max/min to limit the
|
|
// sourceStart/End to only reflect the overlapping portion.)
|
|
var sourceStart = {
|
|
x: Math.max(0, Math.floor((tilePoint.x - topLeft.x) * imgRes.x)),
|
|
y: Math.max(0, Math.floor((tilePoint.y - topLeft.y) * imgRes.y))
|
|
};
|
|
var sourceEnd = {
|
|
x: Math.min(w, Math.ceil((tilePoint.x + 1 - topLeft.x) * imgRes.x)),
|
|
y: Math.min(h, Math.ceil((tilePoint.y + 1 - topLeft.y) * imgRes.y))
|
|
}; // The size, in dest pixels, that each source pixel should occupy.
|
|
// This might be greater or less than 1 (e.g. if x and y resolution
|
|
// are very different).
|
|
|
|
var pixelSize = {
|
|
x: 256 / imgRes.x,
|
|
y: 256 / imgRes.y
|
|
}; // For each pixel in the source image that overlaps the tile...
|
|
|
|
for (var row = sourceStart.y; row < sourceEnd.y; row++) {
|
|
for (var col = sourceStart.x; col < sourceEnd.x; col++) {
|
|
// ...extract the pixel data...
|
|
var i = (row * w + col) * 4;
|
|
var r = imgData[i];
|
|
var g = imgData[i + 1];
|
|
var b = imgData[i + 2];
|
|
var a = imgData[i + 3];
|
|
ctx.fillStyle = "rgba(" + [r, g, b, a / 255].join(",") + ")"; // ...calculate the corresponding pixel coord in the dest image
|
|
// where it should be drawn...
|
|
|
|
var pixelPos = {
|
|
x: (col / imgRes.x + topLeft.x - tilePoint.x) * 256,
|
|
y: (row / imgRes.y + topLeft.y - tilePoint.y) * 256
|
|
}; // ...and draw a rectangle there.
|
|
|
|
ctx.fillRect(Math.round(pixelPos.x), Math.round(pixelPos.y), // Looks crazy, but this is necessary to prevent rounding from
|
|
// causing overlap between this rect and its neighbors. The
|
|
// minuend is the location of the next pixel, while the
|
|
// subtrahend is the position of the current pixel (to turn an
|
|
// absolute coordinate to a width/height). Yes, I had to look
|
|
// up minuend and subtrahend.
|
|
Math.round(pixelPos.x + pixelSize.x) - Math.round(pixelPos.x), Math.round(pixelPos.y + pixelSize.y) - Math.round(pixelPos.y));
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
error = e;
|
|
} finally {
|
|
done(error, canvas);
|
|
}
|
|
});
|
|
return canvas;
|
|
};
|
|
|
|
this.layerManager.addLayer(canvasTiles, "image", layerId, group);
|
|
};
|
|
|
|
methods.removeImage = function (layerId) {
|
|
this.layerManager.removeLayer("image", layerId);
|
|
};
|
|
|
|
methods.clearImages = function () {
|
|
this.layerManager.clearLayers("image");
|
|
};
|
|
|
|
methods.addMeasure = function (options) {
|
|
// if a measureControl already exists, then remove it and
|
|
// replace with a new one
|
|
methods.removeMeasure.call(this);
|
|
this.measureControl = _leaflet2["default"].control.measure(options);
|
|
this.addControl(this.measureControl);
|
|
};
|
|
|
|
methods.removeMeasure = function () {
|
|
if (this.measureControl) {
|
|
this.removeControl(this.measureControl);
|
|
this.measureControl = null;
|
|
}
|
|
};
|
|
|
|
methods.addSelect = function (ctGroup) {
|
|
var _this8 = this;
|
|
|
|
methods.removeSelect.call(this);
|
|
this._selectButton = _leaflet2["default"].easyButton({
|
|
states: [{
|
|
stateName: "select-inactive",
|
|
icon: "ion-qr-scanner",
|
|
title: "Make a selection",
|
|
onClick: function onClick(btn, map) {
|
|
btn.state("select-active");
|
|
_this8._locationFilter = new _leaflet2["default"].LocationFilter2();
|
|
|
|
if (ctGroup) {
|
|
var selectionHandle = new global.crosstalk.SelectionHandle(ctGroup);
|
|
selectionHandle.on("change", function (e) {
|
|
if (e.sender !== selectionHandle) {
|
|
if (_this8._locationFilter) {
|
|
_this8._locationFilter.disable();
|
|
|
|
btn.state("select-inactive");
|
|
}
|
|
}
|
|
});
|
|
|
|
var handler = function handler(e) {
|
|
_this8.layerManager.brush(_this8._locationFilter.getBounds(), {
|
|
sender: selectionHandle
|
|
});
|
|
};
|
|
|
|
_this8._locationFilter.on("enabled", handler);
|
|
|
|
_this8._locationFilter.on("change", handler);
|
|
|
|
_this8._locationFilter.on("disabled", function () {
|
|
selectionHandle.close();
|
|
_this8._locationFilter = null;
|
|
});
|
|
}
|
|
|
|
_this8._locationFilter.addTo(map);
|
|
}
|
|
}, {
|
|
stateName: "select-active",
|
|
icon: "ion-close-round",
|
|
title: "Dismiss selection",
|
|
onClick: function onClick(btn, map) {
|
|
btn.state("select-inactive");
|
|
|
|
_this8._locationFilter.disable(); // If explicitly dismissed, clear the crosstalk selections
|
|
|
|
|
|
_this8.layerManager.unbrush();
|
|
}
|
|
}]
|
|
});
|
|
|
|
this._selectButton.addTo(this);
|
|
};
|
|
|
|
methods.removeSelect = function () {
|
|
if (this._locationFilter) {
|
|
this._locationFilter.disable();
|
|
}
|
|
|
|
if (this._selectButton) {
|
|
this.removeControl(this._selectButton);
|
|
this._selectButton = null;
|
|
}
|
|
};
|
|
|
|
methods.createMapPane = function (name, zIndex) {
|
|
this.createPane(name);
|
|
this.getPane(name).style.zIndex = zIndex;
|
|
};
|
|
|
|
|
|
}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
|
},{"./cluster-layer-store":1,"./crs_utils":3,"./dataframe":4,"./global/htmlwidgets":8,"./global/jquery":9,"./global/leaflet":10,"./global/shiny":12,"./mipmapper":16,"./util":17}],16:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
|
|
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
|
|
|
// This class simulates a mipmap, which shrinks images by powers of two. This
|
|
// stepwise reduction results in "pixel-perfect downscaling" (where every
|
|
// pixel of the original image has some contribution to the downscaled image)
|
|
// as opposed to a single-step downscaling which will discard a lot of data
|
|
// (and with sparse images at small scales can give very surprising results).
|
|
var Mipmapper = /*#__PURE__*/function () {
|
|
function Mipmapper(img) {
|
|
_classCallCheck(this, Mipmapper);
|
|
|
|
this._layers = [img];
|
|
} // The various functions on this class take a callback function BUT MAY OR MAY
|
|
// NOT actually behave asynchronously.
|
|
|
|
|
|
_createClass(Mipmapper, [{
|
|
key: "getBySize",
|
|
value: function getBySize(desiredWidth, desiredHeight, callback) {
|
|
var _this = this;
|
|
|
|
var i = 0;
|
|
var lastImg = this._layers[0];
|
|
|
|
var testNext = function testNext() {
|
|
_this.getByIndex(i, function (img) {
|
|
// If current image is invalid (i.e. too small to be rendered) or
|
|
// it's smaller than what we wanted, return the last known good image.
|
|
if (!img || img.width < desiredWidth || img.height < desiredHeight) {
|
|
callback(lastImg);
|
|
return;
|
|
} else {
|
|
lastImg = img;
|
|
i++;
|
|
testNext();
|
|
return;
|
|
}
|
|
});
|
|
};
|
|
|
|
testNext();
|
|
}
|
|
}, {
|
|
key: "getByIndex",
|
|
value: function getByIndex(i, callback) {
|
|
var _this2 = this;
|
|
|
|
if (this._layers[i]) {
|
|
callback(this._layers[i]);
|
|
return;
|
|
}
|
|
|
|
this.getByIndex(i - 1, function (prevImg) {
|
|
if (!prevImg) {
|
|
// prevImg could not be calculated (too small, possibly)
|
|
callback(null);
|
|
return;
|
|
}
|
|
|
|
if (prevImg.width < 2 || prevImg.height < 2) {
|
|
// Can't reduce this image any further
|
|
callback(null);
|
|
return;
|
|
} // If reduce ever becomes truly asynchronous, we should stuff a promise or
|
|
// something into this._layers[i] before calling this.reduce(), to prevent
|
|
// redundant reduce operations from happening.
|
|
|
|
|
|
_this2.reduce(prevImg, function (reducedImg) {
|
|
_this2._layers[i] = reducedImg;
|
|
callback(reducedImg);
|
|
return;
|
|
});
|
|
});
|
|
}
|
|
}, {
|
|
key: "reduce",
|
|
value: function reduce(img, callback) {
|
|
var imgDataCanvas = document.createElement("canvas");
|
|
imgDataCanvas.width = Math.ceil(img.width / 2);
|
|
imgDataCanvas.height = Math.ceil(img.height / 2);
|
|
imgDataCanvas.style.display = "none";
|
|
document.body.appendChild(imgDataCanvas);
|
|
|
|
try {
|
|
var imgDataCtx = imgDataCanvas.getContext("2d");
|
|
imgDataCtx.drawImage(img, 0, 0, img.width / 2, img.height / 2);
|
|
callback(imgDataCanvas);
|
|
} finally {
|
|
document.body.removeChild(imgDataCanvas);
|
|
}
|
|
}
|
|
}]);
|
|
|
|
return Mipmapper;
|
|
}();
|
|
|
|
exports["default"] = Mipmapper;
|
|
|
|
|
|
},{}],17:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.log = log;
|
|
exports.recycle = recycle;
|
|
exports.asArray = asArray;
|
|
|
|
function log(message) {
|
|
/* eslint-disable no-console */
|
|
if (console && console.log) console.log(message);
|
|
/* eslint-enable no-console */
|
|
}
|
|
|
|
function recycle(values, length, inPlace) {
|
|
if (length === 0 && !inPlace) return [];
|
|
|
|
if (!(values instanceof Array)) {
|
|
if (inPlace) {
|
|
throw new Error("Can't do in-place recycling of a non-Array value");
|
|
}
|
|
|
|
values = [values];
|
|
}
|
|
|
|
if (typeof length === "undefined") length = values.length;
|
|
var dest = inPlace ? values : [];
|
|
var origLength = values.length;
|
|
|
|
while (dest.length < length) {
|
|
dest.push(values[dest.length % origLength]);
|
|
}
|
|
|
|
if (dest.length > length) {
|
|
dest.splice(length, dest.length - length);
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
|
|
function asArray(value) {
|
|
if (value instanceof Array) return value;else return [value];
|
|
}
|
|
|
|
|
|
},{}]},{},[13]);
|