function init(){ } var head = document.getElementsByTagName('head')[0]; var link = document.createElement('link'); link.rel = 'stylesheet'; link.type = 'text/css'; link.href = 'https://maps.conrat.de/plugins/node_modules/leaflet/dist/leaflet.css'; link.media = 'all'; head.appendChild(link); /* @preserve * Leaflet 1.9.3, a JS library for interactive maps. https://leafletjs.com * (c) 2010-2022 Vladimir Agafonkin, (c) 2010-2011 CloudMade */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.leaflet = {})); })(this, (function (exports) { 'use strict'; var version = "1.9.3"; /* * @namespace Util * * Various utility functions, used by Leaflet internally. */ // @function extend(dest: Object, src?: Object): Object // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut. function extend(dest) { var i, j, len, src; for (j = 1, len = arguments.length; j < len; j++) { src = arguments[j]; for (i in src) { dest[i] = src[i]; } } return dest; } // @function create(proto: Object, properties?: Object): Object // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create) var create$2 = Object.create || (function () { function F() {} return function (proto) { F.prototype = proto; return new F(); }; })(); // @function bind(fn: Function, …): Function // Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). // Has a `L.bind()` shortcut. function bind(fn, obj) { var slice = Array.prototype.slice; if (fn.bind) { return fn.bind.apply(fn, slice.call(arguments, 1)); } var args = slice.call(arguments, 2); return function () { return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments); }; } // @property lastId: Number // Last unique ID used by [`stamp()`](#util-stamp) var lastId = 0; // @function stamp(obj: Object): Number // Returns the unique ID of an object, assigning it one if it doesn't have it. function stamp(obj) { if (!('_leaflet_id' in obj)) { obj['_leaflet_id'] = ++lastId; } return obj._leaflet_id; } // @function throttle(fn: Function, time: Number, context: Object): Function // Returns a function which executes function `fn` with the given scope `context` // (so that the `this` keyword refers to `context` inside `fn`'s code). The function // `fn` will be called no more than one time per given amount of `time`. The arguments // received by the bound function will be any arguments passed when binding the // function, followed by any arguments passed when invoking the bound function. // Has an `L.throttle` shortcut. function throttle(fn, time, context) { var lock, args, wrapperFn, later; later = function () { // reset lock and call if queued lock = false; if (args) { wrapperFn.apply(context, args); args = false; } }; wrapperFn = function () { if (lock) { // called too soon, queue to call later args = arguments; } else { // call and lock until later fn.apply(context, arguments); setTimeout(later, time); lock = true; } }; return wrapperFn; } // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number // Returns the number `num` modulo `range` in such a way so it lies within // `range[0]` and `range[1]`. The returned value will be always smaller than // `range[1]` unless `includeMax` is set to `true`. function wrapNum(x, range, includeMax) { var max = range[1], min = range[0], d = max - min; return x === max && includeMax ? x : ((x - min) % d + d) % d + min; } // @function falseFn(): Function // Returns a function which always returns `false`. function falseFn() { return false; } // @function formatNum(num: Number, precision?: Number|false): Number // Returns the number `num` rounded with specified `precision`. // The default `precision` value is 6 decimal places. // `false` can be passed to skip any processing (can be useful to avoid round-off errors). function formatNum(num, precision) { if (precision === false) { return num; } var pow = Math.pow(10, precision === undefined ? 6 : precision); return Math.round(num * pow) / pow; } // @function trim(str: String): String // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim) function trim(str) { return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); } // @function splitWords(str: String): String[] // Trims and splits the string on whitespace and returns the array of parts. function splitWords(str) { return trim(str).split(/\s+/); } // @function setOptions(obj: Object, options: Object): Object // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut. function setOptions(obj, options) { if (!Object.prototype.hasOwnProperty.call(obj, 'options')) { obj.options = obj.options ? create$2(obj.options) : {}; } for (var i in options) { obj.options[i] = options[i]; } return obj.options; } // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}` // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will // be appended at the end. If `uppercase` is `true`, the parameter names will // be uppercased (e.g. `'?A=foo&B=bar'`) function getParamString(obj, existingUrl, uppercase) { var params = []; for (var i in obj) { params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i])); } return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); } var templateRe = /\{ *([\w_ -]+) *\}/g; // @function template(str: String, data: Object): String // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'` // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string // `('Hello foo, bar')`. You can also specify functions instead of strings for // data values — they will be evaluated passing `data` as an argument. function template(str, data) { return str.replace(templateRe, function (str, key) { var value = data[key]; if (value === undefined) { throw new Error('No value provided for variable ' + str); } else if (typeof value === 'function') { value = value(data); } return value; }); } // @function isArray(obj): Boolean // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) var isArray = Array.isArray || function (obj) { return (Object.prototype.toString.call(obj) === '[object Array]'); }; // @function indexOf(array: Array, el: Object): Number // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) function indexOf(array, el) { for (var i = 0; i < array.length; i++) { if (array[i] === el) { return i; } } return -1; } // @property emptyImageUrl: String // Data URI string containing a base64-encoded empty GIF image. // Used as a hack to free memory from unused images on WebKit-powered // mobile devices (by setting image `src` to this string). var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; // inspired by https://paulirish.com/2011/requestanimationframe-for-smart-animating/ function getPrefixed(name) { return window['webkit' + name] || window['moz' + name] || window['ms' + name]; } var lastTime = 0; // fallback for IE 7-8 function timeoutDefer(fn) { var time = +new Date(), timeToCall = Math.max(0, 16 - (time - lastTime)); lastTime = time + timeToCall; return window.setTimeout(fn, timeToCall); } var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer; var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') || getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); }; // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number // Schedules `fn` to be executed when the browser repaints. `fn` is bound to // `context` if given. When `immediate` is set, `fn` is called immediately if // the browser doesn't have native support for // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame), // otherwise it's delayed. Returns a request ID that can be used to cancel the request. function requestAnimFrame(fn, context, immediate) { if (immediate && requestFn === timeoutDefer) { fn.call(context); } else { return requestFn.call(window, bind(fn, context)); } } // @function cancelAnimFrame(id: Number): undefined // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame). function cancelAnimFrame(id) { if (id) { cancelFn.call(window, id); } } var Util = { __proto__: null, extend: extend, create: create$2, bind: bind, get lastId () { return lastId; }, stamp: stamp, throttle: throttle, wrapNum: wrapNum, falseFn: falseFn, formatNum: formatNum, trim: trim, splitWords: splitWords, setOptions: setOptions, getParamString: getParamString, template: template, isArray: isArray, indexOf: indexOf, emptyImageUrl: emptyImageUrl, requestFn: requestFn, cancelFn: cancelFn, requestAnimFrame: requestAnimFrame, cancelAnimFrame: cancelAnimFrame }; // @class Class // @aka L.Class // @section // @uninheritable // Thanks to John Resig and Dean Edwards for inspiration! function Class() {} Class.extend = function (props) { // @function extend(props: Object): Function // [Extends the current class](#class-inheritance) given the properties to be included. // Returns a Javascript function that is a class constructor (to be called with `new`). var NewClass = function () { setOptions(this); // call the constructor if (this.initialize) { this.initialize.apply(this, arguments); } // call all constructor hooks this.callInitHooks(); }; var parentProto = NewClass.__super__ = this.prototype; var proto = create$2(parentProto); proto.constructor = NewClass; NewClass.prototype = proto; // inherit parent's statics for (var i in this) { if (Object.prototype.hasOwnProperty.call(this, i) && i !== 'prototype' && i !== '__super__') { NewClass[i] = this[i]; } } // mix static properties into the class if (props.statics) { extend(NewClass, props.statics); } // mix includes into the prototype if (props.includes) { checkDeprecatedMixinEvents(props.includes); extend.apply(null, [proto].concat(props.includes)); } // mix given properties into the prototype extend(proto, props); delete proto.statics; delete proto.includes; // merge options if (proto.options) { proto.options = parentProto.options ? create$2(parentProto.options) : {}; extend(proto.options, props.options); } proto._initHooks = []; // add method for calling all hooks proto.callInitHooks = function () { if (this._initHooksCalled) { return; } if (parentProto.callInitHooks) { parentProto.callInitHooks.call(this); } this._initHooksCalled = true; for (var i = 0, len = proto._initHooks.length; i < len; i++) { proto._initHooks[i].call(this); } }; return NewClass; }; // @function include(properties: Object): this // [Includes a mixin](#class-includes) into the current class. Class.include = function (props) { var parentOptions = this.prototype.options; extend(this.prototype, props); if (props.options) { this.prototype.options = parentOptions; this.mergeOptions(props.options); } return this; }; // @function mergeOptions(options: Object): this // [Merges `options`](#class-options) into the defaults of the class. Class.mergeOptions = function (options) { extend(this.prototype.options, options); return this; }; // @function addInitHook(fn: Function): this // Adds a [constructor hook](#class-constructor-hooks) to the class. Class.addInitHook = function (fn) { // (Function) || (String, args...) var args = Array.prototype.slice.call(arguments, 1); var init = typeof fn === 'function' ? fn : function () { this[fn].apply(this, args); }; this.prototype._initHooks = this.prototype._initHooks || []; this.prototype._initHooks.push(init); return this; }; function checkDeprecatedMixinEvents(includes) { /* global L: true */ if (typeof L === 'undefined' || !L || !L.Mixin) { return; } includes = isArray(includes) ? includes : [includes]; for (var i = 0; i < includes.length; i++) { if (includes[i] === L.Mixin.Events) { console.warn('Deprecated include of L.Mixin.Events: ' + 'this property will be removed in future releases, ' + 'please inherit from L.Evented instead.', new Error().stack); } } } /* * @class Evented * @aka L.Evented * @inherits Class * * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event). * * @example * * ```js * map.on('click', function(e) { * alert(e.latlng); * } ); * ``` * * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function: * * ```js * function onClick(e) { ... } * * map.on('click', onClick); * map.off('click', onClick); * ``` */ var Events = { /* @method on(type: String, fn: Function, context?: Object): this * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`). * * @alternative * @method on(eventMap: Object): this * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` */ on: function (types, fn, context) { // types can be a map of types/handlers if (typeof types === 'object') { for (var type in types) { // we don't process space-separated events here for performance; // it's a hot path since Layer uses the on(obj) syntax this._on(type, types[type], fn); } } else { // types can be a string of space-separated words types = splitWords(types); for (var i = 0, len = types.length; i < len; i++) { this._on(types[i], fn, context); } } return this; }, /* @method off(type: String, fn?: Function, context?: Object): this * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener. * * @alternative * @method off(eventMap: Object): this * Removes a set of type/listener pairs. * * @alternative * @method off: this * Removes all listeners to all events on the object. This includes implicitly attached events. */ off: function (types, fn, context) { if (!arguments.length) { // clear all listeners if called without arguments delete this._events; } else if (typeof types === 'object') { for (var type in types) { this._off(type, types[type], fn); } } else { types = splitWords(types); var removeAll = arguments.length === 1; for (var i = 0, len = types.length; i < len; i++) { if (removeAll) { this._off(types[i]); } else { this._off(types[i], fn, context); } } } return this; }, // attach listener (without syntactic sugar now) _on: function (type, fn, context, _once) { if (typeof fn !== 'function') { console.warn('wrong listener type: ' + typeof fn); return; } // check if fn already there if (this._listens(type, fn, context) !== false) { return; } if (context === this) { // Less memory footprint. context = undefined; } var newListener = {fn: fn, ctx: context}; if (_once) { newListener.once = true; } this._events = this._events || {}; this._events[type] = this._events[type] || []; this._events[type].push(newListener); }, _off: function (type, fn, context) { var listeners, i, len; if (!this._events) { return; } listeners = this._events[type]; if (!listeners) { return; } if (arguments.length === 1) { // remove all if (this._firingCount) { // Set all removed listeners to noop // so they are not called if remove happens in fire for (i = 0, len = listeners.length; i < len; i++) { listeners[i].fn = falseFn; } } // clear all listeners for a type if function isn't specified delete this._events[type]; return; } if (typeof fn !== 'function') { console.warn('wrong listener type: ' + typeof fn); return; } // find fn and remove it var index = this._listens(type, fn, context); if (index !== false) { var listener = listeners[index]; if (this._firingCount) { // set the removed listener to noop so that's not called if remove happens in fire listener.fn = falseFn; /* copy array in case events are being fired */ this._events[type] = listeners = listeners.slice(); } listeners.splice(index, 1); } }, // @method fire(type: String, data?: Object, propagate?: Boolean): this // Fires an event of the specified type. You can optionally provide a data // object — the first argument of the listener function will contain its // properties. The event can optionally be propagated to event parents. fire: function (type, data, propagate) { if (!this.listens(type, propagate)) { return this; } var event = extend({}, data, { type: type, target: this, sourceTarget: data && data.sourceTarget || this }); if (this._events) { var listeners = this._events[type]; if (listeners) { this._firingCount = (this._firingCount + 1) || 1; for (var i = 0, len = listeners.length; i < len; i++) { var l = listeners[i]; // off overwrites l.fn, so we need to copy fn to a var var fn = l.fn; if (l.once) { this.off(type, fn, l.ctx); } fn.call(l.ctx || this, event); } this._firingCount--; } } if (propagate) { // propagate the event to parents (set with addEventParent) this._propagateEvent(event); } return this; }, // @method listens(type: String, propagate?: Boolean): Boolean // @method listens(type: String, fn: Function, context?: Object, propagate?: Boolean): Boolean // Returns `true` if a particular event type has any listeners attached to it. // The verification can optionally be propagated, it will return `true` if parents have the listener attached to it. listens: function (type, fn, context, propagate) { if (typeof type !== 'string') { console.warn('"string" type argument expected'); } // we don't overwrite the input `fn` value, because we need to use it for propagation var _fn = fn; if (typeof fn !== 'function') { propagate = !!fn; _fn = undefined; context = undefined; } var listeners = this._events && this._events[type]; if (listeners && listeners.length) { if (this._listens(type, _fn, context) !== false) { return true; } } if (propagate) { // also check parents for listeners if event propagates for (var id in this._eventParents) { if (this._eventParents[id].listens(type, fn, context, propagate)) { return true; } } } return false; }, // returns the index (number) or false _listens: function (type, fn, context) { if (!this._events) { return false; } var listeners = this._events[type] || []; if (!fn) { return !!listeners.length; } if (context === this) { // Less memory footprint. context = undefined; } for (var i = 0, len = listeners.length; i < len; i++) { if (listeners[i].fn === fn && listeners[i].ctx === context) { return i; } } return false; }, // @method once(…): this // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed. once: function (types, fn, context) { // types can be a map of types/handlers if (typeof types === 'object') { for (var type in types) { // we don't process space-separated events here for performance; // it's a hot path since Layer uses the on(obj) syntax this._on(type, types[type], fn, true); } } else { // types can be a string of space-separated words types = splitWords(types); for (var i = 0, len = types.length; i < len; i++) { this._on(types[i], fn, context, true); } } return this; }, // @method addEventParent(obj: Evented): this // Adds an event parent - an `Evented` that will receive propagated events addEventParent: function (obj) { this._eventParents = this._eventParents || {}; this._eventParents[stamp(obj)] = obj; return this; }, // @method removeEventParent(obj: Evented): this // Removes an event parent, so it will stop receiving propagated events removeEventParent: function (obj) { if (this._eventParents) { delete this._eventParents[stamp(obj)]; } return this; }, _propagateEvent: function (e) { for (var id in this._eventParents) { this._eventParents[id].fire(e.type, extend({ layer: e.target, propagatedFrom: e.target }, e), true); } } }; // aliases; we should ditch those eventually // @method addEventListener(…): this // Alias to [`on(…)`](#evented-on) Events.addEventListener = Events.on; // @method removeEventListener(…): this // Alias to [`off(…)`](#evented-off) // @method clearAllEventListeners(…): this // Alias to [`off()`](#evented-off) Events.removeEventListener = Events.clearAllEventListeners = Events.off; // @method addOneTimeEventListener(…): this // Alias to [`once(…)`](#evented-once) Events.addOneTimeEventListener = Events.once; // @method fireEvent(…): this // Alias to [`fire(…)`](#evented-fire) Events.fireEvent = Events.fire; // @method hasEventListeners(…): Boolean // Alias to [`listens(…)`](#evented-listens) Events.hasEventListeners = Events.listens; var Evented = Class.extend(Events); /* * @class Point * @aka L.Point * * Represents a point with `x` and `y` coordinates in pixels. * * @example * * ```js * var point = L.point(200, 300); * ``` * * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent: * * ```js * map.panBy([200, 300]); * map.panBy(L.point(200, 300)); * ``` * * Note that `Point` does not inherit from Leaflet's `Class` object, * which means new classes can't inherit from it, and new methods * can't be added to it with the `include` function. */ function Point(x, y, round) { // @property x: Number; The `x` coordinate of the point this.x = (round ? Math.round(x) : x); // @property y: Number; The `y` coordinate of the point this.y = (round ? Math.round(y) : y); } var trunc = Math.trunc || function (v) { return v > 0 ? Math.floor(v) : Math.ceil(v); }; Point.prototype = { // @method clone(): Point // Returns a copy of the current point. clone: function () { return new Point(this.x, this.y); }, // @method add(otherPoint: Point): Point // Returns the result of addition of the current and the given points. add: function (point) { // non-destructive, returns a new point return this.clone()._add(toPoint(point)); }, _add: function (point) { // destructive, used directly for performance in situations where it's safe to modify existing point this.x += point.x; this.y += point.y; return this; }, // @method subtract(otherPoint: Point): Point // Returns the result of subtraction of the given point from the current. subtract: function (point) { return this.clone()._subtract(toPoint(point)); }, _subtract: function (point) { this.x -= point.x; this.y -= point.y; return this; }, // @method divideBy(num: Number): Point // Returns the result of division of the current point by the given number. divideBy: function (num) { return this.clone()._divideBy(num); }, _divideBy: function (num) { this.x /= num; this.y /= num; return this; }, // @method multiplyBy(num: Number): Point // Returns the result of multiplication of the current point by the given number. multiplyBy: function (num) { return this.clone()._multiplyBy(num); }, _multiplyBy: function (num) { this.x *= num; this.y *= num; return this; }, // @method scaleBy(scale: Point): Point // Multiply each coordinate of the current point by each coordinate of // `scale`. In linear algebra terms, multiply the point by the // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation) // defined by `scale`. scaleBy: function (point) { return new Point(this.x * point.x, this.y * point.y); }, // @method unscaleBy(scale: Point): Point // Inverse of `scaleBy`. Divide each coordinate of the current point by // each coordinate of `scale`. unscaleBy: function (point) { return new Point(this.x / point.x, this.y / point.y); }, // @method round(): Point // Returns a copy of the current point with rounded coordinates. round: function () { return this.clone()._round(); }, _round: function () { this.x = Math.round(this.x); this.y = Math.round(this.y); return this; }, // @method floor(): Point // Returns a copy of the current point with floored coordinates (rounded down). floor: function () { return this.clone()._floor(); }, _floor: function () { this.x = Math.floor(this.x); this.y = Math.floor(this.y); return this; }, // @method ceil(): Point // Returns a copy of the current point with ceiled coordinates (rounded up). ceil: function () { return this.clone()._ceil(); }, _ceil: function () { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); return this; }, // @method trunc(): Point // Returns a copy of the current point with truncated coordinates (rounded towards zero). trunc: function () { return this.clone()._trunc(); }, _trunc: function () { this.x = trunc(this.x); this.y = trunc(this.y); return this; }, // @method distanceTo(otherPoint: Point): Number // Returns the cartesian distance between the current and the given points. distanceTo: function (point) { point = toPoint(point); var x = point.x - this.x, y = point.y - this.y; return Math.sqrt(x * x + y * y); }, // @method equals(otherPoint: Point): Boolean // Returns `true` if the given point has the same coordinates. equals: function (point) { point = toPoint(point); return point.x === this.x && point.y === this.y; }, // @method contains(otherPoint: Point): Boolean // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values). contains: function (point) { point = toPoint(point); return Math.abs(point.x) <= Math.abs(this.x) && Math.abs(point.y) <= Math.abs(this.y); }, // @method toString(): String // Returns a string representation of the point for debugging purposes. toString: function () { return 'Point(' + formatNum(this.x) + ', ' + formatNum(this.y) + ')'; } }; // @factory L.point(x: Number, y: Number, round?: Boolean) // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values. // @alternative // @factory L.point(coords: Number[]) // Expects an array of the form `[x, y]` instead. // @alternative // @factory L.point(coords: Object) // Expects a plain object of the form `{x: Number, y: Number}` instead. function toPoint(x, y, round) { if (x instanceof Point) { return x; } if (isArray(x)) { return new Point(x[0], x[1]); } if (x === undefined || x === null) { return x; } if (typeof x === 'object' && 'x' in x && 'y' in x) { return new Point(x.x, x.y); } return new Point(x, y, round); } /* * @class Bounds * @aka L.Bounds * * Represents a rectangular area in pixel coordinates. * * @example * * ```js * var p1 = L.point(10, 10), * p2 = L.point(40, 60), * bounds = L.bounds(p1, p2); * ``` * * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: * * ```js * otherBounds.intersects([[10, 10], [40, 60]]); * ``` * * Note that `Bounds` does not inherit from Leaflet's `Class` object, * which means new classes can't inherit from it, and new methods * can't be added to it with the `include` function. */ function Bounds(a, b) { if (!a) { return; } var points = b ? [a, b] : a; for (var i = 0, len = points.length; i < len; i++) { this.extend(points[i]); } } Bounds.prototype = { // @method extend(point: Point): this // Extends the bounds to contain the given point. // @alternative // @method extend(otherBounds: Bounds): this // Extend the bounds to contain the given bounds extend: function (obj) { var min2, max2; if (!obj) { return this; } if (obj instanceof Point || typeof obj[0] === 'number' || 'x' in obj) { min2 = max2 = toPoint(obj); } else { obj = toBounds(obj); min2 = obj.min; max2 = obj.max; if (!min2 || !max2) { return this; } } // @property min: Point // The top left corner of the rectangle. // @property max: Point // The bottom right corner of the rectangle. if (!this.min && !this.max) { this.min = min2.clone(); this.max = max2.clone(); } else { this.min.x = Math.min(min2.x, this.min.x); this.max.x = Math.max(max2.x, this.max.x); this.min.y = Math.min(min2.y, this.min.y); this.max.y = Math.max(max2.y, this.max.y); } return this; }, // @method getCenter(round?: Boolean): Point // Returns the center point of the bounds. getCenter: function (round) { return toPoint( (this.min.x + this.max.x) / 2, (this.min.y + this.max.y) / 2, round); }, // @method getBottomLeft(): Point // Returns the bottom-left point of the bounds. getBottomLeft: function () { return toPoint(this.min.x, this.max.y); }, // @method getTopRight(): Point // Returns the top-right point of the bounds. getTopRight: function () { // -> Point return toPoint(this.max.x, this.min.y); }, // @method getTopLeft(): Point // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)). getTopLeft: function () { return this.min; // left, top }, // @method getBottomRight(): Point // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)). getBottomRight: function () { return this.max; // right, bottom }, // @method getSize(): Point // Returns the size of the given bounds getSize: function () { return this.max.subtract(this.min); }, // @method contains(otherBounds: Bounds): Boolean // Returns `true` if the rectangle contains the given one. // @alternative // @method contains(point: Point): Boolean // Returns `true` if the rectangle contains the given point. contains: function (obj) { var min, max; if (typeof obj[0] === 'number' || obj instanceof Point) { obj = toPoint(obj); } else { obj = toBounds(obj); } if (obj instanceof Bounds) { min = obj.min; max = obj.max; } else { min = max = obj; } return (min.x >= this.min.x) && (max.x <= this.max.x) && (min.y >= this.min.y) && (max.y <= this.max.y); }, // @method intersects(otherBounds: Bounds): Boolean // Returns `true` if the rectangle intersects the given bounds. Two bounds // intersect if they have at least one point in common. intersects: function (bounds) { // (Bounds) -> Boolean bounds = toBounds(bounds); var min = this.min, max = this.max, min2 = bounds.min, max2 = bounds.max, xIntersects = (max2.x >= min.x) && (min2.x <= max.x), yIntersects = (max2.y >= min.y) && (min2.y <= max.y); return xIntersects && yIntersects; }, // @method overlaps(otherBounds: Bounds): Boolean // Returns `true` if the rectangle overlaps the given bounds. Two bounds // overlap if their intersection is an area. overlaps: function (bounds) { // (Bounds) -> Boolean bounds = toBounds(bounds); var min = this.min, max = this.max, min2 = bounds.min, max2 = bounds.max, xOverlaps = (max2.x > min.x) && (min2.x < max.x), yOverlaps = (max2.y > min.y) && (min2.y < max.y); return xOverlaps && yOverlaps; }, // @method isValid(): Boolean // Returns `true` if the bounds are properly initialized. isValid: function () { return !!(this.min && this.max); }, // @method pad(bufferRatio: Number): Bounds // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. // For example, a ratio of 0.5 extends the bounds by 50% in each direction. // Negative values will retract the bounds. pad: function (bufferRatio) { var min = this.min, max = this.max, heightBuffer = Math.abs(min.x - max.x) * bufferRatio, widthBuffer = Math.abs(min.y - max.y) * bufferRatio; return toBounds( toPoint(min.x - heightBuffer, min.y - widthBuffer), toPoint(max.x + heightBuffer, max.y + widthBuffer)); }, // @method equals(otherBounds: Bounds): Boolean // Returns `true` if the rectangle is equivalent to the given bounds. equals: function (bounds) { if (!bounds) { return false; } bounds = toBounds(bounds); return this.min.equals(bounds.getTopLeft()) && this.max.equals(bounds.getBottomRight()); }, }; // @factory L.bounds(corner1: Point, corner2: Point) // Creates a Bounds object from two corners coordinate pairs. // @alternative // @factory L.bounds(points: Point[]) // Creates a Bounds object from the given array of points. function toBounds(a, b) { if (!a || a instanceof Bounds) { return a; } return new Bounds(a, b); } /* * @class LatLngBounds * @aka L.LatLngBounds * * Represents a rectangular geographical area on a map. * * @example * * ```js * var corner1 = L.latLng(40.712, -74.227), * corner2 = L.latLng(40.774, -74.125), * bounds = L.latLngBounds(corner1, corner2); * ``` * * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this: * * ```js * map.fitBounds([ * [40.712, -74.227], * [40.774, -74.125] * ]); * ``` * * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range. * * Note that `LatLngBounds` does not inherit from Leaflet's `Class` object, * which means new classes can't inherit from it, and new methods * can't be added to it with the `include` function. */ function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[]) if (!corner1) { return; } var latlngs = corner2 ? [corner1, corner2] : corner1; for (var i = 0, len = latlngs.length; i < len; i++) { this.extend(latlngs[i]); } } LatLngBounds.prototype = { // @method extend(latlng: LatLng): this // Extend the bounds to contain the given point // @alternative // @method extend(otherBounds: LatLngBounds): this // Extend the bounds to contain the given bounds extend: function (obj) { var sw = this._southWest, ne = this._northEast, sw2, ne2; if (obj instanceof LatLng) { sw2 = obj; ne2 = obj; } else if (obj instanceof LatLngBounds) { sw2 = obj._southWest; ne2 = obj._northEast; if (!sw2 || !ne2) { return this; } } else { return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this; } if (!sw && !ne) { this._southWest = new LatLng(sw2.lat, sw2.lng); this._northEast = new LatLng(ne2.lat, ne2.lng); } else { sw.lat = Math.min(sw2.lat, sw.lat); sw.lng = Math.min(sw2.lng, sw.lng); ne.lat = Math.max(ne2.lat, ne.lat); ne.lng = Math.max(ne2.lng, ne.lng); } return this; }, // @method pad(bufferRatio: Number): LatLngBounds // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. // For example, a ratio of 0.5 extends the bounds by 50% in each direction. // Negative values will retract the bounds. pad: function (bufferRatio) { var sw = this._southWest, ne = this._northEast, heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; return new LatLngBounds( new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); }, // @method getCenter(): LatLng // Returns the center point of the bounds. getCenter: function () { return new LatLng( (this._southWest.lat + this._northEast.lat) / 2, (this._southWest.lng + this._northEast.lng) / 2); }, // @method getSouthWest(): LatLng // Returns the south-west point of the bounds. getSouthWest: function () { return this._southWest; }, // @method getNorthEast(): LatLng // Returns the north-east point of the bounds. getNorthEast: function () { return this._northEast; }, // @method getNorthWest(): LatLng // Returns the north-west point of the bounds. getNorthWest: function () { return new LatLng(this.getNorth(), this.getWest()); }, // @method getSouthEast(): LatLng // Returns the south-east point of the bounds. getSouthEast: function () { return new LatLng(this.getSouth(), this.getEast()); }, // @method getWest(): Number // Returns the west longitude of the bounds getWest: function () { return this._southWest.lng; }, // @method getSouth(): Number // Returns the south latitude of the bounds getSouth: function () { return this._southWest.lat; }, // @method getEast(): Number // Returns the east longitude of the bounds getEast: function () { return this._northEast.lng; }, // @method getNorth(): Number // Returns the north latitude of the bounds getNorth: function () { return this._northEast.lat; }, // @method contains(otherBounds: LatLngBounds): Boolean // Returns `true` if the rectangle contains the given one. // @alternative // @method contains (latlng: LatLng): Boolean // Returns `true` if the rectangle contains the given point. contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) { obj = toLatLng(obj); } else { obj = toLatLngBounds(obj); } var sw = this._southWest, ne = this._northEast, sw2, ne2; if (obj instanceof LatLngBounds) { sw2 = obj.getSouthWest(); ne2 = obj.getNorthEast(); } else { sw2 = ne2 = obj; } return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); }, // @method intersects(otherBounds: LatLngBounds): Boolean // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common. intersects: function (bounds) { bounds = toLatLngBounds(bounds); var sw = this._southWest, ne = this._northEast, sw2 = bounds.getSouthWest(), ne2 = bounds.getNorthEast(), latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); return latIntersects && lngIntersects; }, // @method overlaps(otherBounds: LatLngBounds): Boolean // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area. overlaps: function (bounds) { bounds = toLatLngBounds(bounds); var sw = this._southWest, ne = this._northEast, sw2 = bounds.getSouthWest(), ne2 = bounds.getNorthEast(), latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat), lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng); return latOverlaps && lngOverlaps; }, // @method toBBoxString(): String // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data. toBBoxString: function () { return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(','); }, // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number. equals: function (bounds, maxMargin) { if (!bounds) { return false; } bounds = toLatLngBounds(bounds); return this._southWest.equals(bounds.getSouthWest(), maxMargin) && this._northEast.equals(bounds.getNorthEast(), maxMargin); }, // @method isValid(): Boolean // Returns `true` if the bounds are properly initialized. isValid: function () { return !!(this._southWest && this._northEast); } }; // TODO International date line? // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng) // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle. // @alternative // @factory L.latLngBounds(latlngs: LatLng[]) // Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds). function toLatLngBounds(a, b) { if (a instanceof LatLngBounds) { return a; } return new LatLngBounds(a, b); } /* @class LatLng * @aka L.LatLng * * Represents a geographical point with a certain latitude and longitude. * * @example * * ``` * var latlng = L.latLng(50.5, 30.5); * ``` * * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent: * * ``` * map.panTo([50, 30]); * map.panTo({lon: 30, lat: 50}); * map.panTo({lat: 50, lng: 30}); * map.panTo(L.latLng(50, 30)); * ``` * * Note that `LatLng` does not inherit from Leaflet's `Class` object, * which means new classes can't inherit from it, and new methods * can't be added to it with the `include` function. */ function LatLng(lat, lng, alt) { if (isNaN(lat) || isNaN(lng)) { throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')'); } // @property lat: Number // Latitude in degrees this.lat = +lat; // @property lng: Number // Longitude in degrees this.lng = +lng; // @property alt: Number // Altitude in meters (optional) if (alt !== undefined) { this.alt = +alt; } } LatLng.prototype = { // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number. equals: function (obj, maxMargin) { if (!obj) { return false; } obj = toLatLng(obj); var margin = Math.max( Math.abs(this.lat - obj.lat), Math.abs(this.lng - obj.lng)); return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin); }, // @method toString(): String // Returns a string representation of the point (for debugging purposes). toString: function (precision) { return 'LatLng(' + formatNum(this.lat, precision) + ', ' + formatNum(this.lng, precision) + ')'; }, // @method distanceTo(otherLatLng: LatLng): Number // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines). distanceTo: function (other) { return Earth.distance(this, toLatLng(other)); }, // @method wrap(): LatLng // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees. wrap: function () { return Earth.wrapLatLng(this); }, // @method toBounds(sizeInMeters: Number): LatLngBounds // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`. toBounds: function (sizeInMeters) { var latAccuracy = 180 * sizeInMeters / 40075017, lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat); return toLatLngBounds( [this.lat - latAccuracy, this.lng - lngAccuracy], [this.lat + latAccuracy, this.lng + lngAccuracy]); }, clone: function () { return new LatLng(this.lat, this.lng, this.alt); } }; // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude). // @alternative // @factory L.latLng(coords: Array): LatLng // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead. // @alternative // @factory L.latLng(coords: Object): LatLng // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead. function toLatLng(a, b, c) { if (a instanceof LatLng) { return a; } if (isArray(a) && typeof a[0] !== 'object') { if (a.length === 3) { return new LatLng(a[0], a[1], a[2]); } if (a.length === 2) { return new LatLng(a[0], a[1]); } return null; } if (a === undefined || a === null) { return a; } if (typeof a === 'object' && 'lat' in a) { return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt); } if (b === undefined) { return null; } return new LatLng(a, b, c); } /* * @namespace CRS * @crs L.CRS.Base * Object that defines coordinate reference systems for projecting * geographical points into pixel (screen) coordinates and back (and to * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See * [spatial reference system](https://en.wikipedia.org/wiki/Spatial_reference_system). * * Leaflet defines the most usual CRSs by default. If you want to use a * CRS not defined by default, take a look at the * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin. * * Note that the CRS instances do not inherit from Leaflet's `Class` object, * and can't be instantiated. Also, new classes can't inherit from them, * and methods can't be added to them with the `include` function. */ var CRS = { // @method latLngToPoint(latlng: LatLng, zoom: Number): Point // Projects geographical coordinates into pixel coordinates for a given zoom. latLngToPoint: function (latlng, zoom) { var projectedPoint = this.projection.project(latlng), scale = this.scale(zoom); return this.transformation._transform(projectedPoint, scale); }, // @method pointToLatLng(point: Point, zoom: Number): LatLng // The inverse of `latLngToPoint`. Projects pixel coordinates on a given // zoom into geographical coordinates. pointToLatLng: function (point, zoom) { var scale = this.scale(zoom), untransformedPoint = this.transformation.untransform(point, scale); return this.projection.unproject(untransformedPoint); }, // @method project(latlng: LatLng): Point // Projects geographical coordinates into coordinates in units accepted for // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services). project: function (latlng) { return this.projection.project(latlng); }, // @method unproject(point: Point): LatLng // Given a projected coordinate returns the corresponding LatLng. // The inverse of `project`. unproject: function (point) { return this.projection.unproject(point); }, // @method scale(zoom: Number): Number // Returns the scale used when transforming projected coordinates into // pixel coordinates for a particular zoom. For example, it returns // `256 * 2^zoom` for Mercator-based CRS. scale: function (zoom) { return 256 * Math.pow(2, zoom); }, // @method zoom(scale: Number): Number // Inverse of `scale()`, returns the zoom level corresponding to a scale // factor of `scale`. zoom: function (scale) { return Math.log(scale / 256) / Math.LN2; }, // @method getProjectedBounds(zoom: Number): Bounds // Returns the projection's bounds scaled and transformed for the provided `zoom`. getProjectedBounds: function (zoom) { if (this.infinite) { return null; } var b = this.projection.bounds, s = this.scale(zoom), min = this.transformation.transform(b.min, s), max = this.transformation.transform(b.max, s); return new Bounds(min, max); }, // @method distance(latlng1: LatLng, latlng2: LatLng): Number // Returns the distance between two geographical coordinates. // @property code: String // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`) // // @property wrapLng: Number[] // An array of two numbers defining whether the longitude (horizontal) coordinate // axis wraps around a given range and how. Defaults to `[-180, 180]` in most // geographical CRSs. If `undefined`, the longitude axis does not wrap around. // // @property wrapLat: Number[] // Like `wrapLng`, but for the latitude (vertical) axis. // wrapLng: [min, max], // wrapLat: [min, max], // @property infinite: Boolean // If true, the coordinate space will be unbounded (infinite in both axes) infinite: false, // @method wrapLatLng(latlng: LatLng): LatLng // Returns a `LatLng` where lat and lng has been wrapped according to the // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds. wrapLatLng: function (latlng) { var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng, lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat, alt = latlng.alt; return new LatLng(lat, lng, alt); }, // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds // Returns a `LatLngBounds` with the same size as the given one, ensuring // that its center is within the CRS's bounds. // Only accepts actual `L.LatLngBounds` instances, not arrays. wrapLatLngBounds: function (bounds) { var center = bounds.getCenter(), newCenter = this.wrapLatLng(center), latShift = center.lat - newCenter.lat, lngShift = center.lng - newCenter.lng; if (latShift === 0 && lngShift === 0) { return bounds; } var sw = bounds.getSouthWest(), ne = bounds.getNorthEast(), newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift), newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift); return new LatLngBounds(newSw, newNe); } }; /* * @namespace CRS * @crs L.CRS.Earth * * Serves as the base for CRS that are global such that they cover the earth. * Can only be used as the base for other CRS and cannot be used directly, * since it does not have a `code`, `projection` or `transformation`. `distance()` returns * meters. */ var Earth = extend({}, CRS, { wrapLng: [-180, 180], // Mean Earth Radius, as recommended for use by // the International Union of Geodesy and Geophysics, // see https://rosettacode.org/wiki/Haversine_formula R: 6371000, // distance between two geographical points using spherical law of cosines approximation distance: function (latlng1, latlng2) { var rad = Math.PI / 180, lat1 = latlng1.lat * rad, lat2 = latlng2.lat * rad, sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2), sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2), a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon, c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return this.R * c; } }); /* * @namespace Projection * @projection L.Projection.SphericalMercator * * Spherical Mercator projection — the most common projection for online maps, * used by almost all free and commercial tile providers. Assumes that Earth is * a sphere. Used by the `EPSG:3857` CRS. */ var earthRadius = 6378137; var SphericalMercator = { R: earthRadius, MAX_LATITUDE: 85.0511287798, project: function (latlng) { var d = Math.PI / 180, max = this.MAX_LATITUDE, lat = Math.max(Math.min(max, latlng.lat), -max), sin = Math.sin(lat * d); return new Point( this.R * latlng.lng * d, this.R * Math.log((1 + sin) / (1 - sin)) / 2); }, unproject: function (point) { var d = 180 / Math.PI; return new LatLng( (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d, point.x * d / this.R); }, bounds: (function () { var d = earthRadius * Math.PI; return new Bounds([-d, -d], [d, d]); })() }; /* * @class Transformation * @aka L.Transformation * * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d` * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing * the reverse. Used by Leaflet in its projections code. * * @example * * ```js * var transformation = L.transformation(2, 5, -1, 10), * p = L.point(1, 2), * p2 = transformation.transform(p), // L.point(7, 8) * p3 = transformation.untransform(p2); // L.point(1, 2) * ``` */ // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number) // Creates a `Transformation` object with the given coefficients. function Transformation(a, b, c, d) { if (isArray(a)) { // use array properties this._a = a[0]; this._b = a[1]; this._c = a[2]; this._d = a[3]; return; } this._a = a; this._b = b; this._c = c; this._d = d; } Transformation.prototype = { // @method transform(point: Point, scale?: Number): Point // Returns a transformed point, optionally multiplied by the given scale. // Only accepts actual `L.Point` instances, not arrays. transform: function (point, scale) { // (Point, Number) -> Point return this._transform(point.clone(), scale); }, // destructive transform (faster) _transform: function (point, scale) { scale = scale || 1; point.x = scale * (this._a * point.x + this._b); point.y = scale * (this._c * point.y + this._d); return point; }, // @method untransform(point: Point, scale?: Number): Point // Returns the reverse transformation of the given point, optionally divided // by the given scale. Only accepts actual `L.Point` instances, not arrays. untransform: function (point, scale) { scale = scale || 1; return new Point( (point.x / scale - this._b) / this._a, (point.y / scale - this._d) / this._c); } }; // factory L.transformation(a: Number, b: Number, c: Number, d: Number) // @factory L.transformation(a: Number, b: Number, c: Number, d: Number) // Instantiates a Transformation object with the given coefficients. // @alternative // @factory L.transformation(coefficients: Array): Transformation // Expects an coefficients array of the form // `[a: Number, b: Number, c: Number, d: Number]`. function toTransformation(a, b, c, d) { return new Transformation(a, b, c, d); } /* * @namespace CRS * @crs L.CRS.EPSG3857 * * The most common CRS for online maps, used by almost all free and commercial * tile providers. Uses Spherical Mercator projection. Set in by default in * Map's `crs` option. */ var EPSG3857 = extend({}, Earth, { code: 'EPSG:3857', projection: SphericalMercator, transformation: (function () { var scale = 0.5 / (Math.PI * SphericalMercator.R); return toTransformation(scale, 0.5, -scale, 0.5); }()) }); var EPSG900913 = extend({}, EPSG3857, { code: 'EPSG:900913' }); // @namespace SVG; @section // There are several static functions which can be called without instantiating L.SVG: // @function create(name: String): SVGElement // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement), // corresponding to the class name passed. For example, using 'line' will return // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement). function svgCreate(name) { return document.createElementNS('http://www.w3.org/2000/svg', name); } // @function pointsToPath(rings: Point[], closed: Boolean): String // Generates a SVG path string for multiple rings, with each ring turning // into "M..L..L.." instructions function pointsToPath(rings, closed) { var str = '', i, j, len, len2, points, p; for (i = 0, len = rings.length; i < len; i++) { points = rings[i]; for (j = 0, len2 = points.length; j < len2; j++) { p = points[j]; str += (j ? 'L' : 'M') + p.x + ' ' + p.y; } // closes the ring for polygons; "x" is VML syntax str += closed ? (Browser.svg ? 'z' : 'x') : ''; } // SVG complains about empty path strings return str || 'M0 0'; } /* * @namespace Browser * @aka L.Browser * * A namespace with static properties for browser/feature detection used by Leaflet internally. * * @example * * ```js * if (L.Browser.ielt9) { * alert('Upgrade your browser, dude!'); * } * ``` */ var style = document.documentElement.style; // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge). var ie = 'ActiveXObject' in window; // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9. var ielt9 = ie && !document.addEventListener; // @property edge: Boolean; `true` for the Edge web browser. var edge = 'msLaunchUri' in navigator && !('documentMode' in document); // @property webkit: Boolean; // `true` for webkit-based browsers like Chrome and Safari (including mobile versions). var webkit = userAgentContains('webkit'); // @property android: Boolean // **Deprecated.** `true` for any browser running on an Android platform. var android = userAgentContains('android'); // @property android23: Boolean; **Deprecated.** `true` for browsers running on Android 2 or Android 3. var android23 = userAgentContains('android 2') || userAgentContains('android 3'); /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */ var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit // @property androidStock: Boolean; **Deprecated.** `true` for the Android stock browser (i.e. not Chrome) var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window); // @property opera: Boolean; `true` for the Opera browser var opera = !!window.opera; // @property chrome: Boolean; `true` for the Chrome browser. var chrome = !edge && userAgentContains('chrome'); // @property gecko: Boolean; `true` for gecko-based browsers like Firefox. var gecko = userAgentContains('gecko') && !webkit && !opera && !ie; // @property safari: Boolean; `true` for the Safari browser. var safari = !chrome && userAgentContains('safari'); var phantom = userAgentContains('phantom'); // @property opera12: Boolean // `true` for the Opera browser supporting CSS transforms (version 12 or later). var opera12 = 'OTransition' in style; // @property win: Boolean; `true` when the browser is running in a Windows platform var win = navigator.platform.indexOf('Win') === 0; // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms. var ie3d = ie && ('transition' in style); // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms. var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23; // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms. var gecko3d = 'MozPerspective' in style; // @property any3d: Boolean // `true` for all browsers supporting CSS transforms. var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom; // @property mobile: Boolean; `true` for all browsers running in a mobile device. var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile'); // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device. var mobileWebkit = mobile && webkit; // @property mobileWebkit3d: Boolean // `true` for all webkit-based browsers in a mobile device supporting CSS transforms. var mobileWebkit3d = mobile && webkit3d; // @property msPointer: Boolean // `true` for browsers implementing the Microsoft touch events model (notably IE10). var msPointer = !window.PointerEvent && window.MSPointerEvent; // @property pointer: Boolean // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). var pointer = !!(window.PointerEvent || msPointer); // @property touchNative: Boolean // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events). // **This does not necessarily mean** that the browser is running in a computer with // a touchscreen, it only means that the browser is capable of understanding // touch events. var touchNative = 'ontouchstart' in window || !!window.TouchEvent; // @property touch: Boolean // `true` for all browsers supporting either [touch](#browser-touch) or [pointer](#browser-pointer) events. // Note: pointer events will be preferred (if available), and processed for all `touch*` listeners. var touch = !window.L_NO_TOUCH && (touchNative || pointer); // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device. var mobileOpera = mobile && opera; // @property mobileGecko: Boolean // `true` for gecko-based browsers running in a mobile device. var mobileGecko = mobile && gecko; // @property retina: Boolean // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%. var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1; // @property passiveEvents: Boolean // `true` for browsers that support passive events. var passiveEvents = (function () { var supportsPassiveOption = false; try { var opts = Object.defineProperty({}, 'passive', { get: function () { // eslint-disable-line getter-return supportsPassiveOption = true; } }); window.addEventListener('testPassiveEventSupport', falseFn, opts); window.removeEventListener('testPassiveEventSupport', falseFn, opts); } catch (e) { // Errors can safely be ignored since this is only a browser support test. } return supportsPassiveOption; }()); // @property canvas: Boolean // `true` when the browser supports [``](https://developer.mozilla.org/docs/Web/API/Canvas_API). var canvas$1 = (function () { return !!document.createElement('canvas').getContext; }()); // @property svg: Boolean // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG). var svg$1 = !!(document.createElementNS && svgCreate('svg').createSVGRect); var inlineSvg = !!svg$1 && (function () { var div = document.createElement('div'); div.innerHTML = ''; return (div.firstChild && div.firstChild.namespaceURI) === 'http://www.w3.org/2000/svg'; })(); // @property vml: Boolean // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language). var vml = !svg$1 && (function () { try { var div = document.createElement('div'); div.innerHTML = ''; var shape = div.firstChild; shape.style.behavior = 'url(#default#VML)'; return shape && (typeof shape.adj === 'object'); } catch (e) { return false; } }()); // @property mac: Boolean; `true` when the browser is running in a Mac platform var mac = navigator.platform.indexOf('Mac') === 0; // @property mac: Boolean; `true` when the browser is running in a Linux platform var linux = navigator.platform.indexOf('Linux') === 0; function userAgentContains(str) { return navigator.userAgent.toLowerCase().indexOf(str) >= 0; } var Browser = { ie: ie, ielt9: ielt9, edge: edge, webkit: webkit, android: android, android23: android23, androidStock: androidStock, opera: opera, chrome: chrome, gecko: gecko, safari: safari, phantom: phantom, opera12: opera12, win: win, ie3d: ie3d, webkit3d: webkit3d, gecko3d: gecko3d, any3d: any3d, mobile: mobile, mobileWebkit: mobileWebkit, mobileWebkit3d: mobileWebkit3d, msPointer: msPointer, pointer: pointer, touch: touch, touchNative: touchNative, mobileOpera: mobileOpera, mobileGecko: mobileGecko, retina: retina, passiveEvents: passiveEvents, canvas: canvas$1, svg: svg$1, vml: vml, inlineSvg: inlineSvg, mac: mac, linux: linux }; /* * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. */ var POINTER_DOWN = Browser.msPointer ? 'MSPointerDown' : 'pointerdown'; var POINTER_MOVE = Browser.msPointer ? 'MSPointerMove' : 'pointermove'; var POINTER_UP = Browser.msPointer ? 'MSPointerUp' : 'pointerup'; var POINTER_CANCEL = Browser.msPointer ? 'MSPointerCancel' : 'pointercancel'; var pEvent = { touchstart : POINTER_DOWN, touchmove : POINTER_MOVE, touchend : POINTER_UP, touchcancel : POINTER_CANCEL }; var handle = { touchstart : _onPointerStart, touchmove : _handlePointer, touchend : _handlePointer, touchcancel : _handlePointer }; var _pointers = {}; var _pointerDocListener = false; // Provides a touch events wrapper for (ms)pointer events. // ref https://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 function addPointerListener(obj, type, handler) { if (type === 'touchstart') { _addPointerDocListener(); } if (!handle[type]) { console.warn('wrong event specified:', type); return falseFn; } handler = handle[type].bind(this, handler); obj.addEventListener(pEvent[type], handler, false); return handler; } function removePointerListener(obj, type, handler) { if (!pEvent[type]) { console.warn('wrong event specified:', type); return; } obj.removeEventListener(pEvent[type], handler, false); } function _globalPointerDown(e) { _pointers[e.pointerId] = e; } function _globalPointerMove(e) { if (_pointers[e.pointerId]) { _pointers[e.pointerId] = e; } } function _globalPointerUp(e) { delete _pointers[e.pointerId]; } function _addPointerDocListener() { // need to keep track of what pointers and how many are active to provide e.touches emulation if (!_pointerDocListener) { // we listen document as any drags that end by moving the touch off the screen get fired there document.addEventListener(POINTER_DOWN, _globalPointerDown, true); document.addEventListener(POINTER_MOVE, _globalPointerMove, true); document.addEventListener(POINTER_UP, _globalPointerUp, true); document.addEventListener(POINTER_CANCEL, _globalPointerUp, true); _pointerDocListener = true; } } function _handlePointer(handler, e) { if (e.pointerType === (e.MSPOINTER_TYPE_MOUSE || 'mouse')) { return; } e.touches = []; for (var i in _pointers) { e.touches.push(_pointers[i]); } e.changedTouches = [e]; handler(e); } function _onPointerStart(handler, e) { // IE10 specific: MsTouch needs preventDefault. See #2000 if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) { preventDefault(e); } _handlePointer(handler, e); } /* * Extends the event handling code with double tap support for mobile browsers. * * Note: currently most browsers fire native dblclick, with only a few exceptions * (see https://github.com/Leaflet/Leaflet/issues/7012#issuecomment-595087386) */ function makeDblclick(event) { // in modern browsers `type` cannot be just overridden: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only var newEvent = {}, prop, i; for (i in event) { prop = event[i]; newEvent[i] = prop && prop.bind ? prop.bind(event) : prop; } event = newEvent; newEvent.type = 'dblclick'; newEvent.detail = 2; newEvent.isTrusted = false; newEvent._simulated = true; // for debug purposes return newEvent; } var delay = 200; function addDoubleTapListener(obj, handler) { // Most browsers handle double tap natively obj.addEventListener('dblclick', handler); // On some platforms the browser doesn't fire native dblclicks for touch events. // It seems that in all such cases `detail` property of `click` event is always `1`. // So here we rely on that fact to avoid excessive 'dblclick' simulation when not needed. var last = 0, detail; function simDblclick(e) { if (e.detail !== 1) { detail = e.detail; // keep in sync to avoid false dblclick in some cases return; } if (e.pointerType === 'mouse' || (e.sourceCapabilities && !e.sourceCapabilities.firesTouchEvents)) { return; } // When clicking on an , the browser generates a click on its //