1/* @preserve 2 * Leaflet 1.4.0+Detached: 3337f36d2a2d2b33946779057619b31f674ff5dc.3337f36, a JS library for interactive maps. http://leafletjs.com 3 * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade 4 */ 5 6window.dispatchEvent(new Event('resize')); 7 8(function (global, factory) { 9 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 10 typeof define === 'function' && define.amd ? define(['exports'], factory) : 11 (factory((global.L = {}))); 12}(this, (function (exports) { 'use strict'; 13 14var version = "1.4.0+HEAD.3337f36"; 15 16/* 17 * @namespace Util 18 * 19 * Various utility functions, used by Leaflet internally. 20 */ 21 22var freeze = Object.freeze; 23Object.freeze = function (obj) { return obj; }; 24 25// @function extend(dest: Object, src?: Object): Object 26// Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut. 27function extend(dest) { 28 var i, j, len, src; 29 30 for (j = 1, len = arguments.length; j < len; j++) { 31 src = arguments[j]; 32 for (i in src) { 33 dest[i] = src[i];
vendor: 7,240 bytes, lines 34-235
34 } 35 } 36 return dest; 37} 38 39// @function create(proto: Object, properties?: Object): Object 40// Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create) 41var create = Object.create || (function () { 42 function F() {} 43 return function (proto) { 44 F.prototype = proto; 45 return new F(); 46 }; 47})(); 48 49// @function bind(fn: Function, â¦): Function 50// 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). 51// Has a `L.bind()` shortcut. 52function bind(fn, obj) { 53 var slice = Array.prototype.slice; 54 55 if (fn.bind) { 56 return fn.bind.apply(fn, slice.call(arguments, 1)); 57 } 58 59 var args = slice.call(arguments, 2); 60 61 return function () { 62 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments); 63 }; 64} 65 66// @property lastId: Number 67// Last unique ID used by [`stamp()`](#util-stamp) 68var lastId = 0; 69 70// @function stamp(obj: Object): Number 71// Returns the unique ID of an object, assigning it one if it doesn't have it. 72function stamp(obj) { 73 /*eslint-disable */ 74 obj._leaflet_id = obj._leaflet_id || ++lastId; 75 return obj._leaflet_id; 76 /* eslint-enable */ 77} 78 79// @function throttle(fn: Function, time: Number, context: Object): Function 80// Returns a function which executes function `fn` with the given scope `context` 81// (so that the `this` keyword refers to `context` inside `fn`'s code). The function 82// `fn` will be called no more than one time per given amount of `time`. The arguments 83// received by the bound function will be any arguments passed when binding the 84// function, followed by any arguments passed when invoking the bound function. 85// Has an `L.throttle` shortcut. 86function throttle(fn, time, context) { 87 var lock, args, wrapperFn, later; 88 89 later = function () { 90 // reset lock and call if queued 91 lock = false; 92 if (args) { 93 wrapperFn.apply(context, args); 94 args = false; 95 } 96 }; 97 98 wrapperFn = function () { 99 if (lock) { 100 // called too soon, queue to call later 101 args = arguments; 102 103 } else { 104 // call and lock until later 105 fn.apply(context, arguments); 106 setTimeout(later, time); 107 lock = true; 108 } 109 }; 110 111 return wrapperFn; 112} 113 114// @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number 115// Returns the number `num` modulo `range` in such a way so it lies within 116// `range[0]` and `range[1]`. The returned value will be always smaller than 117// `range[1]` unless `includeMax` is set to `true`. 118function wrapNum(x, range, includeMax) { 119 var max = range[1], 120 min = range[0], 121 d = max - min; 122 return x === max && includeMax ? x : ((x - min) % d + d) % d + min; 123} 124 125// @function falseFn(): Function 126// Returns a function which always returns `false`. 127function falseFn() { return false; } 128 129// @function formatNum(num: Number, digits?: Number): Number 130// Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default. 131function formatNum(num, digits) { 132 var pow = Math.pow(10, (digits === undefined ? 6 : digits)); 133 return Math.round(num * pow) / pow; 134} 135 136// @function trim(str: String): String 137// Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim) 138function trim(str) { 139 return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); 140} 141 142// @function splitWords(str: String): String[] 143// Trims and splits the string on whitespace and returns the array of parts. 144function splitWords(str) { 145 return trim(str).split(/\s+/); 146} 147 148// @function setOptions(obj: Object, options: Object): Object 149// Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut. 150function setOptions(obj, options) { 151 if (!obj.hasOwnProperty('options')) { 152 obj.options = obj.options ? create(obj.options) : {}; 153 } 154 for (var i in options) { 155 obj.options[i] = options[i]; 156 } 157 return obj.options; 158} 159 160// @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String 161// Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}` 162// translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will 163// be appended at the end. If `uppercase` is `true`, the parameter names will 164// be uppercased (e.g. `'?A=foo&B=bar'`) 165function getParamString(obj, existingUrl, uppercase) { 166 var params = []; 167 for (var i in obj) { 168 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i])); 169 } 170 return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); 171} 172 173var templateRe = /\{ *([\w_-]+) *\}/g; 174 175// @function template(str: String, data: Object): String 176// Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'` 177// and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string 178// `('Hello foo, bar')`. You can also specify functions instead of strings for 179// data values â they will be evaluated passing `data` as an argument. 180function template(str, data) { 181 return str.replace(templateRe, function (str, key) { 182 var value = data[key]; 183 184 if (value === undefined) { 185 throw new Error('No value provided for variable ' + str); 186 187 } else if (typeof value === 'function') { 188 value = value(data); 189 } 190 return value; 191 }); 192} 193 194// @function isArray(obj): Boolean 195// Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) 196var isArray = Array.isArray || function (obj) { 197 return (Object.prototype.toString.call(obj) === '[object Array]'); 198}; 199 200// @function indexOf(array: Array, el: Object): Number 201// Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) 202function indexOf(array, el) { 203 for (var i = 0; i < array.length; i++) { 204 if (array[i] === el) { return i; } 205 } 206 return -1; 207} 208 209// @property emptyImageUrl: String 210// Data URI string containing a base64-encoded empty GIF image. 211// Used as a hack to free memory from unused images on WebKit-powered 212// mobile devices (by setting image `src` to this string). 213var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; 214 215// inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 216 217function getPrefixed(name) { 218 return window['webkit' + name] || window['moz' + name] || window['ms' + name]; 219} 220 221var lastTime = 0; 222 223// fallback for IE 7-8 224function timeoutDefer(fn) { 225 var time = +new Date(), 226 timeToCall = Math.max(0, 16 - (time - lastTime)); 227 228 lastTime = time + timeToCall; 229 return window.setTimeout(fn, timeToCall); 230} 231 232var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer; 233var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') || 234 getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); }; 235
236// @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number 237// Schedules `fn` to be executed when the browser repaints. `fn` is bound to 238// `context` if given. When `immediate` is set, `fn` is called immediately if 239// the browser doesn't have native support for 240// [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame), 241// otherwise it's delayed. Returns a request ID that can be used to cancel the request. 242function requestAnimFrame(fn, context, immediate) { 243 if (immediate && requestFn === timeoutDefer) { 244 fn.call(context); 245 } else { 246 return requestFn.call(window, bind(fn, context)); 247 } 248} 249 250// @function cancelAnimFrame(id: Number): undefined 251// Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame). 252function cancelAnimFrame(id) { 253 if (id) { 254 cancelFn.call(window, id); 255 } 256} 257 258 259var Util = (Object.freeze || Object)({ 260 freeze: freeze, 261 extend: extend, 262 create: create, 263 bind: bind, 264 lastId: lastId, 265 stamp: stamp, 266 throttle: throttle, 267 wrapNum: wrapNum, 268 falseFn: falseFn, 269 formatNum: formatNum, 270 trim: trim, 271 splitWords: splitWords, 272 setOptions: setOptions, 273 getParamString: getParamString, 274 template: template, 275 isArray: isArray, 276 indexOf: indexOf, 277 emptyImageUrl: emptyImageUrl, 278 requestFn: requestFn, 279 cancelFn: cancelFn, 280 requestAnimFrame: requestAnimFrame, 281 cancelAnimFrame: cancelAnimFrame 282}); 283 284// @class Class 285// @aka L.Class 286 287// @section 288// @uninheritable 289 290// Thanks to John Resig and Dean Edwards for inspiration! 291 292function Class() {} 293 294Class.extend = function (props) { 295 296 // @function extend(props: Object): Function 297 // [Extends the current class](#class-inheritance) given the properties to be included. 298 // Returns a Javascript function that is a class constructor (to be called with `new`). 299 var NewClass = function () { 300 301 // call the constructor 302 if (this.initialize) { 303 this.initialize.apply(this, arguments); 304 } 305 306 // call all constructor hooks 307 this.callInitHooks(); 308 }; 309 310 var parentProto = NewClass.__super__ = this.prototype; 311 312 var proto = create(parentProto); 313 proto.constructor = NewClass; 314 315 NewClass.prototype = proto; 316 317 // inherit parent's statics 318 for (var i in this) { 319 if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') { 320 NewClass[i] = this[i]; 321 } 322 } 323 324 // mix static properties into the class 325 if (props.statics) { 326 extend(NewClass, props.statics); 327 delete props.statics; 328 } 329 330 // mix includes into the prototype 331 if (props.includes) { 332 checkDeprecatedMixinEvents(props.includes); 333 extend.apply(null, [proto].concat(props.includes)); 334 delete props.includes; 335 } 336 337 // merge options 338 if (proto.options) { 339 props.options = extend(create(proto.options), props.options); 340 } 341 342 // mix given properties into the prototype 343 extend(proto, props); 344 345 proto._initHooks = []; 346 347 // add method for calling all hooks 348 proto.callInitHooks = function () { 349 350 if (this._initHooksCalled) { return; } 351 352 if (parentProto.callInitHooks) { 353 parentProto.callInitHooks.call(this); 354 } 355 356 this._initHooksCalled = true; 357 358 for (var i = 0, len = proto._initHooks.length; i < len; i++) { 359 proto._initHooks[i].call(this); 360 } 361 }; 362 363 return NewClass; 364}; 365 366 367// @function include(properties: Object): this 368// [Includes a mixin](#class-includes) into the current class. 369Class.include = function (props) { 370 extend(this.prototype, props); 371 return this; 372}; 373 374// @function mergeOptions(options: Object): this 375// [Merges `options`](#class-options) into the defaults of the class. 376Class.mergeOptions = function (options) { 377 extend(this.prototype.options, options); 378 return this; 379}; 380 381// @function addInitHook(fn: Function): this 382// Adds a [constructor hook](#class-constructor-hooks) to the class. 383Class.addInitHook = function (fn) { // (Function) || (String, args...) 384 var args = Array.prototype.slice.call(arguments, 1); 385 386 var init = typeof fn === 'function' ? fn : function () { 387 this[fn].apply(this, args); 388 }; 389 390 this.prototype._initHooks = this.prototype._initHooks || []; 391 this.prototype._initHooks.push(init); 392 return this; 393};
394 395function checkDeprecatedMixinEvents(includes) { 396 if (typeof L === 'undefined' || !L || !L.Mixin) { return; } 397 398 includes = isArray(includes) ? includes : [includes]; 399 400 for (var i = 0; i < includes.length; i++) { 401 if (includes[i] === L.Mixin.Events) { 402 console.warn('Deprecated include of L.Mixin.Events: ' + 403 'this property will be removed in future releases, ' + 404 'please inherit from L.Evented instead.', new Error().stack); 405 } 406 } 407} 408 409/* 410 * @class Evented 411 * @aka L.Evented 412 * @inherits Class 413 * 414 * 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). 415 * 416 * @example 417 * 418 * ```js 419 * map.on('click', function(e) { 420 * alert(e.latlng); 421 * } ); 422 * ``` 423 * 424 * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function: 425 * 426 * ```js 427 * function onClick(e) { ... } 428 * 429 * map.on('click', onClick); 430 * map.off('click', onClick); 431 * ``` 432 */ 433 434var Events = { 435 /* @method on(type: String, fn: Function, context?: Object): this 436 * 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'`). 437 * 438 * @alternative 439 * @method on(eventMap: Object): this 440 * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` 441 */ 442 on: function (types, fn, context) { 443 444 // types can be a map of types/handlers 445 if (typeof types === 'object') { 446 for (var type in types) { 447 // we don't process space-separated events here for performance; 448 // it's a hot path since Layer uses the on(obj) syntax 449 this._on(type, types[type], fn); 450 } 451 452 } else { 453 // types can be a string of space-separated words 454 types = splitWords(types); 455 456 for (var i = 0, len = types.length; i < len; i++) { 457 this._on(types[i], fn, context); 458 } 459 } 460 461 return this; 462 }, 463 464 /* @method off(type: String, fn?: Function, context?: Object): this 465 * 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. 466 * 467 * @alternative 468 * @method off(eventMap: Object): this 469 * Removes a set of type/listener pairs. 470 * 471 * @alternative 472 * @method off: this 473 * Removes all listeners to all events on the object. 474 */ 475 off: function (types, fn, context) { 476 477 if (!types) { 478 // clear all listeners if called without arguments 479 delete this._events; 480 481 } else if (typeof types === 'object') { 482 for (var type in types) { 483 this._off(type, types[type], fn); 484 } 485 486 } else { 487 types = splitWords(types); 488 489 for (var i = 0, len = types.length; i < len; i++) { 490 this._off(types[i], fn, context); 491 } 492 } 493 494 return this; 495 }, 496 497 // attach listener (without syntactic sugar now) 498 _on: function (type, fn, context) { 499 this._events = this._events || {}; 500 501 /* get/init listeners for type */ 502 var typeListeners = this._events[type]; 503 if (!typeListeners) { 504 typeListeners = []; 505 this._events[type] = typeListeners; 506 } 507 508 if (context === this) { 509 // Less memory footprint.
510 context = undefined; 511 } 512 var newListener = {fn: fn, ctx: context}, 513 listeners = typeListeners; 514 515 // check if fn already there 516 for (var i = 0, len = listeners.length; i < len; i++) { 517 if (listeners[i].fn === fn && listeners[i].ctx === context) { 518 return; 519 } 520 } 521 522 listeners.push(newListener); 523 }, 524 525 _off: function (type, fn, context) { 526 var listeners, 527 i, 528 len; 529 530 if (!this._events) { return; } 531 532 listeners = this._events[type]; 533 534 if (!listeners) { 535 return; 536 } 537 538 if (!fn) { 539 // Set all removed listeners to noop so they are not called if remove happens in fire 540 for (i = 0, len = listeners.length; i < len; i++) { 541 listeners[i].fn = falseFn; 542 } 543 // clear all listeners for a type if function isn't specified 544 delete this._events[type]; 545 return; 546 } 547 548 if (context === this) { 549 context = undefined; 550 } 551 552 if (listeners) { 553 554 // find fn and remove it 555 for (i = 0, len = listeners.length; i < len; i++) { 556 var l = listeners[i]; 557 if (l.ctx !== context) { continue; } 558 if (l.fn === fn) { 559 560 // set the removed listener to noop so that's not called if remove happens in fire 561 l.fn = falseFn; 562 563 if (this._firingCount) { 564 /* copy array in case events are being fired */ 565 this._events[type] = listeners = listeners.slice(); 566 } 567 listeners.splice(i, 1); 568 569 return; 570 } 571 } 572 } 573 }, 574 575 // @method fire(type: String, data?: Object, propagate?: Boolean): this 576 // Fires an event of the specified type. You can optionally provide an data 577 // object â the first argument of the listener function will contain its 578 // properties. The event can optionally be propagated to event parents. 579 fire: function (type, data, propagate) { 580 if (!this.listens(type, propagate)) { return this; } 581 582 var event = extend({}, data, { 583 type: type, 584 target: this, 585 sourceTarget: data && data.sourceTarget || this 586 }); 587 588 if (this._events) { 589 var listeners = this._events[type]; 590 591 if (listeners) { 592 this._firingCount = (this._firingCount + 1) || 1; 593 for (var i = 0, len = listeners.length; i < len; i++) { 594 var l = listeners[i]; 595 l.fn.call(l.ctx || this, event); 596 } 597 598 this._firingCount--; 599 } 600 } 601 602 if (propagate) { 603 // propagate the event to parents (set with addEventParent) 604 this._propagateEvent(event); 605 } 606 607 return this; 608 }, 609 610 // @method listens(type: String): Boolean 611 // Returns `true` if a particular event type has any listeners attached to it. 612 listens: function (type, propagate) { 613 var listeners = this._events && this._events[type]; 614 if (listeners && listeners.length) { return true; } 615 616 if (propagate) { 617 // also check parents for listeners if event propagates 618 for (var id in this._eventParents) { 619 if (this._eventParents[id].listens(type, propagate)) { return true; } 620 } 621 } 622 return false; 623 }, 624 625 // @method once(â¦): this 626 // Behaves as [`on(â¦)`](#evented-on), except the listener will only get fired once and then removed. 627 once: function (types, fn, context) { 628 629 if (typeof types === 'object') { 630 for (var type in types) { 631 this.once(type, types[type], fn); 632 } 633 return this; 634 } 635 636 var handler = bind(function () { 637 this 638 .off(types, fn, context) 639 .off(types, handler, context); 640 }, this); 641 642 // add a listener that's executed once and removed after that 643 return this 644 .on(types, fn, context) 645 .on(types, handler, context); 646 }, 647 648 // @method addEventParent(obj: Evented): this 649 // Adds an event parent - an `Evented` that will receive propagated events 650 addEventParent: function (obj) { 651 this._eventParents = this._eventParents || {}; 652 this._eventParents[stamp(obj)] = obj; 653 return this; 654 }, 655 656 // @method removeEventParent(obj: Evented): this 657 // Removes an event parent, so it will stop receiving propagated events 658 removeEventParent: function (obj) { 659 if (this._eventParents) { 660 delete this._eventParents[stamp(obj)]; 661 } 662 return this; 663 }, 664
vendor: 12,682 bytes, lines 665-1120
665 _propagateEvent: function (e) { 666 for (var id in this._eventParents) { 667 this._eventParents[id].fire(e.type, extend({ 668 layer: e.target, 669 propagatedFrom: e.target 670 }, e), true); 671 } 672 } 673}; 674 675// aliases; we should ditch those eventually 676 677// @method addEventListener(â¦): this 678// Alias to [`on(â¦)`](#evented-on) 679Events.addEventListener = Events.on; 680 681// @method removeEventListener(â¦): this 682// Alias to [`off(â¦)`](#evented-off) 683 684// @method clearAllEventListeners(â¦): this 685// Alias to [`off()`](#evented-off) 686Events.removeEventListener = Events.clearAllEventListeners = Events.off; 687 688// @method addOneTimeEventListener(â¦): this 689// Alias to [`once(â¦)`](#evented-once) 690Events.addOneTimeEventListener = Events.once; 691 692// @method fireEvent(â¦): this 693// Alias to [`fire(â¦)`](#evented-fire) 694Events.fireEvent = Events.fire; 695 696// @method hasEventListeners(â¦): Boolean 697// Alias to [`listens(â¦)`](#evented-listens) 698Events.hasEventListeners = Events.listens; 699 700var Evented = Class.extend(Events); 701 702/* 703 * @class Point 704 * @aka L.Point 705 * 706 * Represents a point with `x` and `y` coordinates in pixels. 707 * 708 * @example 709 * 710 * ```js 711 * var point = L.point(200, 300); 712 * ``` 713 * 714 * 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: 715 * 716 * ```js 717 * map.panBy([200, 300]); 718 * map.panBy(L.point(200, 300)); 719 * ``` 720 * 721 * Note that `Point` does not inherit from Leafet's `Class` object, 722 * which means new classes can't inherit from it, and new methods 723 * can't be added to it with the `include` function. 724 */ 725 726function Point(x, y, round) { 727 // @property x: Number; The `x` coordinate of the point 728 this.x = (round ? Math.round(x) : x); 729 // @property y: Number; The `y` coordinate of the point 730 this.y = (round ? Math.round(y) : y); 731} 732 733var trunc = Math.trunc || function (v) { 734 return v > 0 ? Math.floor(v) : Math.ceil(v); 735}; 736 737Point.prototype = { 738 739 // @method clone(): Point 740 // Returns a copy of the current point. 741 clone: function () { 742 return new Point(this.x, this.y); 743 }, 744 745 // @method add(otherPoint: Point): Point 746 // Returns the result of addition of the current and the given points. 747 add: function (point) { 748 // non-destructive, returns a new point 749 return this.clone()._add(toPoint(point)); 750 }, 751 752 _add: function (point) { 753 // destructive, used directly for performance in situations where it's safe to modify existing point 754 this.x += point.x; 755 this.y += point.y; 756 return this; 757 }, 758 759 // @method subtract(otherPoint: Point): Point 760 // Returns the result of subtraction of the given point from the current. 761 subtract: function (point) { 762 return this.clone()._subtract(toPoint(point)); 763 }, 764 765 _subtract: function (point) { 766 this.x -= point.x; 767 this.y -= point.y; 768 return this; 769 }, 770 771 // @method divideBy(num: Number): Point 772 // Returns the result of division of the current point by the given number. 773 divideBy: function (num) { 774 return this.clone()._divideBy(num); 775 }, 776 777 _divideBy: function (num) { 778 this.x /= num; 779 this.y /= num; 780 return this; 781 }, 782 783 // @method multiplyBy(num: Number): Point 784 // Returns the result of multiplication of the current point by the given number. 785 multiplyBy: function (num) { 786 return this.clone()._multiplyBy(num); 787 }, 788 789 _multiplyBy: function (num) { 790 this.x *= num; 791 this.y *= num; 792 return this; 793 }, 794 795 // @method scaleBy(scale: Point): Point 796 // Multiply each coordinate of the current point by each coordinate of 797 // `scale`. In linear algebra terms, multiply the point by the 798 // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation) 799 // defined by `scale`. 800 scaleBy: function (point) { 801 return new Point(this.x * point.x, this.y * point.y); 802 }, 803 804 // @method unscaleBy(scale: Point): Point 805 // Inverse of `scaleBy`. Divide each coordinate of the current point by 806 // each coordinate of `scale`. 807 unscaleBy: function (point) { 808 return new Point(this.x / point.x, this.y / point.y); 809 }, 810 811 // @method round(): Point 812 // Returns a copy of the current point with rounded coordinates. 813 round: function () { 814 return this.clone()._round(); 815 }, 816 817 _round: function () { 818 this.x = Math.round(this.x); 819 this.y = Math.round(this.y); 820 return this; 821 }, 822 823 // @method floor(): Point 824 // Returns a copy of the current point with floored coordinates (rounded down). 825 floor: function () { 826 return this.clone()._floor(); 827 }, 828 829 _floor: function () { 830 this.x = Math.floor(this.x); 831 this.y = Math.floor(this.y); 832 return this; 833 }, 834 835 // @method ceil(): Point 836 // Returns a copy of the current point with ceiled coordinates (rounded up). 837 ceil: function () { 838 return this.clone()._ceil(); 839 }, 840 841 _ceil: function () { 842 this.x = Math.ceil(this.x); 843 this.y = Math.ceil(this.y); 844 return this; 845 }, 846 847 // @method trunc(): Point 848 // Returns a copy of the current point with truncated coordinates (rounded towards zero). 849 trunc: function () { 850 return this.clone()._trunc(); 851 }, 852 853 _trunc: function () { 854 this.x = trunc(this.x); 855 this.y = trunc(this.y); 856 return this; 857 }, 858 859 // @method distanceTo(otherPoint: Point): Number 860 // Returns the cartesian distance between the current and the given points. 861 distanceTo: function (point) { 862 point = toPoint(point); 863 864 var x = point.x - this.x, 865 y = point.y - this.y; 866 867 return Math.sqrt(x * x + y * y); 868 }, 869 870 // @method equals(otherPoint: Point): Boolean 871 // Returns `true` if the given point has the same coordinates. 872 equals: function (point) { 873 point = toPoint(point); 874 875 return point.x === this.x && 876 point.y === this.y; 877 }, 878 879 // @method contains(otherPoint: Point): Boolean 880 // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values). 881 contains: function (point) { 882 point = toPoint(point); 883 884 return Math.abs(point.x) <= Math.abs(this.x) && 885 Math.abs(point.y) <= Math.abs(this.y); 886 }, 887 888 // @method toString(): String 889 // Returns a string representation of the point for debugging purposes. 890 toString: function () { 891 return 'Point(' + 892 formatNum(this.x) + ', ' + 893 formatNum(this.y) + ')'; 894 } 895}; 896 897// @factory L.point(x: Number, y: Number, round?: Boolean) 898// Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values. 899 900// @alternative 901// @factory L.point(coords: Number[]) 902// Expects an array of the form `[x, y]` instead. 903 904// @alternative 905// @factory L.point(coords: Object) 906// Expects a plain object of the form `{x: Number, y: Number}` instead. 907function toPoint(x, y, round) { 908 if (x instanceof Point) { 909 return x; 910 } 911 if (isArray(x)) { 912 return new Point(x[0], x[1]); 913 } 914 if (x === undefined || x === null) { 915 return x; 916 } 917 if (typeof x === 'object' && 'x' in x && 'y' in x) { 918 return new Point(x.x, x.y); 919 } 920 return new Point(x, y, round); 921} 922 923/* 924 * @class Bounds 925 * @aka L.Bounds 926 * 927 * Represents a rectangular area in pixel coordinates. 928 * 929 * @example 930 * 931 * ```js 932 * var p1 = L.point(10, 10), 933 * p2 = L.point(40, 60), 934 * bounds = L.bounds(p1, p2); 935 * ``` 936 * 937 * 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: 938 * 939 * ```js 940 * otherBounds.intersects([[10, 10], [40, 60]]); 941 * ``` 942 * 943 * Note that `Bounds` does not inherit from Leafet's `Class` object, 944 * which means new classes can't inherit from it, and new methods 945 * can't be added to it with the `include` function. 946 */ 947 948function Bounds(a, b) { 949 if (!a) { return; } 950 951 var points = b ? [a, b] : a; 952 953 for (var i = 0, len = points.length; i < len; i++) { 954 this.extend(points[i]); 955 } 956} 957 958Bounds.prototype = { 959 // @method extend(point: Point): this 960 // Extends the bounds to contain the given point. 961 extend: function (point) { // (Point) 962 point = toPoint(point); 963 964 // @property min: Point 965 // The top left corner of the rectangle. 966 // @property max: Point 967 // The bottom right corner of the rectangle. 968 if (!this.min && !this.max) { 969 this.min = point.clone(); 970 this.max = point.clone(); 971 } else { 972 this.min.x = Math.min(point.x, this.min.x); 973 this.max.x = Math.max(point.x, this.max.x); 974 this.min.y = Math.min(point.y, this.min.y); 975 this.max.y = Math.max(point.y, this.max.y); 976 } 977 return this; 978 }, 979 980 // @method getCenter(round?: Boolean): Point 981 // Returns the center point of the bounds. 982 getCenter: function (round) { 983 return new Point( 984 (this.min.x + this.max.x) / 2, 985 (this.min.y + this.max.y) / 2, round); 986 }, 987 988 // @method getBottomLeft(): Point 989 // Returns the bottom-left point of the bounds. 990 getBottomLeft: function () { 991 return new Point(this.min.x, this.max.y); 992 }, 993 994 // @method getTopRight(): Point 995 // Returns the top-right point of the bounds. 996 getTopRight: function () { // -> Point 997 return new Point(this.max.x, this.min.y); 998 }, 999 1000 // @method getTopLeft(): Point 1001 // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)). 1002 getTopLeft: function () { 1003 return this.min; // left, top 1004 }, 1005 1006 // @method getBottomRight(): Point 1007 // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)). 1008 getBottomRight: function () { 1009 return this.max; // right, bottom 1010 }, 1011 1012 // @method getSize(): Point 1013 // Returns the size of the given bounds 1014 getSize: function () { 1015 return this.max.subtract(this.min); 1016 }, 1017 1018 // @method contains(otherBounds: Bounds): Boolean 1019 // Returns `true` if the rectangle contains the given one. 1020 // @alternative 1021 // @method contains(point: Point): Boolean 1022 // Returns `true` if the rectangle contains the given point. 1023 contains: function (obj) { 1024 var min, max; 1025 1026 if (typeof obj[0] === 'number' || obj instanceof Point) { 1027 obj = toPoint(obj); 1028 } else { 1029 obj = toBounds(obj); 1030 } 1031 1032 if (obj instanceof Bounds) { 1033 min = obj.min; 1034 max = obj.max; 1035 } else { 1036 min = max = obj; 1037 } 1038 1039 return (min.x >= this.min.x) && 1040 (max.x <= this.max.x) && 1041 (min.y >= this.min.y) && 1042 (max.y <= this.max.y); 1043 }, 1044 1045 // @method intersects(otherBounds: Bounds): Boolean 1046 // Returns `true` if the rectangle intersects the given bounds. Two bounds 1047 // intersect if they have at least one point in common. 1048 intersects: function (bounds) { // (Bounds) -> Boolean 1049 bounds = toBounds(bounds); 1050 1051 var min = this.min, 1052 max = this.max, 1053 min2 = bounds.min, 1054 max2 = bounds.max, 1055 xIntersects = (max2.x >= min.x) && (min2.x <= max.x), 1056 yIntersects = (max2.y >= min.y) && (min2.y <= max.y); 1057 1058 return xIntersects && yIntersects; 1059 }, 1060 1061 // @method overlaps(otherBounds: Bounds): Boolean 1062 // Returns `true` if the rectangle overlaps the given bounds. Two bounds 1063 // overlap if their intersection is an area. 1064 overlaps: function (bounds) { // (Bounds) -> Boolean 1065 bounds = toBounds(bounds); 1066 1067 var min = this.min, 1068 max = this.max, 1069 min2 = bounds.min, 1070 max2 = bounds.max, 1071 xOverlaps = (max2.x > min.x) && (min2.x < max.x), 1072 yOverlaps = (max2.y > min.y) && (min2.y < max.y); 1073 1074 return xOverlaps && yOverlaps; 1075 }, 1076 1077 isValid: function () { 1078 return !!(this.min && this.max); 1079 } 1080}; 1081 1082 1083// @factory L.bounds(corner1: Point, corner2: Point) 1084// Creates a Bounds object from two corners coordinate pairs. 1085// @alternative 1086// @factory L.bounds(points: Point[]) 1087// Creates a Bounds object from the given array of points. 1088function toBounds(a, b) { 1089 if (!a || a instanceof Bounds) { 1090 return a; 1091 } 1092 return new Bounds(a, b); 1093} 1094 1095/* 1096 * @class LatLngBounds 1097 * @aka L.LatLngBounds 1098 * 1099 * Represents a rectangular geographical area on a map. 1100 * 1101 * @example 1102 * 1103 * ```js 1104 * var corner1 = L.latLng(40.712, -74.227), 1105 * corner2 = L.latLng(40.774, -74.125), 1106 * bounds = L.latLngBounds(corner1, corner2); 1107 * ``` 1108 * 1109 * 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: 1110 * 1111 * ```js 1112 * map.fitBounds([ 1113 * [40.712, -74.227], 1114 * [40.774, -74.125] 1115 * ]); 1116 * ``` 1117 * 1118 * 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. 1119 * 1120 * Note that `LatLngBounds` does not inherit from Leaf
vendor: 4,947 bytes, lines 1120-1290
1120et's `Class` object, 1121 * which means new classes can't inherit from it, and new methods 1122 * can't be added to it with the `include` function. 1123 */ 1124 1125function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[]) 1126 if (!corner1) { return; } 1127 1128 var latlngs = corner2 ? [corner1, corner2] : corner1; 1129 1130 for (var i = 0, len = latlngs.length; i < len; i++) { 1131 this.extend(latlngs[i]); 1132 } 1133} 1134 1135LatLngBounds.prototype = { 1136 1137 // @method extend(latlng: LatLng): this 1138 // Extend the bounds to contain the given point 1139 1140 // @alternative 1141 // @method extend(otherBounds: LatLngBounds): this 1142 // Extend the bounds to contain the given bounds 1143 extend: function (obj) { 1144 var sw = this._southWest, 1145 ne = this._northEast, 1146 sw2, ne2; 1147 1148 if (obj instanceof LatLng) { 1149 sw2 = obj; 1150 ne2 = obj; 1151 1152 } else if (obj instanceof LatLngBounds) { 1153 sw2 = obj._southWest; 1154 ne2 = obj._northEast; 1155 1156 if (!sw2 || !ne2) { return this; } 1157 1158 } else { 1159 return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this; 1160 } 1161 1162 if (!sw && !ne) { 1163 this._southWest = new LatLng(sw2.lat, sw2.lng); 1164 this._northEast = new LatLng(ne2.lat, ne2.lng); 1165 } else { 1166 sw.lat = Math.min(sw2.lat, sw.lat); 1167 sw.lng = Math.min(sw2.lng, sw.lng); 1168 ne.lat = Math.max(ne2.lat, ne.lat); 1169 ne.lng = Math.max(ne2.lng, ne.lng); 1170 } 1171 1172 return this; 1173 }, 1174 1175 // @method pad(bufferRatio: Number): LatLngBounds 1176 // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. 1177 // For example, a ratio of 0.5 extends the bounds by 50% in each direction. 1178 // Negative values will retract the bounds. 1179 pad: function (bufferRatio) { 1180 var sw = this._southWest, 1181 ne = this._northEast, 1182 heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, 1183 widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; 1184 1185 return new LatLngBounds( 1186 new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), 1187 new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); 1188 }, 1189 1190 // @method getCenter(): LatLng 1191 // Returns the center point of the bounds. 1192 getCenter: function () { 1193 return new LatLng( 1194 (this._southWest.lat + this._northEast.lat) / 2, 1195 (this._southWest.lng + this._northEast.lng) / 2); 1196 }, 1197 1198 // @method getSouthWest(): LatLng 1199 // Returns the south-west point of the bounds. 1200 getSouthWest: function () { 1201 return this._southWest; 1202 }, 1203 1204 // @method getNorthEast(): LatLng 1205 // Returns the north-east point of the bounds. 1206 getNorthEast: function () { 1207 return this._northEast; 1208 }, 1209 1210 // @method getNorthWest(): LatLng 1211 // Returns the north-west point of the bounds. 1212 getNorthWest: function () { 1213 return new LatLng(this.getNorth(), this.getWest()); 1214 }, 1215 1216 // @method getSouthEast(): LatLng 1217 // Returns the south-east point of the bounds. 1218 getSouthEast: function () { 1219 return new LatLng(this.getSouth(), this.getEast()); 1220 }, 1221 1222 // @method getWest(): Number 1223 // Returns the west longitude of the bounds 1224 getWest: function () { 1225 return this._southWest.lng; 1226 }, 1227 1228 // @method getSouth(): Number 1229 // Returns the south latitude of the bounds 1230 getSouth: function () { 1231 return this._southWest.lat; 1232 }, 1233 1234 // @method getEast(): Number 1235 // Returns the east longitude of the bounds 1236 getEast: function () { 1237 return this._northEast.lng; 1238 }, 1239 1240 // @method getNorth(): Number 1241 // Returns the north latitude of the bounds 1242 getNorth: function () { 1243 return this._northEast.lat; 1244 }, 1245 1246 // @method contains(otherBounds: LatLngBounds): Boolean 1247 // Returns `true` if the rectangle contains the given one. 1248 1249 // @alternative 1250 // @method contains (latlng: LatLng): Boolean 1251 // Returns `true` if the rectangle contains the given point. 1252 contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean 1253 if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) { 1254 obj = toLatLng(obj); 1255 } else { 1256 obj = toLatLngBounds(obj); 1257 } 1258 1259 var sw = this._southWest, 1260 ne = this._northEast, 1261 sw2, ne2; 1262 1263 if (obj instanceof LatLngBounds) { 1264 sw2 = obj.getSouthWest(); 1265 ne2 = obj.getNorthEast(); 1266 } else { 1267 sw2 = ne2 = obj; 1268 } 1269 1270 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && 1271 (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); 1272 }, 1273 1274 // @method intersects(otherBounds: LatLngBounds): Boolean 1275 // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common. 1276 intersects: function (bounds) { 1277 bounds = toLatLngBounds(bounds); 1278 1279 var sw = this._southWest, 1280 ne = this._northEast, 1281 sw2 = bounds.getSouthWest(), 1282 ne2 = bounds.getNorthEast(), 1283 1284 latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), 1285 lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); 1286 1287 return latIntersects && lngIntersects; 1288 }, 1289 1290 // @method overlaps(otherBounds:
1290Bounds): Boolean 1291 // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area. 1292 overlaps: function (bounds) { 1293 bounds = toLatLngBounds(bounds); 1294 1295 var sw = this._southWest, 1296 ne = this._northEast,
vendor: 6,186 bytes, lines 1297-1483
1297 sw2 = bounds.getSouthWest(), 1298 ne2 = bounds.getNorthEast(), 1299 1300 latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat), 1301 lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng); 1302 1303 return latOverlaps && lngOverlaps; 1304 }, 1305 1306 // @method toBBoxString(): String 1307 // 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. 1308 toBBoxString: function () { 1309 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(','); 1310 }, 1311 1312 // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean 1313 // 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. 1314 equals: function (bounds, maxMargin) { 1315 if (!bounds) { return false; } 1316 1317 bounds = toLatLngBounds(bounds); 1318 1319 return this._southWest.equals(bounds.getSouthWest(), maxMargin) && 1320 this._northEast.equals(bounds.getNorthEast(), maxMargin); 1321 }, 1322 1323 // @method isValid(): Boolean 1324 // Returns `true` if the bounds are properly initialized. 1325 isValid: function () { 1326 return !!(this._southWest && this._northEast); 1327 } 1328}; 1329 1330// TODO International date line? 1331 1332// @factory L.latLngBounds(corner1: LatLng, corner2: LatLng) 1333// Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle. 1334 1335// @alternative 1336// @factory L.latLngBounds(latlngs: LatLng[]) 1337// 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). 1338function toLatLngBounds(a, b) { 1339 if (a instanceof LatLngBounds) { 1340 return a; 1341 } 1342 return new LatLngBounds(a, b); 1343} 1344 1345/* @class LatLng 1346 * @aka L.LatLng 1347 * 1348 * Represents a geographical point with a certain latitude and longitude. 1349 * 1350 * @example 1351 * 1352 * ``` 1353 * var latlng = L.latLng(50.5, 30.5); 1354 * ``` 1355 * 1356 * 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: 1357 * 1358 * ``` 1359 * map.panTo([50, 30]); 1360 * map.panTo({lon: 30, lat: 50}); 1361 * map.panTo({lat: 50, lng: 30}); 1362 * map.panTo(L.latLng(50, 30)); 1363 * ``` 1364 * 1365 * Note that `LatLng` does not inherit from Leaflet's `Class` object, 1366 * which means new classes can't inherit from it, and new methods 1367 * can't be added to it with the `include` function. 1368 */ 1369 1370function LatLng(lat, lng, alt) { 1371 if (isNaN(lat) || isNaN(lng)) { 1372 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')'); 1373 } 1374 1375 // @property lat: Number 1376 // Latitude in degrees 1377 this.lat = +lat; 1378 1379 // @property lng: Number 1380 // Longitude in degrees 1381 this.lng = +lng; 1382 1383 // @property alt: Number 1384 // Altitude in meters (optional) 1385 if (alt !== undefined) { 1386 this.alt = +alt; 1387 } 1388} 1389 1390LatLng.prototype = { 1391 // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean 1392 // 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. 1393 equals: function (obj, maxMargin) { 1394 if (!obj) { return false; } 1395 1396 obj = toLatLng(obj); 1397 1398 var margin = Math.max( 1399 Math.abs(this.lat - obj.lat), 1400 Math.abs(this.lng - obj.lng)); 1401 1402 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin); 1403 }, 1404 1405 // @method toString(): String 1406 // Returns a string representation of the point (for debugging purposes). 1407 toString: function (precision) { 1408 return 'LatLng(' + 1409 formatNum(this.lat, precision) + ', ' + 1410 formatNum(this.lng, precision) + ')'; 1411 }, 1412 1413 // @method distanceTo(otherLatLng: LatLng): Number 1414 // 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). 1415 distanceTo: function (other) { 1416 return Earth.distance(this, toLatLng(other)); 1417 }, 1418 1419 // @method wrap(): LatLng 1420 // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees. 1421 wrap: function () { 1422 return Earth.wrapLatLng(this); 1423 }, 1424 1425 // @method toBounds(sizeInMeters: Number): LatLngBounds 1426 // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`. 1427 toBounds: function (sizeInMeters) { 1428 var latAccuracy = 180 * sizeInMeters / 40075017, 1429 lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat); 1430 1431 return toLatLngBounds( 1432 [this.lat - latAccuracy, this.lng - lngAccuracy], 1433 [this.lat + latAccuracy, this.lng + lngAccuracy]); 1434 }, 1435 1436 clone: function () { 1437 return new LatLng(this.lat, this.lng, this.alt); 1438 } 1439}; 1440 1441 1442 1443// @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng 1444// Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude). 1445 1446// @alternative 1447// @factory L.latLng(coords: Array): LatLng 1448// Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead. 1449 1450// @alternative 1451// @factory L.latLng(coords: Object): LatLng 1452// Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead. 1453 1454function toLatLng(a, b, c) { 1455 if (a instanceof LatLng) { 1456 return a; 1457 } 1458 if (isArray(a) && typeof a[0] !== 'object') { 1459 if (a.length === 3) { 1460 return new LatLng(a[0], a[1], a[2]); 1461 } 1462 if (a.length === 2) { 1463 return new LatLng(a[0], a[1]); 1464 } 1465 return null; 1466 } 1467 if (a === undefined || a === null) { 1468 return a; 1469 } 1470 if (typeof a === 'object' && 'lat' in a) { 1471 return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt); 1472 } 1473 if (b === undefined) { 1474 return null; 1475 } 1476 return new LatLng(a, b, c); 1477} 1478 1479/* 1480 * @namespace CRS 1481 * @crs L.CRS.Base 1482 * Object that defines coordinate reference systems for projecting 1483 * geographical points into pixel (screen) coordinates and back (and to
1484 * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See 1485 * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system). 1486 * 1487 * Leaflet defines the most usual CRSs by default. If you want to use a 1488 * CRS not defined by default, take a look at the 1489 * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin. 1490 * 1491 * Note that the CRS instances do not inherit from Leafet's `Class` object, 1492 * and can't be instantiated. Also, new classes can't inherit from them, 1493 * and methods can't be added to them with the `include` function. 1494 */ 1495 1496var CRS = { 1497 // @method latLngToPoint(latlng: LatLng, zoom: Number): Point 1498 // Projects geographical coordinates into pixel coordinates for a given zoom. 1499 latLngToPoint: function (latlng, zoom) { 1500 var projectedPoint = this.projection.project(latlng), 1501 scale = this.scale(zoom); 1502 1503 return this.transformation._transform(projectedPoint, scale); 1504 }, 1505 1506 // @method pointToLatLng(point: Point, zoom: Number): LatLng 1507 // The inverse of `latLngToPoint`. Projects pixel coordinates on a given 1508 // zoom into geographical coordinates. 1509 pointToLatLng: function (point, zoom) { 1510 var scale = this.scale(zoom), 1511 untransformedPoint = this.transformation.untransform(point, scale); 1512 1513 return this.projection.unproject(untransformedPoint); 1514 }, 1515 1516 // @method project(latlng: LatLng): Point 1517 // Projects geographical coordinates into coordinates in units accepted for 1518 // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services). 1519 project: function (latlng) { 1520 return this.projection.project(latlng); 1521 }, 1522 1523 // @method unproject(point: Point): LatLng 1524 // Given a projected coordinate returns the corresponding LatLng. 1525 // The inverse of `project`. 1526 unproject: function (point) { 1527 return this.projection.unproject(point); 1528 }, 1529 1530 // @method scale(zoom: Number): Number 1531 // Returns the scale used when transforming projected coordinates into 1532 // pixel coordinates for a particular zoom. For example, it returns 1533 // `256 * 2^zoom` for Mercator-based CRS. 1534 scale: function (zoom) { 1535 return 256 * Math.pow(2, zoom); 1536 }, 1537 1538 // @method zoom(scale: Number): Number 1539 // Inverse of `scale()`, returns the zoom level corresponding to a scale 1540 // factor of `scale`. 1541 zoom: function (scale) { 1542 return Math.log(scale / 256) / Math.LN2; 1543 }, 1544 1545 // @method getProjectedBounds(zoom: Number): Bounds 1546 // Returns the projection's bounds scaled and transformed for the provided `zoom`. 1547 getProjectedBounds: function (zoom) { 1548 if (this.infinite) { return null; } 1549 1550 var b = this.projection.bounds, 1551 s = this.scale(zoom), 1552 min = this.transformation.transform(b.min, s), 1553 max = this.transformation.transform(b.max, s); 1554 1555 return new Bounds(min, max); 1556 }, 1557 1558 // @method distance(latlng1: LatLng, latlng2: LatLng): Number 1559 // Returns the distance between two geographical coordinates. 1560 1561 // @property code: String 1562 // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`) 1563 // 1564 // @property wrapLng: Number[] 1565 // An array of two numbers defining whether the longitude (horizontal) coordinate 1566 // axis wraps around a given range and how. Defaults to `[-180, 180]` in most 1567 // geographical CRSs. If `undefined`, the longitude axis does not wrap around. 1568 // 1569 // @property wrapLat: Number[] 1570 // Like `wrapLng`, but for the latitude (vertical) axis. 1571 1572 // wrapLng: [min, max], 1573 // wrapLat: [min, max], 1574 1575 // @property infinite: Boolean 1576 // If true, the coordinate space will be unbounded (infinite in both axes) 1577 infinite: false, 1578 1579 // @method wrapLatLng(latlng: LatLng): LatLng 1580 // Returns a `LatLng` where lat and lng has been wrapped according to the 1581 // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds. 1582 wrapLatLng: function (latlng) { 1583 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng, 1584 lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat, 1585 alt = latlng.alt; 1586 1587 return new LatLng(lat, lng, alt); 1588 }, 1589 1590 // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds 1591 // Returns a `LatLngBounds` with the same size as the given one, ensuring 1592 // that its center is within the CRS's bounds. 1593 // Only accepts actual `L.LatLngBounds` instances, not arrays. 1594 wrapLatLngBounds: function (bounds) { 1595 var center = bounds.getCenter(), 1596 newCenter = this.wrapLatLng(center), 1597 latShift = center.lat - newCenter.lat, 1598 lngShift = center.lng - newCenter.lng; 1599 1600 if (latShift === 0 && lngShift === 0) { 1601 return bounds; 1602 } 1603
vendor: 8,834 bytes, lines 1604-1880
1604 var sw = bounds.getSouthWest(), 1605 ne = bounds.getNorthEast(), 1606 newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift), 1607 newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift); 1608 1609 return new LatLngBounds(newSw, newNe); 1610 } 1611}; 1612 1613/* 1614 * @namespace CRS 1615 * @crs L.CRS.Earth 1616 * 1617 * Serves as the base for CRS that are global such that they cover the earth. 1618 * Can only be used as the base for other CRS and cannot be used directly, 1619 * since it does not have a `code`, `projection` or `transformation`. `distance()` returns 1620 * meters. 1621 */ 1622 1623var Earth = extend({}, CRS, { 1624 wrapLng: [-180, 180], 1625 1626 // Mean Earth Radius, as recommended for use by 1627 // the International Union of Geodesy and Geophysics, 1628 // see http://rosettacode.org/wiki/Haversine_formula 1629 R: 6371000, 1630 1631 // distance between two geographical points using spherical law of cosines approximation 1632 distance: function (latlng1, latlng2) { 1633 var rad = Math.PI / 180, 1634 lat1 = latlng1.lat * rad, 1635 lat2 = latlng2.lat * rad, 1636 sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2), 1637 sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2), 1638 a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon, 1639 c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 1640 return this.R * c; 1641 } 1642}); 1643 1644/* 1645 * @namespace Projection 1646 * @projection L.Projection.SphericalMercator 1647 * 1648 * Spherical Mercator projection â the most common projection for online maps, 1649 * used by almost all free and commercial tile providers. Assumes that Earth is 1650 * a sphere. Used by the `EPSG:3857` CRS. 1651 */ 1652 1653var SphericalMercator = { 1654 1655 R: 6378137, 1656 MAX_LATITUDE: 85.0511287798, 1657 1658 project: function (latlng) { 1659 var d = Math.PI / 180, 1660 max = this.MAX_LATITUDE, 1661 lat = Math.max(Math.min(max, latlng.lat), -max), 1662 sin = Math.sin(lat * d); 1663 1664 return new Point( 1665 this.R * latlng.lng * d, 1666 this.R * Math.log((1 + sin) / (1 - sin)) / 2); 1667 }, 1668 1669 unproject: function (point) { 1670 var d = 180 / Math.PI; 1671 1672 return new LatLng( 1673 (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d, 1674 point.x * d / this.R); 1675 }, 1676 1677 bounds: (function () { 1678 var d = 6378137 * Math.PI; 1679 return new Bounds([-d, -d], [d, d]); 1680 })() 1681}; 1682 1683/* 1684 * @class Transformation 1685 * @aka L.Transformation 1686 * 1687 * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d` 1688 * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing 1689 * the reverse. Used by Leaflet in its projections code. 1690 * 1691 * @example 1692 * 1693 * ```js 1694 * var transformation = L.transformation(2, 5, -1, 10), 1695 * p = L.point(1, 2), 1696 * p2 = transformation.transform(p), // L.point(7, 8) 1697 * p3 = transformation.untransform(p2); // L.point(1, 2) 1698 * ``` 1699 */ 1700 1701 1702// factory new L.Transformation(a: Number, b: Number, c: Number, d: Number) 1703// Creates a `Transformation` object with the given coefficients. 1704function Transformation(a, b, c, d) { 1705 if (isArray(a)) { 1706 // use array properties 1707 this._a = a[0]; 1708 this._b = a[1]; 1709 this._c = a[2]; 1710 this._d = a[3]; 1711 return; 1712 } 1713 this._a = a; 1714 this._b = b; 1715 this._c = c; 1716 this._d = d; 1717} 1718 1719Transformation.prototype = { 1720 // @method transform(point: Point, scale?: Number): Point 1721 // Returns a transformed point, optionally multiplied by the given scale. 1722 // Only accepts actual `L.Point` instances, not arrays. 1723 transform: function (point, scale) { // (Point, Number) -> Point 1724 return this._transform(point.clone(), scale); 1725 }, 1726 1727 // destructive transform (faster) 1728 _transform: function (point, scale) { 1729 scale = scale || 1; 1730 point.x = scale * (this._a * point.x + this._b); 1731 point.y = scale * (this._c * point.y + this._d); 1732 return point; 1733 }, 1734 1735 // @method untransform(point: Point, scale?: Number): Point 1736 // Returns the reverse transformation of the given point, optionally divided 1737 // by the given scale. Only accepts actual `L.Point` instances, not arrays. 1738 untransform: function (point, scale) { 1739 scale = scale || 1; 1740 return new Point( 1741 (point.x / scale - this._b) / this._a, 1742 (point.y / scale - this._d) / this._c); 1743 } 1744}; 1745 1746// factory L.transformation(a: Number, b: Number, c: Number, d: Number) 1747 1748// @factory L.transformation(a: Number, b: Number, c: Number, d: Number) 1749// Instantiates a Transformation object with the given coefficients. 1750 1751// @alternative 1752// @factory L.transformation(coefficients: Array): Transformation 1753// Expects an coefficients array of the form 1754// `[a: Number, b: Number, c: Number, d: Number]`. 1755 1756function toTransformation(a, b, c, d) { 1757 return new Transformation(a, b, c, d); 1758} 1759 1760/* 1761 * @namespace CRS 1762 * @crs L.CRS.EPSG3857 1763 * 1764 * The most common CRS for online maps, used by almost all free and commercial 1765 * tile providers. Uses Spherical Mercator projection. Set in by default in 1766 * Map's `crs` option. 1767 */ 1768 1769var EPSG3857 = extend({}, Earth, { 1770 code: 'EPSG:3857', 1771 projection: SphericalMercator, 1772 1773 transformation: (function () { 1774 var scale = 0.5 / (Math.PI * SphericalMercator.R); 1775 return toTransformation(scale, 0.5, -scale, 0.5); 1776 }()) 1777}); 1778 1779var EPSG900913 = extend({}, EPSG3857, { 1780 code: 'EPSG:900913' 1781}); 1782 1783// @namespace SVG; @section 1784// There are several static functions which can be called without instantiating L.SVG: 1785 1786// @function create(name: String): SVGElement 1787// Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement), 1788// corresponding to the class name passed. For example, using 'line' will return 1789// an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement). 1790function svgCreate(name) { 1791 return document.createElementNS('http://www.w3.org/2000/svg', name); 1792} 1793 1794// @function pointsToPath(rings: Point[], closed: Boolean): String 1795// Generates a SVG path string for multiple rings, with each ring turning 1796// into "M..L..L.." instructions 1797function pointsToPath(rings, closed) { 1798 var str = '', 1799 i, j, len, len2, points, p; 1800 1801 for (i = 0, len = rings.length; i < len; i++) { 1802 points = rings[i]; 1803 1804 for (j = 0, len2 = points.length; j < len2; j++) { 1805 p = points[j]; 1806 str += (j ? 'L' : 'M') + p.x + ' ' + p.y; 1807 } 1808 1809 // closes the ring for polygons; "x" is VML syntax 1810 str += closed ? (svg ? 'z' : 'x') : ''; 1811 } 1812 1813 // SVG complains about empty path strings 1814 return str || 'M0 0'; 1815} 1816 1817/* 1818 * @namespace Browser 1819 * @aka L.Browser 1820 * 1821 * A namespace with static properties for browser/feature detection used by Leaflet internally. 1822 * 1823 * @example 1824 * 1825 * ```js 1826 * if (L.Browser.ielt9) { 1827 * alert('Upgrade your browser, dude!'); 1828 * } 1829 * ``` 1830 */ 1831 1832var style$1 = document.documentElement.style; 1833 1834// @property ie: Boolean; `true` for all Internet Explorer versions (not Edge). 1835var ie = 'ActiveXObject' in window; 1836 1837// @property ielt9: Boolean; `true` for Internet Explorer versions less than 9. 1838var ielt9 = ie && !document.addEventListener; 1839 1840// @property edge: Boolean; `true` for the Edge web browser. 1841var edge = 'msLaunchUri' in navigator && !('documentMode' in document); 1842 1843// @property webkit: Boolean; 1844// `true` for webkit-based browsers like Chrome and Safari (including mobile versions). 1845var webkit = userAgentContains('webkit'); 1846 1847// @property android: Boolean 1848// `true` for any browser running on an Android platform. 1849var android = userAgentContains('android'); 1850 1851// @property android23: Boolean; `true` for browsers running on Android 2 or Android 3. 1852var android23 = userAgentContains('android 2') || userAgentContains('android 3'); 1853 1854/* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */ 1855var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit 1856// @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome) 1857var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window); 1858 1859// @property opera: Boolean; `true` for the Opera browser 1860var opera = !!window.opera; 1861 1862// @property chrome: Boolean; `true` for the Chrome browser. 1863var chrome = userAgentContains('chrome'); 1864 1865// @property gecko: Boolean; `true` for gecko-based browsers like Firefox. 1866var gecko = userAgentContains('gecko') && !webkit && !opera && !ie; 1867 1868// @property safari: Boolean; `true` for the Safari browser. 1869var safari = !chrome && userAgentContains('safari'); 1870 1871var phantom = userAgentContains('phantom'); 1872 1873// @property opera12: Boolean 1874// `true` for the Opera browser supporting CSS transforms (version 12 or later). 1875var opera12 = 'OTransition' in style$1; 1876 1877// @property win: Boolean; `true` when the browser is running in a Windows platform 1878var win = navigator.platform.indexOf('Win') === 0; 1879 1880// @property ie3d: Boolean;
1880 `true` for all Internet Explorer versions supporting CSS transforms. 1881var ie3d = ie && ('transition' in style$1); 1882 1883// @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms. 1884var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23; 1885 1886// @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms. 1887var gecko3d = 'MozPerspective' in style$1; 1888 1889// @property any3d: Boolean 1890// `true` for all browsers supporting CSS transforms. 1891var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom; 1892 1893// @property mobile: Boolean; `true` for all browsers running in a mobile device. 1894var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile'); 1895 1896// @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device. 1897var mobileWebkit = mobile && webkit; 1898 1899// @property mobileWebkit3d: Boolean 1900// `true` for all webkit-based browsers in a mobile device supporting CSS transforms. 1901var mobileWebkit3d = mobile && webkit3d; 1902 1903// @property msPointer: Boolean 1904// `true` for browsers implementing the Microsoft touch events model (notably IE10). 1905var msPointer = !window.PointerEvent && window.MSPointerEvent; 1906 1907// @property pointer: Boolean 1908// `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). 1909var pointer = !!(window.PointerEvent || msPointer); 1910 1911// @property touch: Boolean 1912// `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events). 1913// This does not necessarily mean that the browser is running in a computer with 1914// a touchscreen, it only means that the browser is capable of understanding 1915// touch events. 1916var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window || 1917 (window.DocumentTouch && document instanceof window.DocumentTouch)); 1918 1919// @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device. 1920var mobileOpera = mobile && opera; 1921 1922// @property mobileGecko: Boolean 1923// `true` for gecko-based browsers running in a mobile device. 1924var mobileGecko = mobile && gecko; 1925 1926// @property retina: Boolean 1927// `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%. 1928var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1; 1929 1930 1931// @property canvas: Boolean 1932// `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API). 1933var canvas = (function () { 1934 return !!document.createElement('canvas').getContext; 1935}()); 1936 1937// @property svg: Boolean 1938// `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG). 1939var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect); 1940 1941// @property vml: Boolean 1942// `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language). 1943var vml = !svg && (function () { 1944 try { 1945 var div = document.createElement('div'); 1946 div.innerHTML = '<v:shape adj="1"/>'; 1947 1948 var shape = div.firstChild; 1949 shape.style.behavior = 'url(#default#VML)'; 1950 1951 return shape && (typeof shape.adj === 'object'); 1952 1953 } catch (e) { 1954 return false; 1955 } 1956}()); 1957 1958 1959function userAgentContains(str) { 1960 return navigator.userAgent.toLowerCase().indexOf(str) >= 0; 1961} 1962 1963 1964var Browser = (Object.freeze || Object)({ 1965 ie: ie, 1966 ielt9: ielt9, 1967 edge: edge, 1968 webkit: webkit, 1969 android: android, 1970 android23: android23, 1971 androidStock: androidStock, 1972 opera: opera, 1973 chrome: chrome, 1974 gecko: gecko, 1975 safari: safari, 1976 phantom: phantom, 1977 opera12: opera12, 1978 win: win, 1979 ie3d: ie3d, 1980 webkit3d: webkit3d, 1981 gecko3d: gecko3d, 1982 any3d: any3d, 1983 mobile: mobile, 1984 mobileWebkit: mobileWebkit, 1985 mobileWebkit3d: mobileWebkit3d, 1986 msPointer: msPointer, 1987 pointer: pointer, 1988 touch: touch, 1989 mobileOpera: mobileOpera, 1990 mobileGecko: mobileGecko, 1991 retina: retina, 1992 canvas: canvas, 1993 svg: svg, 1994 vml: vml 1995}); 1996 1997/* 1998 * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. 1999 */ 2000 2001 2002var POINTER_DOWN = msPointer ? 'MSPointerDown' : 'pointerdown';
2003var POINTER_MOVE = msPointer ? 'MSPointerMove' : 'pointermove'; 2004var POINTER_UP = msPointer ? 'MSPointerUp' : 'pointerup'; 2005var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel'; 2006var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION']; 2007 2008var _pointers = {}; 2009var _pointerDocListener = false; 2010 2011// DomEvent.DoubleTap needs to know about this 2012var _pointersCount = 0; 2013 2014// Provides a touch events wrapper for (ms)pointer events. 2015// ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 2016 2017function addPointerListener(obj, type, handler, id) { 2018 if (type === 'touchstart') { 2019 _addPointerStart(obj, handler, id); 2020 2021 } else if (type === 'touchmove') { 2022 _addPointerMove(obj, handler, id); 2023 2024 } else if (type === 'touchend') { 2025 _addPointerEnd(obj, handler, id); 2026 } 2027 2028 return this; 2029} 2030 2031function removePointerListener(obj, type, id) { 2032 var handler = obj['_leaflet_' + type + id]; 2033 2034 if (type === 'touchstart') { 2035 obj.removeEventListener(POINTER_DOWN, handler, false); 2036 2037 } else if (type === 'touchmove') { 2038 obj.removeEventListener(POINTER_MOVE, handler, false); 2039 2040 } else if (type === 'touchend') { 2041 obj.removeEventListener(POINTER_UP, handler, false); 2042 obj.removeEventListener(POINTER_CANCEL, handler, false); 2043 } 2044 2045 return this; 2046} 2047 2048function _addPointerStart(obj, handler, id) { 2049 var onDown = bind(function (e) { 2050 if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { 2051 // In IE11, some touch events needs to fire for form controls, or 2052 // the controls will stop working. We keep a whitelist of tag names that 2053 // need these events. For other target tags, we prevent default on the event. 2054 if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) { 2055 preventDefault(e); 2056 } else { 2057 return; 2058 } 2059 } 2060 2061 _handlePointer(e, handler); 2062 }); 2063 2064 obj['_leaflet_touchstart' + id] = onDown; 2065 obj.addEventListener(POINTER_DOWN, onDown, false); 2066 2067 // need to keep track of what pointers and how many are active to provide e.touches emulation 2068 if (!_pointerDocListener) { 2069 // we listen documentElement as any drags that end by moving the touch off the screen get fired there 2070 document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true); 2071 document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true); 2072 document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true); 2073 document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true); 2074 2075 _pointerDocListener = true; 2076 } 2077} 2078 2079function _globalPointerDown(e) { 2080 _pointers[e.pointerId] = e; 2081 _pointersCount++; 2082} 2083 2084function _globalPointerMove(e) { 2085 if (_pointers[e.pointerId]) { 2086 _pointers[e.pointerId] = e; 2087 } 2088} 2089 2090function _globalPointerUp(e) { 2091 delete _pointers[e.pointerId]; 2092 _pointersCount--; 2093} 2094 2095function _handlePointer(e, handler) { 2096 e.touches = []; 2097 for (var i in _pointers) { 2098 e.touches.push(_pointers[i]); 2099 } 2100 e.changedTouches = [e]; 2101 2102 handler(e); 2103} 2104 2105function _addPointerMove(obj, handler, id) { 2106 var onMove = function (e) { 2107 // don't fire touch moves when mouse isn't down 2108 if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; } 2109 2110 _handlePointer(e, handler); 2111 }; 2112 2113 obj['_leaflet_touchmove' + id] = onMove; 2114 obj.addEventListener(POINTER_MOVE, onMove, false); 2115} 2116 2117function _addPointerEnd(obj, handler, id) { 2118 var onUp = function (e) { 2119 _handlePointer(e, handler); 2120 }; 2121 2122 obj['_leaflet_touchend' + id] = onUp; 2123 obj.addEventListener(POINTER_UP, onUp, false); 2124 obj.addEventListener(POINTER_CANCEL, onUp, false); 2125} 2126 2127/* 2128 * Extends the event handling code with double tap support for mobile browsers. 2129 */ 2130 2131var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart'; 2132var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend'; 2133var _pre = '_leaflet_'; 2134 2135// inspired by Zepto touch code by Thomas Fuchs 2136function addDoubleTapListener(obj, handler, id) { 2137 var last, touch$$1, 2138 doubleTap = false,
2139 delay = 250; 2140 2141 function onTouchStart(e) { 2142 var count; 2143 2144 if (pointer) { 2145 if ((!edge) || e.pointerType === 'mouse') { return; } 2146 count = _pointersCount; 2147 } else { 2148 count = e.touches.length; 2149 } 2150 2151 if (count > 1) { return; } 2152 2153 var now = Date.now(), 2154 delta = now - (last || now); 2155 2156 touch$$1 = e.touches ? e.touches[0] : e; 2157 doubleTap = (delta > 0 && delta <= delay); 2158 last = now; 2159 } 2160 2161 function onTouchEnd(e) { 2162 if (doubleTap && !touch$$1.cancelBubble) { 2163 if (pointer) { 2164 if ((!edge) || e.pointerType === 'mouse') { return; } 2165 // work around .type being readonly with MSPointer* events 2166 var newTouch = {}, 2167 prop, i; 2168 2169 for (i in touch$$1) { 2170 prop = touch$$1[i]; 2171 newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop; 2172 } 2173 touch$$1 = newTouch; 2174 } 2175 touch$$1.type = 'dblclick'; 2176 handler(touch$$1); 2177 last = null; 2178 } 2179 } 2180 2181 obj[_pre + _touchstart + id] = onTouchStart; 2182 obj[_pre + _touchend + id] = onTouchEnd; 2183 obj[_pre + 'dblclick' + id] = handler; 2184 2185 obj.addEventListener(_touchstart, onTouchStart, false); 2186 obj.addEventListener(_touchend, onTouchEnd, false); 2187 2188 // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse), 2189 // the browser doesn't fire touchend/pointerup events but does fire 2190 // native dblclicks. See #4127. 2191 // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180. 2192 obj.addEventListener('dblclick', handler, false); 2193 2194 return this; 2195} 2196 2197function removeDoubleTapListener(obj, id) { 2198 var touchstart = obj[_pre + _touchstart + id], 2199 touchend = obj[_pre + _touchend + id], 2200 dblclick = obj[_pre + 'dblclick' + id]; 2201 2202 obj.removeEventListener(_touchstart, touchstart, false); 2203 obj.removeEventListener(_touchend, touchend, false); 2204 if (!edge) { 2205 obj.removeEventListener('dblclick', dblclick, false); 2206 } 2207 2208 return this; 2209} 2210 2211/* 2212 * @namespace DomUtil 2213 * 2214 * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model) 2215 * tree, used by Leaflet internally. 2216 * 2217 * Most functions expecting or returning a `HTMLElement` also work for 2218 * SVG elements. The only difference is that classes refer to CSS classes 2219 * in HTML and SVG classes in SVG. 2220 */ 2221 2222 2223// @property TRANSFORM: String 2224// Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit). 2225var TRANSFORM = testProp( 2226 ['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']); 2227 2228// webkitTransition comes first because some browser versions that drop vendor prefix don't do 2229// the same for the transitionend event, in particular the Android 4.1 stock browser 2230 2231// @property TRANSITION: String 2232// Vendor-prefixed transition style name. 2233var TRANSITION = testProp( 2234 ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); 2235 2236// @property TRANSITION_END: String 2237// Vendor-prefixed transitionend event name. 2238var TRANSITION_END = 2239 TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend'; 2240 2241 2242// @function get(id: String|HTMLElement): HTMLElement 2243// Returns an element given its DOM id, or returns the element itself 2244// if it was passed directly. 2245function get(id) { 2246 return typeof id === 'string' ? document.getElementById(id) : id; 2247} 2248 2249// @function getStyle(el: HTMLElement, styleAttrib: String): String 2250// Returns the value for a certain style attribute on an element, 2251// including computed values or values set through CSS. 2252function getStyle(el, style) { 2253 var value = el.style[style] || (el.currentStyle && el.currentStyle[style]); 2254 2255 if ((!value || value === 'auto') && document.defaultView) { 2256 var css = document.defaultView.getComputedStyle(el, null); 2257 value = css ? css[style] : null; 2258 } 2259 return value === 'auto' ? null : value; 2260} 2261 2262// @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement 2263// Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element. 2264function create$1(tagName, className, container) { 2265 var el = document.createElement(tagName); 2266 el.className = className || ''; 2267 2268 if (container) { 2269 container.appendChild(el); 2270 } 2271 return el; 2272} 2273 2274// @function remove(el: HTMLElement) 2275// Removes `el` from its parent element 2276function remove(el) { 2277 var parent = el.parentNode; 2278 if (parent) { 2279 parent.removeChild(el); 2280 } 2281} 2282 2283// @function empty(el: HTMLElement) 2284// Removes all of `el`'s children elements from `el` 2285function empty(el) { 2286 while (el.firstChild) { 2287 el.removeChild(el.firstChild); 2288 } 2289} 2290 2291// @function toFront(el: HTMLElement) 2292// Makes `el` the last child of its parent, so it renders in front of the other children. 2293function toFront(el) { 2294 var parent = el.parentNode; 2295 if (parent && parent.lastChild !== el) { 2296 parent.appendChild(el); 2297 } 2298} 2299 2300// @function toBack(el: HTMLElement) 2301// Makes `el` the first child of its parent, so it renders behind the other children. 2302function toBack(el) { 2303 var parent = el.parentNode; 2304 if (parent && parent.firstChild !== el) { 2305 parent.insertBefore(el, parent.firstChild); 2306 } 2307} 2308 2309// @function hasClass(el: HTMLElement, name: String): Boolean 2310// Returns `true` if the element's class attribute contains `name`. 2311function hasClass(el, name) { 2312 if (el.classList !== undefined) { 2313 return el.classList.contains(name); 2314 } 2315 var className = getClass(el); 2316 return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className); 2317} 2318 2319// @function addClass(el: HTMLElement, name: String) 2320// Adds `name` to the element's class attribute. 2321function addClass(el, name) { 2322 if (el.classList !== undefined) { 2323 var classes = splitWords(name); 2324 for (var i = 0, len = classes.length; i < len; i++) { 2325 el.classList.add(classes[i]); 2326 } 2327 } else if (!hasClass(el, name)) { 2328 var className = getClass(el); 2329 setClass(el, (className ? className + ' ' : '') + name); 2330 } 2331} 2332 2333// @function removeClass(el: HTMLElement, name: String) 2334// Removes `name` from the element's class attribute. 2335function removeClass(el, name) { 2336 if (el.classList !== undefined) { 2337 el.classList.remove(name); 2338 } else { 2339 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' '))); 2340 } 2341} 2342 2343// @function setClass(el: HTMLElement, name: String) 2344// Sets the element's class. 2345function setClass(el, name) { 2346 if (el.className.baseVal === undefined) { 2347 el.className = name; 2348 } else { 2349 // in case of SVG element 2350 el.className.baseVal = name; 2351 } 2352} 2353 2354// @function getClass(el: HTMLElement): String 2355// Returns the element's class. 2356function getClass(el) { 2357 // Check if the element is an SVGElementInstance and use the correspondingElement instead 2358 // (Required for linked SVG elements in IE11.) 2359 if (el.correspondingElement) { 2360 el = el.correspondingElement; 2361 } 2362 return el.className.baseVal === undefined ? el.className : el.className.baseVal; 2363} 2364 2365// @function setOpacity(el: HTMLElement, opacity: Number) 2366// Set the opacity of an element (including old IE support). 2367// `opacity` must be a number from `0` to `1`. 2368function setOpacity(el, value) { 2369 if ('opacity' in el.style) { 2370 el.style.opacity = value; 2371 } else if ('filter' in el.style) { 2372 _setOpacityIE(el, value); 2373 } 2374} 2375 2376function _setOpacityIE(el, value) { 2377 var filter = false, 2378 filterName = 'DXImageTransform.Microsoft.Alpha'; 2379 2380 // filters collection throws an error if we try to retrieve a filter that doesn't exist 2381 try { 2382 filter = el.filters.item(filterName); 2383 } catch (e) { 2384 // don't set opacity to 1 if we haven't already set an opacity, 2385 // it isn't needed and breaks transparent pngs. 2386 if (value === 1) { return; } 2387 } 2388 2389 value = Math.round(value * 100); 2390 2391 if (filter) { 2392 filter.Enabled = (value !== 100); 2393 filter.Opacity = value; 2394 } else { 2395 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; 2396 } 2397} 2398 2399// @function testProp(props: String[]): String|false 2400// Goes through the array of style names and returns the first name 2401// that is a valid style name for an element. If no such name is found, 2402// it returns false. Useful for vendor-prefixed styles like `transform`. 2403function testProp(props) { 2404 var style = document.documentElement.style; 2405 2406 for (var i = 0; i < props.length; i++) { 2407 if (props[i] in style) { 2408 return props[i]; 2409 } 2410 } 2411 return false; 2412} 2413 2414// @function setTransform(el: HTMLElement, offset: Point, scale?: Number) 2415// Resets the 3D CSS transform of `el` so it is translated by `offset` pixels 2416// and optionally scaled by `scale`. Does not have an effect if the 2417// browser doesn't support 3D CSS transforms. 2418function setTransform(el, offset, scale) { 2419 var pos = offset || new Point(0, 0); 2420 2421 el.style[TRANSFORM] = 2422 (ie3d ? 2423 'translate(' + pos.x + 'px,' + pos.y + 'px)' : 2424 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') + 2425 (scale ? ' scale(' + scale + ')' : ''); 2426} 2427 2428// @function setPosition(el: HTMLElement, position: Point) 2429// Sets the position of `el` to coordinates specified by `position`,
vendor: 7,486 bytes, lines 2430-2659
2430// using CSS translate or top/left positioning depending on the browser 2431// (used by Leaflet internally to position its layers). 2432function setPosition(el, point) { 2433 2434 /*eslint-disable */ 2435 el._leaflet_pos = point; 2436 /* eslint-enable */ 2437 2438 if (any3d) { 2439 setTransform(el, point); 2440 } else { 2441 el.style.left = point.x + 'px'; 2442 el.style.top = point.y + 'px'; 2443 } 2444} 2445 2446// @function getPosition(el: HTMLElement): Point 2447// Returns the coordinates of an element previously positioned with setPosition. 2448function getPosition(el) { 2449 // this method is only used for elements previously positioned using setPosition, 2450 // so it's safe to cache the position for performance 2451 2452 return el._leaflet_pos || new Point(0, 0); 2453} 2454 2455// @function disableTextSelection() 2456// Prevents the user from generating `selectstart` DOM events, usually generated 2457// when the user drags the mouse through a page with text. Used internally 2458// by Leaflet to override the behaviour of any click-and-drag interaction on 2459// the map. Affects drag interactions on the whole document. 2460 2461// @function enableTextSelection() 2462// Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection). 2463var disableTextSelection; 2464var enableTextSelection; 2465var _userSelect; 2466if ('onselectstart' in document) { 2467 disableTextSelection = function () { 2468 on(window, 'selectstart', preventDefault); 2469 }; 2470 enableTextSelection = function () { 2471 off(window, 'selectstart', preventDefault); 2472 }; 2473} else { 2474 var userSelectProperty = testProp( 2475 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']); 2476 2477 disableTextSelection = function () { 2478 if (userSelectProperty) { 2479 var style = document.documentElement.style; 2480 _userSelect = style[userSelectProperty]; 2481 style[userSelectProperty] = 'none'; 2482 } 2483 }; 2484 enableTextSelection = function () { 2485 if (userSelectProperty) { 2486 document.documentElement.style[userSelectProperty] = _userSelect; 2487 _userSelect = undefined; 2488 } 2489 }; 2490} 2491 2492// @function disableImageDrag() 2493// As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but 2494// for `dragstart` DOM events, usually generated when the user drags an image. 2495function disableImageDrag() { 2496 on(window, 'dragstart', preventDefault); 2497} 2498 2499// @function enableImageDrag() 2500// Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection). 2501function enableImageDrag() { 2502 off(window, 'dragstart', preventDefault); 2503} 2504 2505var _outlineElement; 2506var _outlineStyle; 2507// @function preventOutline(el: HTMLElement) 2508// Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline) 2509// of the element `el` invisible. Used internally by Leaflet to prevent 2510// focusable elements from displaying an outline when the user performs a 2511// drag interaction on them. 2512function preventOutline(element) { 2513 while (element.tabIndex === -1) { 2514 element = element.parentNode; 2515 } 2516 if (!element.style) { return; } 2517 restoreOutline(); 2518 _outlineElement = element; 2519 _outlineStyle = element.style.outline; 2520 element.style.outline = 'none'; 2521 on(window, 'keydown', restoreOutline); 2522} 2523 2524// @function restoreOutline() 2525// Cancels the effects of a previous [`L.DomUtil.preventOutline`](). 2526function restoreOutline() { 2527 if (!_outlineElement) { return; } 2528 _outlineElement.style.outline = _outlineStyle; 2529 _outlineElement = undefined; 2530 _outlineStyle = undefined; 2531 off(window, 'keydown', restoreOutline); 2532} 2533 2534// @function getSizedParentNode(el: HTMLElement): HTMLElement 2535// Finds the closest parent node which size (width and height) is not null. 2536function getSizedParentNode(element) { 2537 do { 2538 element = element.parentNode; 2539 } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body); 2540 return element; 2541} 2542 2543// @function getScale(el: HTMLElement): Object 2544// Computes the CSS scale currently applied on the element. 2545// Returns an object with `x` and `y` members as horizontal and vertical scales respectively, 2546// and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect). 2547function getScale(element) { 2548 var rect = element.getBoundingClientRect(); // Read-only in old browsers. 2549 2550 return { 2551 x: rect.width / element.offsetWidth || 1, 2552 y: rect.height / element.offsetHeight || 1, 2553 boundingClientRect: rect 2554 }; 2555} 2556 2557 2558var DomUtil = (Object.freeze || Object)({ 2559 TRANSFORM: TRANSFORM, 2560 TRANSITION: TRANSITION, 2561 TRANSITION_END: TRANSITION_END, 2562 get: get, 2563 getStyle: getStyle, 2564 create: create$1, 2565 remove: remove, 2566 empty: empty, 2567 toFront: toFront, 2568 toBack: toBack, 2569 hasClass: hasClass, 2570 addClass: addClass, 2571 removeClass: removeClass, 2572 setClass: setClass, 2573 getClass: getClass, 2574 setOpacity: setOpacity, 2575 testProp: testProp, 2576 setTransform: setTransform, 2577 setPosition: setPosition, 2578 getPosition: getPosition, 2579 disableTextSelection: disableTextSelection, 2580 enableTextSelection: enableTextSelection, 2581 disableImageDrag: disableImageDrag, 2582 enableImageDrag: enableImageDrag, 2583 preventOutline: preventOutline, 2584 restoreOutline: restoreOutline, 2585 getSizedParentNode: getSizedParentNode, 2586 getScale: getScale 2587}); 2588 2589/* 2590 * @namespace DomEvent 2591 * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally. 2592 */ 2593 2594// Inspired by John Resig, Dean Edwards and YUI addEvent implementations. 2595 2596// @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this 2597// Adds a listener function (`fn`) to a particular DOM event type of the 2598// element `el`. You can optionally specify the context of the listener 2599// (object the `this` keyword will point to). You can also pass several 2600// space-separated types (e.g. `'click dblclick'`). 2601 2602// @alternative 2603// @function on(el: HTMLElement, eventMap: Object, context?: Object): this 2604// Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` 2605function on(obj, types, fn, context) { 2606 2607 if (typeof types === 'object') { 2608 for (var type in types) { 2609 addOne(obj, type, types[type], fn); 2610 } 2611 } else { 2612 types = splitWords(types); 2613 2614 for (var i = 0, len = types.length; i < len; i++) { 2615 addOne(obj, types[i], fn, context); 2616 } 2617 } 2618 2619 return this; 2620} 2621 2622var eventsKey = '_leaflet_events'; 2623 2624// @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this 2625// Removes a previously added listener function. 2626// Note that if you passed a custom context to on, you must pass the same 2627// context to `off` in order to remove the listener. 2628 2629// @alternative 2630// @function off(el: HTMLElement, eventMap: Object, context?: Object): this 2631// Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` 2632function off(obj, types, fn, context) { 2633 2634 if (typeof types === 'object') { 2635 for (var type in types) { 2636 removeOne(obj, type, types[type], fn); 2637 } 2638 } else if (types) { 2639 types = splitWords(types); 2640 2641 for (var i = 0, len = types.length; i < len; i++) { 2642 removeOne(obj, types[i], fn, context); 2643 } 2644 } else { 2645 for (var j in obj[eventsKey]) { 2646 removeOne(obj, j, obj[eventsKey][j]); 2647 } 2648 delete obj[eventsKey]; 2649 } 2650 2651 return this; 2652} 2653 2654function addOne(obj, type, fn, context) { 2655 var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''); 2656 2657 if (obj[eventsKey] && obj[eventsKey][id]) { return this; } 2658 2659 var handler = function (e) {
2660 return fn.call(context || obj, e || window.event); 2661 }; 2662 2663 var originalHandler = handler; 2664 2665 if (pointer && type.indexOf('touch') === 0) { 2666 // Needs DomEvent.Pointer.js 2667 addPointerListener(obj, type, handler, id); 2668 2669 } else if (touch && (type === 'dblclick') && addDoubleTapListener && 2670 !(pointer && chrome)) { 2671 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener 2672 // See #5180 2673 addDoubleTapListener(obj, handler, id); 2674 2675 } else if ('addEventListener' in obj) { 2676 2677 if (type === 'mousewheel') { 2678 obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false); 2679 2680 } else if ((type === 'mouseenter') || (type === 'mouseleave')) { 2681 handler = function (e) { 2682 e = e || window.event; 2683 if (isExternalTarget(obj, e)) { 2684 originalHandler(e); 2685 } 2686 }; 2687 obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false); 2688 2689 } else { 2690 if (type === 'click' && android) { 2691 handler = function (e) { 2692 filterClick(e, originalHandler); 2693 }; 2694 } 2695 obj.addEventListener(type, handler, false); 2696 } 2697 2698 } else if ('attachEvent' in obj) { 2699 obj.attachEvent('on' + type, handler); 2700 } 2701 2702 obj[eventsKey] = obj[eventsKey] || {}; 2703 obj[eventsKey][id] = handler; 2704} 2705 2706function removeOne(obj, type, fn, context) { 2707 2708 var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''), 2709 handler = obj[eventsKey] && obj[eventsKey][id]; 2710 2711 if (!handler) { return this; } 2712 2713 if (pointer && type.indexOf('touch') === 0) { 2714 removePointerListener(obj, type, id); 2715 2716 } else if (touch && (type === 'dblclick') && removeDoubleTapListener && 2717 !(pointer && chrome)) { 2718 removeDoubleTapListener(obj, id); 2719 2720 } else if ('removeEventListener' in obj) { 2721 2722 if (type === 'mousewheel') { 2723 obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false); 2724 2725 } else { 2726 obj.removeEventListener( 2727 type === 'mouseenter' ? 'mouseover' : 2728 type === 'mouseleave' ? 'mouseout' : type, handler, false); 2729 } 2730 2731 } else if ('detachEvent' in obj) { 2732 obj.detachEvent('on' + type, handler); 2733 } 2734 2735 obj[eventsKey][id] = null; 2736} 2737 2738// @function stopPropagation(ev: DOMEvent): this 2739// Stop the given event from propagation to parent elements. Used inside the listener functions: 2740// ```js 2741// L.DomEvent.on(div, 'click', function (ev) { 2742// L.DomEvent.stopPropagation(ev); 2743// }); 2744// ``` 2745function stopPropagation(e) { 2746 2747 if (e.stopPropagation) { 2748 e.stopPropagation(); 2749 } else if (e.originalEvent) { // In case of Leaflet event. 2750 e.originalEvent._stopped = true; 2751 } else { 2752 e.cancelBubble = true; 2753 } 2754 skipped(e); 2755 2756 return this; 2757} 2758 2759// @function disableScrollPropagation(el: HTMLElement): this 2760// Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants). 2761function disableScrollPropagation(el) { 2762 addOne(el, 'mousewheel', stopPropagation); 2763 return this; 2764} 2765 2766// @function disableClickPropagation(el: HTMLElement): this 2767// Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`, 2768// `'mousedown'` and `'touchstart'` events (plus browser variants). 2769function disableClickPropagation(el) { 2770 on(el, 'mousedown touchstart dblclick', stopPropagation); 2771 addOne(el, 'click', fakeStop); 2772 return this; 2773} 2774 2775// @function preventDefault(ev: DOMEvent): this 2776// Prevents the default action of the DOM Event `ev` from happening (such as 2777// following a link in the href of the a element, or doing a POST request 2778// with page reload when a `<form>` is submitted). 2779// Use it inside listener functions. 2780function preventDefault(e) { 2781 if (e.preventDefault) { 2782 e.preventDefault(); 2783 } else { 2784 e.returnValue = false; 2785 } 2786 return this; 2787} 2788 2789// @function stop(ev: DOMEvent): this 2790// Does `stopPropagation` and `preventDefault` at the same time. 2791function stop(e) { 2792 preventDefault(e); 2793 stopPropagation(e); 2794 return this; 2795} 2796 2797// @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point 2798// Gets normalized mouse position from a DOM event relative to the 2799// `container` (border excluded) or to the whole page if not specified.
2800function getMousePosition(e, container) { 2801 if (!container) { 2802 return new Point(e.clientX, e.clientY); 2803 } 2804 2805 var scale = getScale(container), 2806 offset = scale.boundingClientRect; // left and top values are in page scale (like the event clientX/Y) 2807 2808 return new Point( 2809 // offset.left/top values are in page scale (like clientX/Y), 2810 // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies). 2811 (e.clientX - offset.left) / scale.x - container.clientLeft, 2812 (e.clientY - offset.top) / scale.y - container.clientTop 2813 ); 2814} 2815 2816// Chrome on Win scrolls double the pixels as in other platforms (see #4538), 2817// and Firefox scrolls device pixels, not CSS pixels 2818var wheelPxFactor = 2819 (win && chrome) ? 2 * window.devicePixelRatio : 2820 gecko ? window.devicePixelRatio : 1; 2821 2822// @function getWheelDelta(ev: DOMEvent): Number 2823// Gets normalized wheel delta from a mousewheel DOM event, in vertical 2824// pixels scrolled (negative if scrolling down). 2825// Events from pointing devices without precise scrolling are mapped to 2826// a best guess of 60 pixels. 2827function getWheelDelta(e) { 2828 return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta 2829 (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels 2830 (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines 2831 (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages 2832 (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events 2833 e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels 2834 (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines 2835 e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages 2836 0; 2837} 2838 2839var skipEvents = {}; 2840 2841function fakeStop(e) { 2842 // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e) 2843 skipEvents[e.type] = true; 2844} 2845 2846function skipped(e) { 2847 var events = skipEvents[e.type]; 2848 // reset when checking, as it's only used in map container and propagates outside of the map 2849 skipEvents[e.type] = false; 2850 return events; 2851} 2852 2853// check if element really left/entered the event target (for mouseenter/mouseleave) 2854function isExternalTarget(el, e) { 2855 2856 var related = e.relatedTarget; 2857 2858 if (!related) { return true; } 2859 2860 try { 2861 while (related && (related !== el)) { 2862 related = related.parentNode; 2863 } 2864 } catch (err) { 2865 return false; 2866 } 2867 return (related !== el); 2868} 2869 2870var lastClick; 2871 2872// this is a horrible workaround for a bug in Android where a single touch triggers two click events 2873function filterClick(e, handler) { 2874 var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)), 2875 elapsed = lastClick && (timeStamp - lastClick); 2876 2877 // are they closer together than 500ms yet more than 100ms? 2878 // Android typically triggers them ~300ms apart while multiple listeners 2879 // on the same event should be triggered far faster; 2880 // or check if click is simulated on the element, and if it is, reject any non-simulated events 2881 2882 if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { 2883 stop(e); 2884 return; 2885 } 2886 lastClick = timeStamp; 2887 2888 handler(e); 2889} 2890 2891 2892 2893 2894var DomEvent = (Object.freeze || Object)({ 2895 on: on, 2896 off: off, 2897 stopPropagation: stopPropagation, 2898 disableScrollPropagation: disableScrollPropagation, 2899 disableClickPropagation: disableClickPropagation, 2900 preventDefault: preventDefault, 2901 stop: stop, 2902 getMousePosition: getMousePosition, 2903 getWheelDelta: getWheelDelta, 2904 fakeStop: fakeStop, 2905 skipped: skipped, 2906 isExternalTarget: isExternalTarget, 2907 addListener: on, 2908 removeListener: off 2909}); 2910 2911/* 2912 * @class PosAnimation 2913 * @aka L.PosAnimation 2914 * @inherits Evented 2915 * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9. 2916 * 2917 * @example 2918 * ```js 2919 * var fx = new L.PosAnimation(); 2920 * fx.run(el, [300, 500], 0.5); 2921 * ``` 2922 * 2923 * @constructor L.PosAnimation() 2924 * Creates a `PosAnimation` object. 2925 * 2926 */ 2927 2928var PosAnimation = Evented.extend({ 2929 2930 // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?:
vendor: 1,697 bytes, lines 2930-2996
2930Number) 2931 // Run an animation of a given element to a new position, optionally setting 2932 // duration in seconds (`0.25` by default) and easing linearity factor (3rd 2933 // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1), 2934 // `0.5` by default). 2935 run: function (el, newPos, duration, easeLinearity) { 2936 this.stop(); 2937 2938 this._el = el; 2939 this._inProgress = true; 2940 this._duration = duration || 0.25; 2941 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2); 2942 2943 this._startPos = getPosition(el); 2944 this._offset = newPos.subtract(this._startPos); 2945 this._startTime = +new Date(); 2946 2947 // @event start: Event 2948 // Fired when the animation starts 2949 this.fire('start'); 2950 2951 this._animate(); 2952 }, 2953 2954 // @method stop() 2955 // Stops the animation (if currently running). 2956 stop: function () { 2957 if (!this._inProgress) { return; } 2958 2959 this._step(true); 2960 this._complete(); 2961 }, 2962 2963 _animate: function () { 2964 // animation loop 2965 this._animId = requestAnimFrame(this._animate, this); 2966 this._step(); 2967 }, 2968 2969 _step: function (round) { 2970 var elapsed = (+new Date()) - this._startTime, 2971 duration = this._duration * 1000; 2972 2973 if (elapsed < duration) { 2974 this._runFrame(this._easeOut(elapsed / duration), round); 2975 } else { 2976 this._runFrame(1); 2977 this._complete(); 2978 } 2979 }, 2980 2981 _runFrame: function (progress, round) { 2982 var pos = this._startPos.add(this._offset.multiplyBy(progress)); 2983 if (round) { 2984 pos._round(); 2985 } 2986 setPosition(this._el, pos); 2987 2988 // @event step: Event 2989 // Fired continuously during the animation. 2990 this.fire('step'); 2991 }, 2992 2993 _complete: function () { 2994 cancelAnimFrame(this._animId); 2995 2996 this._inProgress = false;
2997 // @event end: Event 2998 // Fired when the animation ends. 2999 this.fire('end'); 3000 }, 3001 3002 _easeOut: function (t) { 3003 return 1 - Math.pow(1 - t, this._easeOutPower); 3004 } 3005}); 3006 3007/* 3008 * @class Map 3009 * @aka L.Map 3010 * @inherits Evented 3011 * 3012 * The central class of the API â it is used to create a map on a page and manipulate it. 3013 * 3014 * @example 3015 * 3016 * ```js 3017 * // initialize the map on the "map" div with a given center and zoom 3018 * var map = L.map('map', { 3019 * center: [51.505, -0.09], 3020 * zoom: 13 3021 * }); 3022 * ``` 3023 * 3024 */ 3025 3026var Map = Evented.extend({ 3027 3028 options: { 3029 // @section Map State Options 3030 // @option crs: CRS = L.CRS.EPSG3857 3031 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not 3032 // sure what it means. 3033 crs: EPSG3857, 3034 3035 // @option center: LatLng = undefined 3036 // Initial geographic center of the map 3037 center: undefined, 3038 3039 // @option zoom: Number = undefined 3040 // Initial map zoom level 3041 zoom: undefined, 3042 3043 // @option minZoom: Number = * 3044 // Minimum zoom level of the map. 3045 // If not specified and at least one `GridLayer` or `TileLayer` is in the map, 3046 // the lowest of their `minZoom` options will be used instead. 3047 minZoom: undefined, 3048 3049 // @option maxZoom: Number = * 3050 // Maximum zoom level of the map. 3051 // If not specified and at least one `GridLayer` or `TileLayer` is in the map, 3052 // the highest of their `maxZoom` options will be used instead. 3053 maxZoom: undefined, 3054 3055 // @option layers: Layer[] = [] 3056 // Array of layers that will be added to the map initially 3057 layers: [], 3058 3059 // @option maxBounds: LatLngBounds = null 3060 // When this option is set, the map restricts the view to the given 3061 // geographical bounds, bouncing the user back if the user tries to pan 3062 // outside the view. To set the restriction dynamically, use 3063 // [`setMaxBounds`](#map-setmaxbounds) method. 3064 maxBounds: undefined, 3065 3066 // @option renderer: Renderer = * 3067 // The default method for drawing vector layers on the map. `L.SVG` 3068 // or `L.Canvas` by default depending on browser support. 3069 renderer: undefined, 3070 3071 3072 // @section Animation Options 3073 // @option zoomAnimation: Boolean = true 3074 // Whether the map zoom animation is enabled. By default it's enabled 3075 // in all browsers that support CSS3 Transitions except Android. 3076 zoomAnimation: true, 3077 3078 // @option zoomAnimationThreshold: Number = 4 3079 // Won't animate zoom if the zoom difference exceeds this value. 3080 zoomAnimationThreshold: 4, 3081 3082 // @option fadeAnimation: Boolean = true 3083 // Whether the tile fade animation is enabled. By default it's enabled 3084 // in all browsers that support CSS3 Transitions except Android. 3085 fadeAnimation: true, 3086 3087 // @option markerZoomAnimation: Boolean = true 3088 // Whether markers animate their zoom with the zoom animation, if disabled 3089 // they will disappear for the length of the animation. By default it's 3090 // enabled in all browsers that support CSS3 Transitions except Android. 3091 markerZoomAnimation: true, 3092 3093 // @option transform3DLimit: Number = 2^23 3094 // Defines the maximum size of a CSS translation transform. The default 3095 // value should not be changed unless a web browser positions layers in 3096 // the wrong place after doing a large `panBy`. 3097 transform3DLimit: 8388608, // Precision limit of a 32-bit float 3098 3099 // @section Interaction Options 3100 // @option zoomSnap: Number = 1 3101 // Forces the map's zoom level to always be a multiple of this, particularly 3102 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom. 3103 // By default, the zoom level snaps to the nearest integer; lower values 3104 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0` 3105 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom. 3106 zoomSnap: 1, 3107 3108 // @option zoomDelta: Number = 1 3109 // Controls how much the map's zoom level will change after a 3110 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+` 3111 // or `-` on the keyboard, or using the [zoom controls](#control-zoom). 3112 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity. 3113 zoomDelta: 1, 3114
vendor: 3,639 bytes, lines 3115-3225
3115 // @option trackResize: Boolean = true 3116 // Whether the map automatically handles browser window resize to update itself. 3117 trackResize: true 3118 }, 3119 3120 initialize: function (id, options) { // (HTMLElement or String, Object) 3121 options = setOptions(this, options); 3122 3123 // Make sure to assign internal flags at the beginning, 3124 // to avoid inconsistent state in some edge cases. 3125 this._handlers = []; 3126 this._layers = {}; 3127 this._zoomBoundLayers = {}; 3128 this._sizeChanged = true; 3129 3130 this._initContainer(id); 3131 this._initLayout(); 3132 3133 // hack for https://github.com/Leaflet/Leaflet/issues/1980 3134 this._onResize = bind(this._onResize, this); 3135 3136 this._initEvents(); 3137 3138 if (options.maxBounds) { 3139 this.setMaxBounds(options.maxBounds); 3140 } 3141 3142 if (options.zoom !== undefined) { 3143 this._zoom = this._limitZoom(options.zoom); 3144 } 3145 3146 if (options.center && options.zoom !== undefined) { 3147 this.setView(toLatLng(options.center), options.zoom, {reset: true}); 3148 } 3149 3150 this.callInitHooks(); 3151 3152 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera 3153 this._zoomAnimated = TRANSITION && any3d && !mobileOpera && 3154 this.options.zoomAnimation; 3155 3156 // zoom transitions run with the same duration for all layers, so if one of transitionend events 3157 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally 3158 if (this._zoomAnimated) { 3159 this._createAnimProxy(); 3160 on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this); 3161 } 3162 3163 this._addLayers(this.options.layers); 3164 }, 3165 3166 3167 // @section Methods for modifying map state 3168 3169 // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this 3170 // Sets the view of the map (geographical center and zoom) with the given 3171 // animation options. 3172 setView: function (center, zoom, options) { 3173 3174 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom); 3175 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds); 3176 options = options || {}; 3177 3178 this._stop(); 3179 3180 if (this._loaded && !options.reset && options !== true) { 3181 3182 if (options.animate !== undefined) { 3183 options.zoom = extend({animate: options.animate}, options.zoom); 3184 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan); 3185 } 3186 3187 // try animating pan or zoom 3188 var moved = (this._zoom !== zoom) ? 3189 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) : 3190 this._tryAnimatedPan(center, options.pan); 3191 3192 if (moved) { 3193 // prevent resize handler call, the view will refresh after animation anyway 3194 clearTimeout(this._sizeTimer); 3195 return this; 3196 } 3197 } 3198 3199 // animation didn't start, just reset the map view 3200 this._resetView(center, zoom); 3201 3202 return this; 3203 }, 3204 3205 // @method setZoom(zoom: Number, options?: Zoom/pan options): this 3206 // Sets the zoom of the map. 3207 setZoom: function (zoom, options) { 3208 if (!this._loaded) { 3209 this._zoom = zoom; 3210 return this; 3211 } 3212 return this.setView(this.getCenter(), zoom, {zoom: options}); 3213 }, 3214 3215 // @method zoomIn(delta?: Number, options?: Zoom options): this 3216 // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). 3217 zoomIn: function (delta, options) { 3218 delta = delta || (any3d ? this.options.zoomDelta : 1); 3219 return this.setZoom(this._zoom + delta, options); 3220 }, 3221 3222 // @method zoomOut(delta?: Number, options?: Zoom options): this 3223 // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). 3224 zoomOut: function (delta, options) { 3225 delta = delta || (
vendor: 4,557 bytes, lines 3225-3355
3225any3d ? this.options.zoomDelta : 1); 3226 return this.setZoom(this._zoom - delta, options); 3227 }, 3228 3229 // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this 3230 // Zooms the map while keeping a specified geographical point on the map 3231 // stationary (e.g. used internally for scroll zoom and double-click zoom). 3232 // @alternative 3233 // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this 3234 // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary. 3235 setZoomAround: function (latlng, zoom, options) { 3236 var scale = this.getZoomScale(zoom), 3237 viewHalf = this.getSize().divideBy(2), 3238 containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng), 3239 3240 centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale), 3241 newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); 3242 3243 return this.setView(newCenter, zoom, {zoom: options}); 3244 }, 3245 3246 _getBoundsCenterZoom: function (bounds, options) { 3247 3248 options = options || {}; 3249 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds); 3250 3251 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), 3252 paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), 3253 3254 zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); 3255 3256 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom; 3257 3258 if (zoom === Infinity) { 3259 return { 3260 center: bounds.getCenter(), 3261 zoom: zoom 3262 }; 3263 } 3264 3265 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), 3266 3267 swPoint = this.project(bounds.getSouthWest(), zoom), 3268 nePoint = this.project(bounds.getNorthEast(), zoom), 3269 center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom); 3270 3271 return { 3272 center: center, 3273 zoom: zoom 3274 }; 3275 }, 3276 3277 // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this 3278 // Sets a map view that contains the given geographical bounds with the 3279 // maximum zoom level possible. 3280 fitBounds: function (bounds, options) { 3281 3282 bounds = toLatLngBounds(bounds); 3283 3284 if (!bounds.isValid()) { 3285 throw new Error('Bounds are not valid.'); 3286 } 3287 3288 var target = this._getBoundsCenterZoom(bounds, options); 3289 return this.setView(target.center, target.zoom, options); 3290 }, 3291 3292 // @method fitWorld(options?: fitBounds options): this 3293 // Sets a map view that mostly contains the whole world with the maximum 3294 // zoom level possible. 3295 fitWorld: function (options) { 3296 return this.fitBounds([[-90, -180], [90, 180]], options); 3297 }, 3298 3299 // @method panTo(latlng: LatLng, options?: Pan options): this 3300 // Pans the map to a given center. 3301 panTo: function (center, options) { // (LatLng) 3302 return this.setView(center, this._zoom, {pan: options}); 3303 }, 3304 3305 // @method panBy(offset: Point, options?: Pan options): this 3306 // Pans the map by a given number of pixels (animated). 3307 panBy: function (offset, options) { 3308 offset = toPoint(offset).round(); 3309 options = options || {}; 3310 3311 if (!offset.x && !offset.y) { 3312 return this.fire('moveend'); 3313 } 3314 // If we pan too far, Chrome gets issues with tiles 3315 // and makes them disappear or appear in the wrong place (slightly offset) #2602 3316 if (options.animate !== true && !this.getSize().contains(offset)) { 3317 this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); 3318 return this; 3319 } 3320 3321 if (!this._panAnim) { 3322 this._panAnim = new PosAnimation(); 3323 3324 this._panAnim.on({ 3325 'step': this._onPanTransitionStep, 3326 'end': this._onPanTransitionEnd 3327 }, this); 3328 } 3329 3330 // don't fire movestart if animating inertia 3331 if (!options.noMoveStart) { 3332 this.fire('movestart'); 3333 } 3334 3335 // animate pan unless animate: false specified 3336 if (options.animate !== false) { 3337 addClass(this._mapPane, 'leaflet-pan-anim'); 3338 3339 var newPos = this._getMapPanePos().subtract(offset).round(); 3340 this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity); 3341 } else { 3342 this._rawPanBy(offset); 3343 this.fire('move').fire('moveend'); 3344 } 3345 3346 return this; 3347 }, 3348 3349 // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this 3350 // Sets the view of the map (geographical center and zoom) performing a smooth 3351 // pan-zoom animation. 3352 flyTo: function (targetCenter, targetZoom, options) { 3353 3354 options = options || {}; 3355 if (options.animate === false || !
vendor: 1,654 bytes, lines 3355-3409
3355any3d) { 3356 return this.setView(targetCenter, targetZoom, options); 3357 } 3358 3359 this._stop(); 3360 3361 var from = this.project(this.getCenter()), 3362 to = this.project(targetCenter), 3363 size = this.getSize(), 3364 startZoom = this._zoom; 3365 3366 targetCenter = toLatLng(targetCenter); 3367 targetZoom = targetZoom === undefined ? startZoom : targetZoom; 3368 3369 var w0 = Math.max(size.x, size.y), 3370 w1 = w0 * this.getZoomScale(startZoom, targetZoom), 3371 u1 = (to.distanceTo(from)) || 1, 3372 rho = 1.42, 3373 rho2 = rho * rho; 3374 3375 function r(i) { 3376 var s1 = i ? -1 : 1, 3377 s2 = i ? w1 : w0, 3378 t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1, 3379 b1 = 2 * s2 * rho2 * u1, 3380 b = t1 / b1, 3381 sq = Math.sqrt(b * b + 1) - b; 3382 3383 // workaround for floating point precision bug when sq = 0, log = -Infinite, 3384 // thus triggering an infinite loop in flyTo 3385 var log = sq < 0.000000001 ? -18 : Math.log(sq); 3386 3387 return log; 3388 } 3389 3390 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; } 3391 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; } 3392 function tanh(n) { return sinh(n) / cosh(n); } 3393 3394 var r0 = r(0); 3395 3396 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); } 3397 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; } 3398 3399 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); } 3400 3401 var start = Date.now(), 3402 S = (r(1) - r0) / rho, 3403 duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8; 3404 3405 function frame() { 3406 var t = (Date.now() - start) / duration, 3407 s = easeOut(t) * S; 3408 3409 if (t <= 1) {
3410 this._flyToFrame = requestAnimFrame(frame, this); 3411 3412 this._move( 3413 this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom), 3414 this.getScaleZoom(w0 / w(s), startZoom), 3415 {flyTo: true}); 3416 3417 } else { 3418 this 3419 ._move(targetCenter, targetZoom) 3420 ._moveEnd(true); 3421 } 3422 } 3423 3424 this._moveStart(true, options.noMoveStart); 3425 3426 frame.call(this); 3427 return this; 3428 }, 3429 3430 // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this 3431 // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto), 3432 // but takes a bounds parameter like [`fitBounds`](#map-fitbounds). 3433 flyToBounds: function (bounds, options) { 3434 var target = this._getBoundsCenterZoom(bounds, options); 3435 return this.flyTo(target.center, target.zoom, options); 3436 }, 3437 3438 // @method setMaxBounds(bounds: Bounds): this 3439 // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option). 3440 setMaxBounds: function (bounds) { 3441 bounds = toLatLngBounds(bounds); 3442 3443 if (!bounds.isValid()) { 3444 this.options.maxBounds = null; 3445 return this.off('moveend', this._panInsideMaxBounds); 3446 } else if (this.options.maxBounds) { 3447 this.off('moveend', this._panInsideMaxBounds); 3448 } 3449 3450 this.options.maxBounds = bounds; 3451 3452 if (this._loaded) { 3453 this._panInsideMaxBounds(); 3454 } 3455 3456 return this.on('moveend', this._panInsideMaxBounds); 3457 }, 3458 3459 // @method setMinZoom(zoom: Number): this 3460 // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option). 3461 setMinZoom: function (zoom) { 3462 var oldZoom = this.options.minZoom; 3463 this.options.minZoom = zoom; 3464 3465 if (this._loaded && oldZoom !== zoom) { 3466 this.fire('zoomlevelschange'); 3467 3468 if (this.getZoom() < this.options.minZoom) { 3469 return this.setZoom(zoom); 3470 } 3471 } 3472 3473 return this; 3474 }, 3475 3476 // @method setMaxZoom(zoom: Number): this 3477 // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option). 3478 setMaxZoom: function (zoom) { 3479 var oldZoom = this.options.maxZoom; 3480 this.options.maxZoom = zoom; 3481 3482 if (this._loaded && oldZoom !== zoom) { 3483 this.fire('zoomlevelschange'); 3484 3485 if (this.getZoom() > this.options.maxZoom) { 3486 return this.setZoom(zoom); 3487 } 3488 } 3489 3490 return this; 3491 }, 3492 3493 // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this 3494 // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any. 3495 panInsideBounds: function (bounds, options) { 3496 this._enforcingBounds = true; 3497 var center = this.getCenter(), 3498 newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds)); 3499 3500 if (!center.equals(newCenter)) { 3501 this.panTo(newCenter, options); 3502 } 3503 3504 this._enforcingBounds = false; 3505 return this; 3506 }, 3507 3508 // @method panInside(latlng: LatLng, options?: options): this 3509 // Pans the map the minimum amount to make the `latlng` visible. Use 3510 // `padding`, `paddingTopLeft` and `paddingTopRight` options to fit 3511 // the display to more restricted bounds, like [`fitBounds`](#map-fitbounds). 3512 // If `latlng` is already within the (optionally padded) display bounds, 3513 // the map will not be panned. 3514 panInside: function (latlng, options) { 3515 options = options || {}; 3516 3517 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), 3518 paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), 3519 center = this.getCenter(), 3520 pixelCenter = this.project(center), 3521 pixelPoint = this.project(latlng), 3522 pixelBounds = this.getPixelBounds(), 3523 halfPixelBounds = pixelBounds.getSize().divideBy(2), 3524 paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]); 3525 3526 if (!paddedBounds.contains(pixelPoint)) { 3527 this._enforcingBounds = true; 3528 var diff = pixelCenter.subtract(pixelPoint), 3529 newCenter = toPoint(pixelPoint.x + diff.x, pixelPoint.y + diff.y); 3530 3531 if (pixelPoint.x < paddedBounds.min.x || pixelPoint.x > paddedBounds.max.x) { 3532 newCenter.x = pixelCenter.x - diff.x; 3533 if (diff.x > 0) { 3534 newCenter.x += halfPixelBounds.x - paddingTL.x; 3535 } else { 3536 newCenter.x -= halfPixelBounds.x - paddingBR.x; 3537 } 3538 } 3539 if (pixelPoint.y < paddedBounds.min.y || pixelPoint.y > paddedBounds.max.y) { 3540 newCenter.y = pixelCenter.y - diff.y; 3541 if (diff.y > 0) { 3542 newCenter.y += halfPixelBounds.y - paddingTL.y; 3543 } else { 3544 newCenter.y -= halfPixelBounds.y - paddingBR.y; 3545 } 3546 } 3547 this.panTo(this.unproject(new
vendor: 4,163 bytes, lines 3547-3678
3547Center), options); 3548 this._enforcingBounds = false; 3549 } 3550 return this; 3551 }, 3552 3553 // @method invalidateSize(options: Zoom/pan options): this 3554 // Checks if the map container size changed and updates the map if so â 3555 // call it after you've changed the map size dynamically, also animating 3556 // pan by default. If `options.pan` is `false`, panning will not occur. 3557 // If `options.debounceMoveend` is `true`, it will delay `moveend` event so 3558 // that it doesn't happen often even if the method is called many 3559 // times in a row. 3560 3561 // @alternative 3562 // @method invalidateSize(animate: Boolean): this 3563 // Checks if the map container size changed and updates the map if so â 3564 // call it after you've changed the map size dynamically, also animating 3565 // pan by default. 3566 invalidateSize: function (options) { 3567 if (!this._loaded) { return this; } 3568 3569 options = extend({ 3570 animate: false, 3571 pan: true 3572 }, options === true ? {animate: true} : options); 3573 3574 var oldSize = this.getSize(); 3575 this._sizeChanged = true; 3576 this._lastCenter = null; 3577 3578 var newSize = this.getSize(), 3579 oldCenter = oldSize.divideBy(2).round(), 3580 newCenter = newSize.divideBy(2).round(), 3581 offset = oldCenter.subtract(newCenter); 3582 3583 if (!offset.x && !offset.y) { return this; } 3584 3585 if (options.animate && options.pan) { 3586 this.panBy(offset); 3587 3588 } else { 3589 if (options.pan) { 3590 this._rawPanBy(offset); 3591 } 3592 3593 this.fire('move'); 3594 3595 if (options.debounceMoveend) { 3596 clearTimeout(this._sizeTimer); 3597 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200); 3598 } else { 3599 this.fire('moveend'); 3600 } 3601 } 3602 3603 // @section Map state change events 3604 // @event resize: ResizeEvent 3605 // Fired when the map is resized. 3606 return this.fire('resize', { 3607 oldSize: oldSize, 3608 newSize: newSize 3609 }); 3610 }, 3611 3612 // @section Methods for modifying map state 3613 // @method stop(): this 3614 // Stops the currently running `panTo` or `flyTo` animation, if any. 3615 stop: function () { 3616 this.setZoom(this._limitZoom(this._zoom)); 3617 if (!this.options.zoomSnap) { 3618 this.fire('viewreset'); 3619 } 3620 return this._stop(); 3621 }, 3622 3623 // @section Geolocation methods 3624 // @method locate(options?: Locate options): this 3625 // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound) 3626 // event with location data on success or a [`locationerror`](#map-locationerror) event on failure, 3627 // and optionally sets the map view to the user's location with respect to 3628 // detection accuracy (or to the world view if geolocation failed). 3629 // Note that, if your page doesn't use HTTPS, this method will fail in 3630 // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins)) 3631 // See `Locate options` for more details. 3632 locate: function (options) { 3633 3634 options = this._locateOptions = extend({ 3635 timeout: 10000, 3636 watch: false 3637 // setView: false 3638 // maxZoom: <Number> 3639 // maximumAge: 0 3640 // enableHighAccuracy: false 3641 }, options); 3642 3643 if (!('geolocation' in navigator)) { 3644 this._handleGeolocationError({ 3645 code: 0, 3646 message: 'Geolocation not supported.' 3647 }); 3648 return this; 3649 } 3650 3651 var onResponse = bind(this._handleGeolocationResponse, this), 3652 onError = bind(this._handleGeolocationError, this); 3653 3654 if (options.watch) { 3655 this._locationWatchId = 3656 navigator.geolocation.watchPosition(onResponse, onError, options); 3657 } else { 3658 navigator.geolocation.getCurrentPosition(onResponse, onError, options); 3659 } 3660 return this; 3661 }, 3662 3663 // @method stopLocate(): this 3664 // Stops watching location previously initiated by `map.locate({watch: true})` 3665 // and aborts resetting the map view if map.locate was called with 3666 // `{setView: true}`. 3667 stopLocate: function () { 3668 if (navigator.geolocation && navigator.geolocation.clearWatch) { 3669 navigator.geolocation.clearWatch(this._locationWatchId); 3670 } 3671 if (this._locateOptions) { 3672 this._locateOptions.setView = false; 3673 } 3674 return this; 3675 }, 3676 3677 _handleGeolocationError: function (error) { 3678
vendor: 5,993 bytes, lines 3678-3880
3678var c = error.code, 3679 message = error.message || 3680 (c === 1 ? 'permission denied' : 3681 (c === 2 ? 'position unavailable' : 'timeout')); 3682 3683 if (this._locateOptions.setView && !this._loaded) { 3684 this.fitWorld(); 3685 } 3686 3687 // @section Location events 3688 // @event locationerror: ErrorEvent 3689 // Fired when geolocation (using the [`locate`](#map-locate) method) failed. 3690 this.fire('locationerror', { 3691 code: c, 3692 message: 'Geolocation error: ' + message + '.' 3693 }); 3694 }, 3695 3696 _handleGeolocationResponse: function (pos) { 3697 var lat = pos.coords.latitude, 3698 lng = pos.coords.longitude, 3699 latlng = new LatLng(lat, lng), 3700 bounds = latlng.toBounds(pos.coords.accuracy * 2), 3701 options = this._locateOptions; 3702 3703 if (options.setView) { 3704 var zoom = this.getBoundsZoom(bounds); 3705 this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom); 3706 } 3707 3708 var data = { 3709 latlng: latlng, 3710 bounds: bounds, 3711 timestamp: pos.timestamp 3712 }; 3713 3714 for (var i in pos.coords) { 3715 if (typeof pos.coords[i] === 'number') { 3716 data[i] = pos.coords[i]; 3717 } 3718 } 3719 3720 // @event locationfound: LocationEvent 3721 // Fired when geolocation (using the [`locate`](#map-locate) method) 3722 // went successfully. 3723 this.fire('locationfound', data); 3724 }, 3725 3726 // TODO Appropriate docs section? 3727 // @section Other Methods 3728 // @method addHandler(name: String, HandlerClass: Function): this 3729 // Adds a new `Handler` to the map, given its name and constructor function. 3730 addHandler: function (name, HandlerClass) { 3731 if (!HandlerClass) { return this; } 3732 3733 var handler = this[name] = new HandlerClass(this); 3734 3735 this._handlers.push(handler); 3736 3737 if (this.options[name]) { 3738 handler.enable(); 3739 } 3740 3741 return this; 3742 }, 3743 3744 // @method remove(): this 3745 // Destroys the map and clears all related event listeners. 3746 remove: function () { 3747 3748 this._initEvents(true); 3749 3750 if (this._containerId !== this._container._leaflet_id) { 3751 throw new Error('Map container is being reused by another instance'); 3752 } 3753 3754 try { 3755 // throws error in IE6-8 3756 delete this._container._leaflet_id; 3757 delete this._containerId; 3758 } catch (e) { 3759 /*eslint-disable */ 3760 this._container._leaflet_id = undefined; 3761 /* eslint-enable */ 3762 this._containerId = undefined; 3763 } 3764 3765 if (this._locationWatchId !== undefined) { 3766 this.stopLocate(); 3767 } 3768 3769 this._stop(); 3770 3771 remove(this._mapPane); 3772 3773 if (this._clearControlPos) { 3774 this._clearControlPos(); 3775 } 3776 if (this._resizeRequest) { 3777 cancelAnimFrame(this._resizeRequest); 3778 this._resizeRequest = null; 3779 } 3780 3781 this._clearHandlers(); 3782 3783 if (this._loaded) { 3784 // @section Map state change events 3785 // @event unload: Event 3786 // Fired when the map is destroyed with [remove](#map-remove) method. 3787 this.fire('unload'); 3788 } 3789 3790 var i; 3791 for (i in this._layers) { 3792 this._layers[i].remove(); 3793 } 3794 for (i in this._panes) { 3795 remove(this._panes[i]); 3796 } 3797 3798 this._layers = []; 3799 this._panes = []; 3800 delete this._mapPane; 3801 delete this._renderer; 3802 3803 return this; 3804 }, 3805 3806 // @section Other Methods 3807 // @method createPane(name: String, container?: HTMLElement): HTMLElement 3808 // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already, 3809 // then returns it. The pane is created as a child of `container`, or 3810 // as a child of the main map pane if not set. 3811 createPane: function (name, container) { 3812 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''), 3813 pane = create$1('div', className, container || this._mapPane); 3814 3815 if (name) { 3816 this._panes[name] = pane; 3817 } 3818 return pane; 3819 }, 3820 3821 // @section Methods for Getting Map State 3822 3823 // @method getCenter(): LatLng 3824 // Returns the geographical center of the map view 3825 getCenter: function () { 3826 this._checkIfLoaded(); 3827 3828 if (this._lastCenter && !this._moved()) { 3829 return this._lastCenter; 3830 } 3831 return this.layerPointToLatLng(this._getCenterLayerPoint()); 3832 }, 3833 3834 // @method getZoom(): Number 3835 // Returns the current zoom level of the map view 3836 getZoom: function () { 3837 return this._zoom; 3838 }, 3839 3840 // @method getBounds(): LatLngBounds 3841 // Returns the geographical bounds visible in the current map view 3842 getBounds: function () { 3843 var bounds = this.getPixelBounds(), 3844 sw = this.unproject(bounds.getBottomLeft()), 3845 ne = this.unproject(bounds.getTopRight()); 3846 3847 return new LatLngBounds(sw, ne); 3848 }, 3849 3850 // @method getMinZoom(): Number 3851 // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default. 3852 getMinZoom: function () { 3853 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom; 3854 }, 3855 3856 // @method getMaxZoom(): Number 3857 // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers). 3858 getMaxZoom: function () { 3859 return this.options.maxZoom === undefined ? 3860 (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) : 3861 this.options.maxZoom; 3862 }, 3863 3864 // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number 3865 // Returns the maximum zoom level on which the given bounds fit to the map 3866 // view in its entirety. If `inside` (optional) is set to `true`, the method 3867 // instead returns the minimum zoom level on which the map view fits into 3868 // the given bounds in its entirety. 3869 getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number 3870 bounds = toLatLngBounds(bounds); 3871 padding = toPoint(padding || [0, 0]); 3872 3873 var zoom = this.getZoom() || 0, 3874 min = this.getMinZoom(), 3875 max = this.getMaxZoom(), 3876 nw = bounds.getNorthWest(), 3877 se = bounds.getSouthEast(), 3878 size = this.getSize().subtract(padding), 3879 boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(), 3880 snap =
vendor: 9,204 bytes, lines 3880-4108
3880any3d ? this.options.zoomSnap : 1, 3881 scalex = size.x / boundsSize.x, 3882 scaley = size.y / boundsSize.y, 3883 scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley); 3884 3885 zoom = this.getScaleZoom(scale, zoom); 3886 3887 if (snap) { 3888 zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level 3889 zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap; 3890 } 3891 3892 return Math.max(min, Math.min(max, zoom)); 3893 }, 3894 3895 // @method getSize(): Point 3896 // Returns the current size of the map container (in pixels). 3897 getSize: function () { 3898 if (!this._size || this._sizeChanged) { 3899 this._size = new Point( 3900 this._container.clientWidth || 0, 3901 this._container.clientHeight || 0); 3902 3903 this._sizeChanged = false; 3904 } 3905 return this._size.clone(); 3906 }, 3907 3908 // @method getPixelBounds(): Bounds 3909 // Returns the bounds of the current map view in projected pixel 3910 // coordinates (sometimes useful in layer and overlay implementations). 3911 getPixelBounds: function (center, zoom) { 3912 var topLeftPoint = this._getTopLeftPoint(center, zoom); 3913 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); 3914 }, 3915 3916 // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to 3917 // the map pane? "left point of the map layer" can be confusing, specially 3918 // since there can be negative offsets. 3919 // @method getPixelOrigin(): Point 3920 // Returns the projected pixel coordinates of the top left point of 3921 // the map layer (useful in custom layer and overlay implementations). 3922 getPixelOrigin: function () { 3923 this._checkIfLoaded(); 3924 return this._pixelOrigin; 3925 }, 3926 3927 // @method getPixelWorldBounds(zoom?: Number): Bounds 3928 // Returns the world's bounds in pixel coordinates for zoom level `zoom`. 3929 // If `zoom` is omitted, the map's current zoom level is used. 3930 getPixelWorldBounds: function (zoom) { 3931 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom); 3932 }, 3933 3934 // @section Other Methods 3935 3936 // @method getPane(pane: String|HTMLElement): HTMLElement 3937 // Returns a [map pane](#map-pane), given its name or its HTML element (its identity). 3938 getPane: function (pane) { 3939 return typeof pane === 'string' ? this._panes[pane] : pane; 3940 }, 3941 3942 // @method getPanes(): Object 3943 // Returns a plain object containing the names of all [panes](#map-pane) as keys and 3944 // the panes as values. 3945 getPanes: function () { 3946 return this._panes; 3947 }, 3948 3949 // @method getContainer: HTMLElement 3950 // Returns the HTML element that contains the map. 3951 getContainer: function () { 3952 return this._container; 3953 }, 3954 3955 3956 // @section Conversion Methods 3957 3958 // @method getZoomScale(toZoom: Number, fromZoom: Number): Number 3959 // Returns the scale factor to be applied to a map transition from zoom level 3960 // `fromZoom` to `toZoom`. Used internally to help with zoom animations. 3961 getZoomScale: function (toZoom, fromZoom) { 3962 // TODO replace with universal implementation after refactoring projections 3963 var crs = this.options.crs; 3964 fromZoom = fromZoom === undefined ? this._zoom : fromZoom; 3965 return crs.scale(toZoom) / crs.scale(fromZoom); 3966 }, 3967 3968 // @method getScaleZoom(scale: Number, fromZoom: Number): Number 3969 // Returns the zoom level that the map would end up at, if it is at `fromZoom` 3970 // level and everything is scaled by a factor of `scale`. Inverse of 3971 // [`getZoomScale`](#map-getZoomScale). 3972 getScaleZoom: function (scale, fromZoom) { 3973 var crs = this.options.crs; 3974 fromZoom = fromZoom === undefined ? this._zoom : fromZoom; 3975 var zoom = crs.zoom(scale * crs.scale(fromZoom)); 3976 return isNaN(zoom) ? Infinity : zoom; 3977 }, 3978 3979 // @method project(latlng: LatLng, zoom: Number): Point 3980 // Projects a geographical coordinate `LatLng` according to the projection 3981 // of the map's CRS, then scales it according to `zoom` and the CRS's 3982 // `Transformation`. The result is pixel coordinate relative to 3983 // the CRS origin. 3984 project: function (latlng, zoom) { 3985 zoom = zoom === undefined ? this._zoom : zoom; 3986 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom); 3987 }, 3988 3989 // @method unproject(point: Point, zoom: Number): LatLng 3990 // Inverse of [`project`](#map-project). 3991 unproject: function (point, zoom) { 3992 zoom = zoom === undefined ? this._zoom : zoom; 3993 return this.options.crs.pointToLatLng(toPoint(point), zoom); 3994 }, 3995 3996 // @method layerPointToLatLng(point: Point): LatLng 3997 // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), 3998 // returns the corresponding geographical coordinate (for the current zoom level). 3999 layerPointToLatLng: function (point) { 4000 var projectedPoint = toPoint(point).add(this.getPixelOrigin()); 4001 return this.unproject(projectedPoint); 4002 }, 4003 4004 // @method latLngToLayerPoint(latlng: LatLng): Point 4005 // Given a geographical coordinate, returns the corresponding pixel coordinate 4006 // relative to the [origin pixel](#map-getpixelorigin). 4007 latLngToLayerPoint: function (latlng) { 4008 var projectedPoint = this.project(toLatLng(latlng))._round(); 4009 return projectedPoint._subtract(this.getPixelOrigin()); 4010 }, 4011 4012 // @method wrapLatLng(latlng: LatLng): LatLng 4013 // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the 4014 // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the 4015 // CRS's bounds. 4016 // By default this means longitude is wrapped around the dateline so its 4017 // value is between -180 and +180 degrees. 4018 wrapLatLng: function (latlng) { 4019 return this.options.crs.wrapLatLng(toLatLng(latlng)); 4020 }, 4021 4022 // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds 4023 // Returns a `LatLngBounds` with the same size as the given one, ensuring that 4024 // its center is within the CRS's bounds. 4025 // By default this means the center longitude is wrapped around the dateline so its 4026 // value is between -180 and +180 degrees, and the majority of the bounds 4027 // overlaps the CRS's bounds. 4028 wrapLatLngBounds: function (latlng) { 4029 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng)); 4030 }, 4031 4032 // @method distance(latlng1: LatLng, latlng2: LatLng): Number 4033 // Returns the distance between two geographical coordinates according to 4034 // the map's CRS. By default this measures distance in meters. 4035 distance: function (latlng1, latlng2) { 4036 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2)); 4037 }, 4038 4039 // @method containerPointToLayerPoint(point: Point): Point 4040 // Given a pixel coordinate relative to the map container, returns the corresponding 4041 // pixel coordinate relative to the [origin pixel](#map-getpixelorigin). 4042 containerPointToLayerPoint: function (point) { // (Point) 4043 return toPoint(point).subtract(this._getMapPanePos()); 4044 }, 4045 4046 // @method layerPointToContainerPoint(point: Point): Point 4047 // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), 4048 // returns the corresponding pixel coordinate relative to the map container. 4049 layerPointToContainerPoint: function (point) { // (Point) 4050 return toPoint(point).add(this._getMapPanePos()); 4051 }, 4052 4053 // @method containerPointToLatLng(point: Point): LatLng 4054 // Given a pixel coordinate relative to the map container, returns 4055 // the corresponding geographical coordinate (for the current zoom level). 4056 containerPointToLatLng: function (point) { 4057 var layerPoint = this.containerPointToLayerPoint(toPoint(point)); 4058 return this.layerPointToLatLng(layerPoint); 4059 }, 4060 4061 // @method latLngToContainerPoint(latlng: LatLng): Point 4062 // Given a geographical coordinate, returns the corresponding pixel coordinate 4063 // relative to the map container. 4064 latLngToContainerPoint: function (latlng) { 4065 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng))); 4066 }, 4067 4068 // @method mouseEventToContainerPoint(ev: MouseEvent): Point 4069 // Given a MouseEvent object, returns the pixel coordinate relative to the 4070 // map container where the event took place. 4071 mouseEventToContainerPoint: function (e) { 4072 return getMousePosition(e, this._container); 4073 }, 4074 4075 // @method mouseEventToLayerPoint(ev: MouseEvent): Point 4076 // Given a MouseEvent object, returns the pixel coordinate relative to 4077 // the [origin pixel](#map-getpixelorigin) where the event took place. 4078 mouseEventToLayerPoint: function (e) { 4079 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); 4080 }, 4081 4082 // @method mouseEventToLatLng(ev: MouseEvent): LatLng 4083 // Given a MouseEvent object, returns geographical coordinate where the 4084 // event took place. 4085 mouseEventToLatLng: function (e) { // (MouseEvent) 4086 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); 4087 }, 4088 4089 4090 // map initialization methods 4091 4092 _initContainer: function (id) { 4093 var container = this._container = get(id); 4094 4095 if (!container) { 4096 throw new Error('Map container not found.'); 4097 } else if (container._leaflet_id) { 4098 throw new Error('Map container is already initialized.'); 4099 } 4100 4101 on(container, 'scroll', this._onScroll, this); 4102 this._containerId = stamp(container); 4103 }, 4104 4105 _initLayout: function () { 4106 var container = this._container; 4107 4108 this._fadeAnimated = this.options.fadeAnimation &&
vendor: 23,539 bytes, lines 4108-4895
4108any3d; 4109 4110 addClass(container, 'leaflet-container' + 4111 (touch ? ' leaflet-touch' : '') + 4112 (retina ? ' leaflet-retina' : '') + 4113 (ielt9 ? ' leaflet-oldie' : '') + 4114 (safari ? ' leaflet-safari' : '') + 4115 (this._fadeAnimated ? ' leaflet-fade-anim' : '')); 4116 4117 var position = getStyle(container, 'position'); 4118 4119 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') { 4120 container.style.position = 'relative'; 4121 } 4122 4123 this._initPanes(); 4124 4125 if (this._initControlPos) { 4126 this._initControlPos(); 4127 } 4128 }, 4129 4130 _initPanes: function () { 4131 var panes = this._panes = {}; 4132 this._paneRenderers = {}; 4133 4134 // @section 4135 // 4136 // Panes are DOM elements used to control the ordering of layers on the map. You 4137 // can access panes with [`map.getPane`](#map-getpane) or 4138 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the 4139 // [`map.createPane`](#map-createpane) method. 4140 // 4141 // Every map has the following default panes that differ only in zIndex. 4142 // 4143 // @pane mapPane: HTMLElement = 'auto' 4144 // Pane that contains all other map panes 4145 4146 this._mapPane = this.createPane('mapPane', this._container); 4147 setPosition(this._mapPane, new Point(0, 0)); 4148 4149 // @pane tilePane: HTMLElement = 200 4150 // Pane for `GridLayer`s and `TileLayer`s 4151 this.createPane('tilePane'); 4152 // @pane overlayPane: HTMLElement = 400 4153 // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s 4154 this.createPane('shadowPane'); 4155 // @pane shadowPane: HTMLElement = 500 4156 // Pane for overlay shadows (e.g. `Marker` shadows) 4157 this.createPane('overlayPane'); 4158 // @pane markerPane: HTMLElement = 600 4159 // Pane for `Icon`s of `Marker`s 4160 this.createPane('markerPane'); 4161 // @pane tooltipPane: HTMLElement = 650 4162 // Pane for `Tooltip`s. 4163 this.createPane('tooltipPane'); 4164 // @pane popupPane: HTMLElement = 700 4165 // Pane for `Popup`s. 4166 this.createPane('popupPane'); 4167 4168 if (!this.options.markerZoomAnimation) { 4169 addClass(panes.markerPane, 'leaflet-zoom-hide'); 4170 addClass(panes.shadowPane, 'leaflet-zoom-hide'); 4171 } 4172 }, 4173 4174 4175 // private methods that modify map state 4176 4177 // @section Map state change events 4178 _resetView: function (center, zoom) { 4179 setPosition(this._mapPane, new Point(0, 0)); 4180 4181 var loading = !this._loaded; 4182 this._loaded = true; 4183 zoom = this._limitZoom(zoom); 4184 4185 this.fire('viewprereset'); 4186 4187 var zoomChanged = this._zoom !== zoom; 4188 this 4189 ._moveStart(zoomChanged, false) 4190 ._move(center, zoom) 4191 ._moveEnd(zoomChanged); 4192 4193 // @event viewreset: Event 4194 // Fired when the map needs to redraw its content (this usually happens 4195 // on map zoom or load). Very useful for creating custom overlays. 4196 this.fire('viewreset'); 4197 4198 // @event load: Event 4199 // Fired when the map is initialized (when its center and zoom are set 4200 // for the first time). 4201 if (loading) { 4202 this.fire('load'); 4203 } 4204 }, 4205 4206 _moveStart: function (zoomChanged, noMoveStart) { 4207 // @event zoomstart: Event 4208 // Fired when the map zoom is about to change (e.g. before zoom animation). 4209 // @event movestart: Event 4210 // Fired when the view of the map starts changing (e.g. user starts dragging the map). 4211 if (zoomChanged) { 4212 this.fire('zoomstart'); 4213 } 4214 if (!noMoveStart) { 4215 this.fire('movestart'); 4216 } 4217 return this; 4218 }, 4219 4220 _move: function (center, zoom, data) { 4221 if (zoom === undefined) { 4222 zoom = this._zoom; 4223 } 4224 var zoomChanged = this._zoom !== zoom; 4225 4226 this._zoom = zoom; 4227 this._lastCenter = center; 4228 this._pixelOrigin = this._getNewPixelOrigin(center); 4229 4230 // @event zoom: Event 4231 // Fired repeatedly during any change in zoom level, including zoom 4232 // and fly animations. 4233 if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530 4234 this.fire('zoom', data); 4235 } 4236 4237 // @event move: Event 4238 // Fired repeatedly during any movement of the map, including pan and 4239 // fly animations. 4240 return this.fire('move', data); 4241 }, 4242 4243 _moveEnd: function (zoomChanged) { 4244 // @event zoomend: Event 4245 // Fired when the map has changed, after any animations. 4246 if (zoomChanged) { 4247 this.fire('zoomend'); 4248 } 4249 4250 // @event moveend: Event 4251 // Fired when the center of the map stops changing (e.g. user stopped 4252 // dragging the map). 4253 return this.fire('moveend'); 4254 }, 4255 4256 _stop: function () { 4257 cancelAnimFrame(this._flyToFrame); 4258 if (this._panAnim) { 4259 this._panAnim.stop(); 4260 } 4261 return this; 4262 }, 4263 4264 _rawPanBy: function (offset) { 4265 setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); 4266 }, 4267 4268 _getZoomSpan: function () { 4269 return this.getMaxZoom() - this.getMinZoom(); 4270 }, 4271 4272 _panInsideMaxBounds: function () { 4273 if (!this._enforcingBounds) { 4274 this.panInsideBounds(this.options.maxBounds); 4275 } 4276 }, 4277 4278 _checkIfLoaded: function () { 4279 if (!this._loaded) { 4280 throw new Error('Set map center and zoom first.'); 4281 } 4282 }, 4283 4284 // DOM event handling 4285 4286 // @section Interaction events 4287 _initEvents: function (remove$$1) { 4288 this._targets = {}; 4289 this._targets[stamp(this._container)] = this; 4290 4291 var onOff = remove$$1 ? off : on; 4292 4293 // @event click: MouseEvent 4294 // Fired when the user clicks (or taps) the map. 4295 // @event dblclick: MouseEvent 4296 // Fired when the user double-clicks (or double-taps) the map. 4297 // @event mousedown: MouseEvent 4298 // Fired when the user pushes the mouse button on the map. 4299 // @event mouseup: MouseEvent 4300 // Fired when the user releases the mouse button on the map. 4301 // @event mouseover: MouseEvent 4302 // Fired when the mouse enters the map. 4303 // @event mouseout: MouseEvent 4304 // Fired when the mouse leaves the map. 4305 // @event mousemove: MouseEvent 4306 // Fired while the mouse moves over the map. 4307 // @event contextmenu: MouseEvent 4308 // Fired when the user pushes the right mouse button on the map, prevents 4309 // default browser context menu from showing if there are listeners on 4310 // this event. Also fired on mobile when the user holds a single touch 4311 // for a second (also called long press). 4312 // @event keypress: KeyboardEvent 4313 // Fired when the user presses a key from the keyboard while the map is focused. 4314 onOff(this._container, 'click dblclick mousedown mouseup ' + 4315 'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this); 4316 4317 if (this.options.trackResize) { 4318 onOff(window, 'resize', this._onResize, this); 4319 } 4320 4321 if (any3d && this.options.transform3DLimit) { 4322 (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd); 4323 } 4324 }, 4325 4326 _onResize: function () { 4327 cancelAnimFrame(this._resizeRequest); 4328 this._resizeRequest = requestAnimFrame( 4329 function () { this.invalidateSize({debounceMoveend: true}); }, this); 4330 }, 4331 4332 _onScroll: function () { 4333 this._container.scrollTop = 0; 4334 this._container.scrollLeft = 0; 4335 }, 4336 4337 _onMoveEnd: function () { 4338 var pos = this._getMapPanePos(); 4339 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) { 4340 // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have 4341 // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/ 4342 this._resetView(this.getCenter(), this.getZoom()); 4343 } 4344 }, 4345 4346 _findEventTargets: function (e, type) { 4347 var targets = [], 4348 target, 4349 isHover = type === 'mouseout' || type === 'mouseover', 4350 src = e.target || e.srcElement, 4351 dragging = false; 4352 4353 while (src) { 4354 target = this._targets[stamp(src)]; 4355 if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) { 4356 // Prevent firing click after you just dragged an object. 4357 dragging = true; 4358 break; 4359 } 4360 if (target && target.listens(type, true)) { 4361 if (isHover && !isExternalTarget(src, e)) { break; } 4362 targets.push(target); 4363 if (isHover) { break; } 4364 } 4365 if (src === this._container) { break; } 4366 src = src.parentNode; 4367 } 4368 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) { 4369 targets = [this]; 4370 } 4371 return targets; 4372 }, 4373 4374 _handleDOMEvent: function (e) { 4375 if (!this._loaded || skipped(e)) { return; } 4376 4377 var type = e.type; 4378 4379 if (type === 'mousedown' || type === 'keypress') { 4380 // prevents outline when clicking on keyboard-focusable element 4381 preventOutline(e.target || e.srcElement); 4382 } 4383 4384 this._fireDOMEvent(e, type); 4385 }, 4386 4387 _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'], 4388 4389 _fireDOMEvent: function (e, type, targets) { 4390 4391 if (e.type === 'click') { 4392 // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups). 4393 // @event preclick: MouseEvent 4394 // Fired before mouse click on the map (sometimes useful when you 4395 // want something to happen on click before any existing click 4396 // handlers start running). 4397 var synth = extend({}, e); 4398 synth.type = 'preclick'; 4399 this._fireDOMEvent(synth, synth.type, targets); 4400 } 4401 4402 if (e._stopped) { return; } 4403 4404 // Find the layer the event is propagating from and its parents. 4405 targets = (targets || []).concat(this._findEventTargets(e, type)); 4406 4407 if (!targets.length) { return; } 4408 4409 var target = targets[0]; 4410 if (type === 'contextmenu' && target.listens(type, true)) { 4411 preventDefault(e); 4412 } 4413 4414 var data = { 4415 originalEvent: e 4416 }; 4417 4418 if (e.type !== 'keypress') { 4419 var isMarker = target.getLatLng && (!target._radius || target._radius <= 10); 4420 data.containerPoint = isMarker ? 4421 this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e); 4422 data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); 4423 data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint); 4424 } 4425 4426 for (var i = 0; i < targets.length; i++) { 4427 targets[i].fire(type, data, true); 4428 if (data.originalEvent._stopped || 4429 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; } 4430 } 4431 }, 4432 4433 _draggableMoved: function (obj) { 4434 obj = obj.dragging && obj.dragging.enabled() ? obj : this; 4435 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved()); 4436 }, 4437 4438 _clearHandlers: function () { 4439 for (var i = 0, len = this._handlers.length; i < len; i++) { 4440 this._handlers[i].disable(); 4441 } 4442 }, 4443 4444 // @section Other Methods 4445 4446 // @method whenReady(fn: Function, context?: Object): this 4447 // Runs the given function `fn` when the map gets initialized with 4448 // a view (center and zoom) and at least one layer, or immediately 4449 // if it's already initialized, optionally passing a function context. 4450 whenReady: function (callback, context) { 4451 if (this._loaded) { 4452 callback.call(context || this, {target: this}); 4453 } else { 4454 this.on('load', callback, context); 4455 } 4456 return this; 4457 }, 4458 4459 4460 // private methods for getting map state 4461 4462 _getMapPanePos: function () { 4463 return getPosition(this._mapPane) || new Point(0, 0); 4464 }, 4465 4466 _moved: function () { 4467 var pos = this._getMapPanePos(); 4468 return pos && !pos.equals([0, 0]); 4469 }, 4470 4471 _getTopLeftPoint: function (center, zoom) { 4472 var pixelOrigin = center && zoom !== undefined ? 4473 this._getNewPixelOrigin(center, zoom) : 4474 this.getPixelOrigin(); 4475 return pixelOrigin.subtract(this._getMapPanePos()); 4476 }, 4477 4478 _getNewPixelOrigin: function (center, zoom) { 4479 var viewHalf = this.getSize()._divideBy(2); 4480 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round(); 4481 }, 4482 4483 _latLngToNewLayerPoint: function (latlng, zoom, center) { 4484 var topLeft = this._getNewPixelOrigin(center, zoom); 4485 return this.project(latlng, zoom)._subtract(topLeft); 4486 }, 4487 4488 _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) { 4489 var topLeft = this._getNewPixelOrigin(center, zoom); 4490 return toBounds([ 4491 this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft), 4492 this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft), 4493 this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft), 4494 this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft) 4495 ]); 4496 }, 4497 4498 // layer point of the current center 4499 _getCenterLayerPoint: function () { 4500 return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); 4501 }, 4502 4503 // offset of the specified place to the current center in pixels 4504 _getCenterOffset: function (latlng) { 4505 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); 4506 }, 4507 4508 // adjust center for view to get inside bounds 4509 _limitCenter: function (center, zoom, bounds) { 4510 4511 if (!bounds) { return center; } 4512 4513 var centerPoint = this.project(center, zoom), 4514 viewHalf = this.getSize().divideBy(2), 4515 viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), 4516 offset = this._getBoundsOffset(viewBounds, bounds, zoom); 4517 4518 // If offset is less than a pixel, ignore. 4519 // This prevents unstable projections from getting into 4520 // an infinite loop of tiny offsets. 4521 if (offset.round().equals([0, 0])) { 4522 return center; 4523 } 4524 4525 return this.unproject(centerPoint.add(offset), zoom); 4526 }, 4527 4528 // adjust offset for view to get inside bounds 4529 _limitOffset: function (offset, bounds) { 4530 if (!bounds) { return offset; } 4531 4532 var viewBounds = this.getPixelBounds(), 4533 newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); 4534 4535 return offset.add(this._getBoundsOffset(newBounds, bounds)); 4536 }, 4537 4538 // returns offset needed for pxBounds to get inside maxBounds at a specified zoom 4539 _getBoundsOffset: function (pxBounds, maxBounds, zoom) { 4540 var projectedMaxBounds = toBounds( 4541 this.project(maxBounds.getNorthEast(), zoom), 4542 this.project(maxBounds.getSouthWest(), zoom) 4543 ), 4544 minOffset = projectedMaxBounds.min.subtract(pxBounds.min), 4545 maxOffset = projectedMaxBounds.max.subtract(pxBounds.max), 4546 4547 dx = this._rebound(minOffset.x, -maxOffset.x), 4548 dy = this._rebound(minOffset.y, -maxOffset.y); 4549 4550 return new Point(dx, dy); 4551 }, 4552 4553 _rebound: function (left, right) { 4554 return left + right > 0 ? 4555 Math.round(left - right) / 2 : 4556 Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); 4557 }, 4558 4559 _limitZoom: function (zoom) { 4560 var min = this.getMinZoom(), 4561 max = this.getMaxZoom(), 4562 snap = any3d ? this.options.zoomSnap : 1; 4563 if (snap) { 4564 zoom = Math.round(zoom / snap) * snap; 4565 } 4566 return Math.max(min, Math.min(max, zoom)); 4567 }, 4568 4569 _onPanTransitionStep: function () { 4570 this.fire('move'); 4571 }, 4572 4573 _onPanTransitionEnd: function () { 4574 removeClass(this._mapPane, 'leaflet-pan-anim'); 4575 this.fire('moveend'); 4576 }, 4577 4578 _tryAnimatedPan: function (center, options) { 4579 // difference between the new and current centers in pixels 4580 var offset = this._getCenterOffset(center)._trunc(); 4581 4582 // don't animate too far unless animate: true specified in options 4583 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; } 4584 4585 this.panBy(offset, options); 4586 4587 return true; 4588 }, 4589 4590 _createAnimProxy: function () { 4591 4592 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated'); 4593 this._panes.mapPane.appendChild(proxy); 4594 4595 this.on('zoomanim', function (e) { 4596 var prop = TRANSFORM, 4597 transform = this._proxy.style[prop]; 4598 4599 setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); 4600 4601 // workaround for case when transform is the same and so transitionend event is not fired 4602 if (transform === this._proxy.style[prop] && this._animatingZoom) { 4603 this._onZoomTransitionEnd(); 4604 } 4605 }, this); 4606 4607 this.on('load moveend', function () { 4608 var c = this.getCenter(), 4609 z = this.getZoom(); 4610 setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1)); 4611 }, this); 4612 4613 this._on('unload', this._destroyAnimProxy, this); 4614 }, 4615 4616 _destroyAnimProxy: function () { 4617 remove(this._proxy); 4618 delete this._proxy; 4619 }, 4620 4621 _catchTransitionEnd: function (e) { 4622 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) { 4623 this._onZoomTransitionEnd(); 4624 } 4625 }, 4626 4627 _nothingToAnimate: function () { 4628 return !this._container.getElementsByClassName('leaflet-zoom-animated').length; 4629 }, 4630 4631 _tryAnimatedZoom: function (center, zoom, options) { 4632 4633 if (this._animatingZoom) { return true; } 4634 4635 options = options || {}; 4636 4637 // don't animate if disabled, not supported or zoom difference is too large 4638 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || 4639 Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; } 4640 4641 // offset is the pixel coords of the zoom origin relative to the current center 4642 var scale = this.getZoomScale(zoom), 4643 offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale); 4644 4645 // don't animate if the zoom origin isn't within one screen from the current center, unless forced 4646 if (options.animate !== true && !this.getSize().contains(offset)) { return false; } 4647 4648 requestAnimFrame(function () { 4649 this 4650 ._moveStart(true, false) 4651 ._animateZoom(center, zoom, true); 4652 }, this); 4653 4654 return true; 4655 }, 4656 4657 _animateZoom: function (center, zoom, startAnim, noUpdate) { 4658 if (!this._mapPane) { return; } 4659 4660 if (startAnim) { 4661 this._animatingZoom = true; 4662 4663 // remember what center/zoom to set after animation 4664 this._animateToCenter = center; 4665 this._animateToZoom = zoom; 4666 4667 addClass(this._mapPane, 'leaflet-zoom-anim'); 4668 } 4669 4670 // @event zoomanim: ZoomAnimEvent 4671 // Fired at least once per zoom animation. For continous zoom, like pinch zooming, fired once per frame during zoom. 4672 this.fire('zoomanim', { 4673 center: center, 4674 zoom: zoom, 4675 noUpdate: noUpdate 4676 }); 4677 4678 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693 4679 setTimeout(bind(this._onZoomTransitionEnd, this), 250); 4680 }, 4681 4682 _onZoomTransitionEnd: function () { 4683 if (!this._animatingZoom) { return; } 4684 4685 if (this._mapPane) { 4686 removeClass(this._mapPane, 'leaflet-zoom-anim'); 4687 } 4688 4689 this._animatingZoom = false; 4690 4691 this._move(this._animateToCenter, this._animateToZoom); 4692 4693 // This anim frame should prevent an obscure iOS webkit tile loading race condition. 4694 requestAnimFrame(function () { 4695 this._moveEnd(true); 4696 }, this); 4697 } 4698}); 4699 4700// @section 4701 4702// @factory L.map(id: String, options?: Map options) 4703// Instantiates a map object given the DOM ID of a `<div>` element 4704// and optionally an object literal with `Map options`. 4705// 4706// @alternative 4707// @factory L.map(el: HTMLElement, options?: Map options) 4708// Instantiates a map object given an instance of a `<div>` HTML element 4709// and optionally an object literal with `Map options`. 4710function createMap(id, options) { 4711 return new Map(id, options); 4712} 4713 4714/* 4715 * @class Control 4716 * @aka L.Control 4717 * @inherits Class 4718 * 4719 * L.Control is a base class for implementing map controls. Handles positioning. 4720 * All other controls extend from this class. 4721 */ 4722 4723var Control = Class.extend({ 4724 // @section 4725 // @aka Control options 4726 options: { 4727 // @option position: String = 'topright' 4728 // The position of the control (one of the map corners). Possible values are `'topleft'`, 4729 // `'topright'`, `'bottomleft'` or `'bottomright'` 4730 position: 'topright' 4731 }, 4732 4733 initialize: function (options) { 4734 setOptions(this, options); 4735 }, 4736 4737 /* @section 4738 * Classes extending L.Control will inherit the following methods: 4739 * 4740 * @method getPosition: string 4741 * Returns the position of the control. 4742 */ 4743 getPosition: function () { 4744 return this.options.position; 4745 }, 4746 4747 // @method setPosition(position: string): this 4748 // Sets the position of the control. 4749 setPosition: function (position) { 4750 var map = this._map; 4751 4752 if (map) { 4753 map.removeControl(this); 4754 } 4755 4756 this.options.position = position; 4757 4758 if (map) { 4759 map.addControl(this); 4760 } 4761 4762 return this; 4763 }, 4764 4765 // @method getContainer: HTMLElement 4766 // Returns the HTMLElement that contains the control. 4767 getContainer: function () { 4768 return this._container; 4769 }, 4770 4771 // @method addTo(map: Map): this 4772 // Adds the control to the given map. 4773 addTo: function (map) { 4774 this.remove(); 4775 this._map = map; 4776 4777 var container = this._container = this.onAdd(map), 4778 pos = this.getPosition(), 4779 corner = map._controlCorners[pos]; 4780 4781 addClass(container, 'leaflet-control'); 4782 4783 if (pos.indexOf('bottom') !== -1) { 4784 corner.insertBefore(container, corner.firstChild); 4785 } else { 4786 corner.appendChild(container); 4787 } 4788 4789 return this; 4790 }, 4791 4792 // @method remove: this 4793 // Removes the control from the map it is currently active on. 4794 remove: function () { 4795 if (!this._map) { 4796 return this; 4797 } 4798 4799 remove(this._container); 4800 4801 if (this.onRemove) { 4802 this.onRemove(this._map); 4803 } 4804 4805 this._map = null; 4806 4807 return this; 4808 }, 4809 4810 _refocusOnMap: function (e) { 4811 // if map exists and event is not a keyboard event 4812 if (this._map && e && e.screenX > 0 && e.screenY > 0) { 4813 this._map.getContainer().focus(); 4814 } 4815 } 4816}); 4817 4818var control = function (options) { 4819 return new Control(options); 4820}; 4821 4822/* @section Extension methods 4823 * @uninheritable 4824 * 4825 * Every control should extend from `L.Control` and (re-)implement the following methods. 4826 * 4827 * @method onAdd(map: Map): HTMLElement 4828 * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo). 4829 * 4830 * @method onRemove(map: Map) 4831 * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove). 4832 */ 4833 4834/* @namespace Map 4835 * @section Methods for Layers and Controls 4836 */ 4837Map.include({ 4838 // @method addControl(control: Control): this 4839 // Adds the given control to the map 4840 addControl: function (control) { 4841 control.addTo(this); 4842 return this; 4843 }, 4844 4845 // @method removeControl(control: Control): this 4846 // Removes the given control from the map 4847 removeControl: function (control) { 4848 control.remove(); 4849 return this; 4850 }, 4851 4852 _initControlPos: function () { 4853 var corners = this._controlCorners = {}, 4854 l = 'leaflet-', 4855 container = this._controlContainer = 4856 create$1('div', l + 'control-container', this._container); 4857 4858 function createCorner(vSide, hSide) { 4859 var className = l + vSide + ' ' + l + hSide; 4860 4861 corners[vSide + hSide] = create$1('div', className, container); 4862 } 4863 4864 createCorner('top', 'left'); 4865 createCorner('top', 'right'); 4866 createCorner('bottom', 'left'); 4867 createCorner('bottom', 'right'); 4868 }, 4869 4870 _clearControlPos: function () { 4871 for (var i in this._controlCorners) { 4872 remove(this._controlCorners[i]); 4873 } 4874 remove(this._controlContainer); 4875 delete this._controlCorners; 4876 delete this._controlContainer; 4877 } 4878}); 4879 4880/* 4881 * @class Control.Layers 4882 * @aka L.Control.Layers 4883 * @inherits Control 4884 * 4885 * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`. 4886 * 4887 * @example 4888 * 4889 * ```js 4890 * var baseLayers = { 4891 * "Mapbox": mapbox, 4892 * "OpenStreetMap": osm 4893 * }; 4894 * 4895 * var overlays = {
4896 * "Marker": marker, 4897 * "Roads": roadsLayer 4898 * }; 4899 * 4900 * L.control.layers(baseLayers, overlays).addTo(map); 4901 * ``` 4902 * 4903 * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values: 4904 * 4905 * ```js 4906 * { 4907 * "<someName1>": layer1, 4908 * "<someName2>": layer2 4909 * } 4910 * ``` 4911 * 4912 * The layer names can contain HTML, which allows you to add additional styling to the items: 4913 * 4914 * ```js 4915 * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer} 4916 * ``` 4917 */ 4918 4919var Layers = Control.extend({ 4920 // @section 4921 // @aka Control.Layers options 4922 options: { 4923 // @option collapsed: Boolean = true 4924 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch. 4925 collapsed: true, 4926 position: 'topright', 4927 4928 // @option autoZIndex: Boolean = true 4929 // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off. 4930 autoZIndex: true, 4931 4932 // @option hideSingleBase: Boolean = false 4933 // If `true`, the base layers in the control will be hidden when there is only one. 4934 hideSingleBase: false, 4935 4936 // @option sortLayers: Boolean = false 4937 // Whether to sort the layers. When `false`, layers will keep the order 4938 // in which they were added to the control. 4939 sortLayers: false, 4940 4941 // @option sortFunction: Function = * 4942 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 4943 // that will be used for sorting the layers, when `sortLayers` is `true`. 4944 // The function receives both the `L.Layer` instances and their names, as in 4945 // `sortFunction(layerA, layerB, nameA, nameB)`. 4946 // By default, it sorts layers alphabetically by their name. 4947 sortFunction: function (layerA, layerB, nameA, nameB) { 4948 return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0); 4949 } 4950 }, 4951 4952 initialize: function (baseLayers, overlays, options) { 4953 setOptions(this, options); 4954 4955 this._layerControlInputs = []; 4956 this._layers = []; 4957 this._lastZIndex = 0; 4958 this._handlingClick = false; 4959 4960 for (var i in baseLayers) { 4961 this._addLayer(baseLayers[i], i); 4962 } 4963 4964 for (i in overlays) { 4965 this._addLayer(overlays[i], i, true); 4966 } 4967 }, 4968 4969 onAdd: function (map) { 4970 this._initLayout(); 4971 this._update(); 4972 4973 this._map = map; 4974 map.on('zoomend', this._checkDisabledLayers, this); 4975 4976 for (var i = 0; i < this._layers.length; i++) { 4977 this._layers[i].layer.on('add remove', this._onLayerChange, this); 4978 } 4979 4980 return this._container; 4981 }, 4982 4983 addTo: function (map) { 4984 Control.prototype.addTo.call(this, map); 4985 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height. 4986 return this._expandIfNotCollapsed(); 4987 }, 4988 4989 onRemove: function () { 4990 this._map.off('zoomend', this._checkDisabledLayers, this); 4991 4992 for (var i = 0; i < this._layers.length; i++) { 4993 this._layers[i].layer.off('add remove', this._onLayerChange, this); 4994 } 4995 }, 4996 4997 // @method addBaseLayer(layer: Layer, name: String): this 4998 // Adds a base layer (radio button entry) with the given name to the control. 4999 addBaseLayer: function (layer, name) { 5000 this._addLayer(layer, name); 5001 return (this._map) ? this._update() : this; 5002 }, 5003 5004 // @method addOverlay(layer: Layer, name: String): this 5005 // Adds an overlay (checkbox entry) with the given name to the control. 5006 addOverlay: function (layer, name) { 5007 this._addLayer(layer, name, true); 5008 return (this._map) ? this._update() : this; 5009 }, 5010 5011 // @method removeLayer(layer: Layer): this 5012 // Remove the given layer from the control. 5013 removeLayer: function (layer) { 5014 layer.off('add remove', this._onLayerChange, this); 5015 5016 var obj = this._getLayer(stamp(layer)); 5017 if (obj) { 5018 this._layers.splice(this._layers.indexOf(obj), 1); 5019 } 5020 return (this._map) ? this._update() : this; 5021 }, 5022 5023 // @method expand(): this 5024 // Expand the control container if collapsed. 5025 expand: function () { 5026 addClass(this._container, 'leaflet-control-layers-expanded'); 5027 this._section.style.height = null; 5028 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50); 5029 if (acceptableHeight < this._section.clientHeight) { 5030 addClass(this._section, 'leaflet-control-layers-scrollbar'); 5031 this._section.style.height = acceptableHeight + 'px'; 5032 } else { 5033 removeClass(this._section, 'leaflet-control-layers-scrollbar'); 5034 } 5035 this._checkDisabledLayers(); 5036 return this; 5037 }, 5038 5039 // @method collapse(): this 5040 // Collapse the control container if expanded. 5041 collapse: function () { 5042 removeClass(this._container, 'leaflet-control-layers-expanded'); 5043 return this; 5044 }, 5045 5046 _initLayout: function () { 5047 var className = 'leaflet-control-layers', 5048 container = this._container = create$1('div', className), 5049 collapsed = this.options.collapsed; 5050 5051 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
vendor: 19,116 bytes, lines 5052-5753
5052 container.setAttribute('aria-haspopup', true); 5053 5054 disableClickPropagation(container); 5055 disableScrollPropagation(container); 5056 5057 var section = this._section = create$1('section', className + '-list'); 5058 5059 if (collapsed) { 5060 this._map.on('click', this.collapse, this); 5061 5062 if (!android) { 5063 on(container, { 5064 mouseenter: this.expand, 5065 mouseleave: this.collapse 5066 }, this); 5067 } 5068 } 5069 5070 var link = this._layersLink = create$1('a', className + '-toggle', container); 5071 link.href = '#'; 5072 link.title = 'Layers'; 5073 5074 if (touch) { 5075 on(link, 'click', stop); 5076 on(link, 'click', this.expand, this); 5077 } else { 5078 on(link, 'focus', this.expand, this); 5079 } 5080 5081 if (!collapsed) { 5082 this.expand(); 5083 } 5084 5085 this._baseLayersList = create$1('div', className + '-base', section); 5086 this._separator = create$1('div', className + '-separator', section); 5087 this._overlaysList = create$1('div', className + '-overlays', section); 5088 5089 container.appendChild(section); 5090 }, 5091 5092 _getLayer: function (id) { 5093 for (var i = 0; i < this._layers.length; i++) { 5094 5095 if (this._layers[i] && stamp(this._layers[i].layer) === id) { 5096 return this._layers[i]; 5097 } 5098 } 5099 }, 5100 5101 _addLayer: function (layer, name, overlay) { 5102 if (this._map) { 5103 layer.on('add remove', this._onLayerChange, this); 5104 } 5105 5106 this._layers.push({ 5107 layer: layer, 5108 name: name, 5109 overlay: overlay 5110 }); 5111 5112 if (this.options.sortLayers) { 5113 this._layers.sort(bind(function (a, b) { 5114 return this.options.sortFunction(a.layer, b.layer, a.name, b.name); 5115 }, this)); 5116 } 5117 5118 if (this.options.autoZIndex && layer.setZIndex) { 5119 this._lastZIndex++; 5120 layer.setZIndex(this._lastZIndex); 5121 } 5122 5123 this._expandIfNotCollapsed(); 5124 }, 5125 5126 _update: function () { 5127 if (!this._container) { return this; } 5128 5129 empty(this._baseLayersList); 5130 empty(this._overlaysList); 5131 5132 this._layerControlInputs = []; 5133 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0; 5134 5135 for (i = 0; i < this._layers.length; i++) { 5136 obj = this._layers[i]; 5137 this._addItem(obj); 5138 overlaysPresent = overlaysPresent || obj.overlay; 5139 baseLayersPresent = baseLayersPresent || !obj.overlay; 5140 baseLayersCount += !obj.overlay ? 1 : 0; 5141 } 5142 5143 // Hide base layers section if there's only one layer. 5144 if (this.options.hideSingleBase) { 5145 baseLayersPresent = baseLayersPresent && baseLayersCount > 1; 5146 this._baseLayersList.style.display = baseLayersPresent ? '' : 'none'; 5147 } 5148 5149 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; 5150 5151 return this; 5152 }, 5153 5154 _onLayerChange: function (e) { 5155 if (!this._handlingClick) { 5156 this._update(); 5157 } 5158 5159 var obj = this._getLayer(stamp(e.target)); 5160 5161 // @namespace Map 5162 // @section Layer events 5163 // @event baselayerchange: LayersControlEvent 5164 // Fired when the base layer is changed through the [layer control](#control-layers). 5165 // @event overlayadd: LayersControlEvent 5166 // Fired when an overlay is selected through the [layer control](#control-layers). 5167 // @event overlayremove: LayersControlEvent 5168 // Fired when an overlay is deselected through the [layer control](#control-layers). 5169 // @namespace Control.Layers 5170 var type = obj.overlay ? 5171 (e.type === 'add' ? 'overlayadd' : 'overlayremove') : 5172 (e.type === 'add' ? 'baselayerchange' : null); 5173 5174 if (type) { 5175 this._map.fire(type, obj); 5176 } 5177 }, 5178 5179 // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) 5180 _createRadioElement: function (name, checked) { 5181 5182 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' + 5183 name + '"' + (checked ? ' checked="checked"' : '') + '/>'; 5184 5185 var radioFragment = document.createElement('div'); 5186 radioFragment.innerHTML = radioHtml; 5187 5188 return radioFragment.firstChild; 5189 }, 5190 5191 _addItem: function (obj) { 5192 var label = document.createElement('label'), 5193 checked = this._map.hasLayer(obj.layer), 5194 input; 5195 5196 if (obj.overlay) { 5197 input = document.createElement('input'); 5198 input.type = 'checkbox'; 5199 input.className = 'leaflet-control-layers-selector'; 5200 input.defaultChecked = checked; 5201 } else { 5202 input = this._createRadioElement('leaflet-base-layers', checked); 5203 } 5204 5205 this._layerControlInputs.push(input); 5206 input.layerId = stamp(obj.layer); 5207 5208 on(input, 'click', this._onInputClick, this); 5209 5210 var name = document.createElement('span'); 5211 name.innerHTML = ' ' + obj.name; 5212 5213 // Helps from preventing layer control flicker when checkboxes are disabled 5214 // https://github.com/Leaflet/Leaflet/issues/2771 5215 var holder = document.createElement('div'); 5216 5217 label.appendChild(holder); 5218 holder.appendChild(input); 5219 holder.appendChild(name); 5220 5221 var container = obj.overlay ? this._overlaysList : this._baseLayersList; 5222 container.appendChild(label); 5223 5224 this._checkDisabledLayers(); 5225 return label; 5226 }, 5227 5228 _onInputClick: function () { 5229 var inputs = this._layerControlInputs, 5230 input, layer; 5231 var addedLayers = [], 5232 removedLayers = []; 5233 5234 this._handlingClick = true; 5235 5236 for (var i = inputs.length - 1; i >= 0; i--) { 5237 input = inputs[i]; 5238 layer = this._getLayer(input.layerId).layer; 5239 5240 if (input.checked) { 5241 addedLayers.push(layer); 5242 } else if (!input.checked) { 5243 removedLayers.push(layer); 5244 } 5245 } 5246 5247 // Bugfix issue 2318: Should remove all old layers before readding new ones 5248 for (i = 0; i < removedLayers.length; i++) { 5249 if (this._map.hasLayer(removedLayers[i])) { 5250 this._map.removeLayer(removedLayers[i]); 5251 } 5252 } 5253 for (i = 0; i < addedLayers.length; i++) { 5254 if (!this._map.hasLayer(addedLayers[i])) { 5255 this._map.addLayer(addedLayers[i]); 5256 } 5257 } 5258 5259 this._handlingClick = false; 5260 5261 this._refocusOnMap(); 5262 }, 5263 5264 _checkDisabledLayers: function () { 5265 var inputs = this._layerControlInputs, 5266 input, 5267 layer, 5268 zoom = this._map.getZoom(); 5269 5270 for (var i = inputs.length - 1; i >= 0; i--) { 5271 input = inputs[i]; 5272 layer = this._getLayer(input.layerId).layer; 5273 input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) || 5274 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom); 5275 5276 } 5277 }, 5278 5279 _expandIfNotCollapsed: function () { 5280 if (this._map && !this.options.collapsed) { 5281 this.expand(); 5282 } 5283 return this; 5284 }, 5285 5286 _expand: function () { 5287 // Backward compatibility, remove me in 1.1. 5288 return this.expand(); 5289 }, 5290 5291 _collapse: function () { 5292 // Backward compatibility, remove me in 1.1. 5293 return this.collapse(); 5294 } 5295 5296}); 5297 5298 5299// @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options) 5300// Creates an attribution control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation. 5301var layers = function (baseLayers, overlays, options) { 5302 return new Layers(baseLayers, overlays, options); 5303}; 5304 5305/* 5306 * @class Control.Zoom 5307 * @aka L.Control.Zoom 5308 * @inherits Control 5309 * 5310 * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`. 5311 */ 5312 5313var Zoom = Control.extend({ 5314 // @section 5315 // @aka Control.Zoom options 5316 options: { 5317 position: 'topleft', 5318 5319 // @option zoomInText: String = '+' 5320 // The text set on the 'zoom in' button. 5321 zoomInText: '+', 5322 5323 // @option zoomInTitle: String = 'Zoom in' 5324 // The title set on the 'zoom in' button. 5325 zoomInTitle: 'Zoom in', 5326 5327 // @option zoomOutText: String = '−' 5328 // The text set on the 'zoom out' button. 5329 zoomOutText: '−', 5330 5331 // @option zoomOutTitle: String = 'Zoom out' 5332 // The title set on the 'zoom out' button. 5333 zoomOutTitle: 'Zoom out' 5334 }, 5335 5336 onAdd: function (map) { 5337 var zoomName = 'leaflet-control-zoom', 5338 container = create$1('div', zoomName + ' leaflet-bar'), 5339 options = this.options; 5340 5341 this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle, 5342 zoomName + '-in', container, this._zoomIn); 5343 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle, 5344 zoomName + '-out', container, this._zoomOut); 5345 5346 this._updateDisabled(); 5347 map.on('zoomend zoomlevelschange', this._updateDisabled, this); 5348 5349 return container; 5350 }, 5351 5352 onRemove: function (map) { 5353 map.off('zoomend zoomlevelschange', this._updateDisabled, this); 5354 }, 5355 5356 disable: function () { 5357 this._disabled = true; 5358 this._updateDisabled(); 5359 return this; 5360 }, 5361 5362 enable: function () { 5363 this._disabled = false; 5364 this._updateDisabled(); 5365 return this; 5366 }, 5367 5368 _zoomIn: function (e) { 5369 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) { 5370 this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); 5371 } 5372 }, 5373 5374 _zoomOut: function (e) { 5375 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) { 5376 this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); 5377 } 5378 }, 5379 5380 _createButton: function (html, title, className, container, fn) { 5381 var link = create$1('a', className, container); 5382 link.innerHTML = html; 5383 link.href = '#'; 5384 link.title = title; 5385 5386 /* 5387 * Will force screen readers like VoiceOver to read this as "Zoom in - button" 5388 */ 5389 link.setAttribute('role', 'button'); 5390 link.setAttribute('aria-label', title); 5391 5392 disableClickPropagation(link); 5393 on(link, 'click', stop); 5394 on(link, 'click', fn, this); 5395 on(link, 'click', this._refocusOnMap, this); 5396 5397 return link; 5398 }, 5399 5400 _updateDisabled: function () { 5401 var map = this._map, 5402 className = 'leaflet-disabled'; 5403 5404 removeClass(this._zoomInButton, className); 5405 removeClass(this._zoomOutButton, className); 5406 5407 if (this._disabled || map._zoom === map.getMinZoom()) { 5408 addClass(this._zoomOutButton, className); 5409 } 5410 if (this._disabled || map._zoom === map.getMaxZoom()) { 5411 addClass(this._zoomInButton, className); 5412 } 5413 } 5414}); 5415 5416// @namespace Map 5417// @section Control options 5418// @option zoomControl: Boolean = true 5419// Whether a [zoom control](#control-zoom) is added to the map by default. 5420Map.mergeOptions({ 5421 zoomControl: true 5422}); 5423 5424Map.addInitHook(function () { 5425 if (this.options.zoomControl) { 5426 // @section Controls 5427 // @property zoomControl: Control.Zoom 5428 // The default zoom control (only available if the 5429 // [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map). 5430 this.zoomControl = new Zoom(); 5431 this.addControl(this.zoomControl); 5432 } 5433}); 5434 5435// @namespace Control.Zoom 5436// @factory L.control.zoom(options: Control.Zoom options) 5437// Creates a zoom control 5438var zoom = function (options) { 5439 return new Zoom(options); 5440}; 5441 5442/* 5443 * @class Control.Scale 5444 * @aka L.Control.Scale 5445 * @inherits Control 5446 * 5447 * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`. 5448 * 5449 * @example 5450 * 5451 * ```js 5452 * L.control.scale().addTo(map); 5453 * ``` 5454 */ 5455 5456var Scale = Control.extend({ 5457 // @section 5458 // @aka Control.Scale options 5459 options: { 5460 position: 'bottomleft', 5461 5462 // @option maxWidth: Number = 100 5463 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500). 5464 maxWidth: 100, 5465 5466 // @option metric: Boolean = True 5467 // Whether to show the metric scale line (m/km). 5468 metric: true, 5469 5470 // @option imperial: Boolean = True 5471 // Whether to show the imperial scale line (mi/ft). 5472 imperial: true 5473 5474 // @option updateWhenIdle: Boolean = false 5475 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)). 5476 }, 5477 5478 onAdd: function (map) { 5479 var className = 'leaflet-control-scale', 5480 container = create$1('div', className), 5481 options = this.options; 5482 5483 this._addScales(options, className + '-line', container); 5484 5485 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this); 5486 map.whenReady(this._update, this); 5487 5488 return container; 5489 }, 5490 5491 onRemove: function (map) { 5492 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this); 5493 }, 5494 5495 _addScales: function (options, className, container) { 5496 if (options.metric) { 5497 this._mScale = create$1('div', className, container); 5498 } 5499 if (options.imperial) { 5500 this._iScale = create$1('div', className, container); 5501 } 5502 }, 5503 5504 _update: function () { 5505 var map = this._map, 5506 y = map.getSize().y / 2; 5507 5508 var maxMeters = map.distance( 5509 map.containerPointToLatLng([0, y]), 5510 map.containerPointToLatLng([this.options.maxWidth, y])); 5511 5512 this._updateScales(maxMeters); 5513 }, 5514 5515 _updateScales: function (maxMeters) { 5516 if (this.options.metric && maxMeters) { 5517 this._updateMetric(maxMeters); 5518 } 5519 if (this.options.imperial && maxMeters) { 5520 this._updateImperial(maxMeters); 5521 } 5522 }, 5523 5524 _updateMetric: function (maxMeters) { 5525 var meters = this._getRoundNum(maxMeters), 5526 label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km'; 5527 5528 this._updateScale(this._mScale, label, meters / maxMeters); 5529 }, 5530 5531 _updateImperial: function (maxMeters) { 5532 var maxFeet = maxMeters * 3.2808399, 5533 maxMiles, miles, feet; 5534 5535 if (maxFeet > 5280) { 5536 maxMiles = maxFeet / 5280; 5537 miles = this._getRoundNum(maxMiles); 5538 this._updateScale(this._iScale, miles + ' mi', miles / maxMiles); 5539 5540 } else { 5541 feet = this._getRoundNum(maxFeet); 5542 this._updateScale(this._iScale, feet + ' ft', feet / maxFeet); 5543 } 5544 }, 5545 5546 _updateScale: function (scale, text, ratio) { 5547 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px'; 5548 scale.innerHTML = text; 5549 }, 5550 5551 _getRoundNum: function (num) { 5552 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1), 5553 d = num / pow10; 5554 5555 d = d >= 10 ? 10 : 5556 d >= 5 ? 5 : 5557 d >= 3 ? 3 : 5558 d >= 2 ? 2 : 1; 5559 5560 return pow10 * d; 5561 } 5562}); 5563 5564 5565// @factory L.control.scale(options?: Control.Scale options) 5566// Creates an scale control with the given options. 5567var scale = function (options) { 5568 return new Scale(options); 5569}; 5570 5571/* 5572 * @class Control.Attribution 5573 * @aka L.Control.Attribution 5574 * @inherits Control 5575 * 5576 * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control. 5577 */ 5578 5579var Attribution = Control.extend({ 5580 // @section 5581 // @aka Control.Attribution options 5582 options: { 5583 position: 'bottomright', 5584 5585 // @option prefix: String = 'Leaflet' 5586 // The HTML text shown before the attributions. Pass `false` to disable. 5587 prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>' 5588 }, 5589 5590 initialize: function (options) { 5591 setOptions(this, options); 5592 5593 this._attributions = {}; 5594 }, 5595 5596 onAdd: function (map) { 5597 map.attributionControl = this; 5598 this._container = create$1('div', 'leaflet-control-attribution'); 5599 disableClickPropagation(this._container); 5600 5601 // TODO ugly, refactor 5602 for (var i in map._layers) { 5603 if (map._layers[i].getAttribution) { 5604 this.addAttribution(map._layers[i].getAttribution()); 5605 } 5606 } 5607 5608 this._update(); 5609 5610 return this._container; 5611 }, 5612 5613 // @method setPrefix(prefix: String): this 5614 // Sets the text before the attributions. 5615 setPrefix: function (prefix) { 5616 this.options.prefix = prefix; 5617 this._update(); 5618 return this; 5619 }, 5620 5621 // @method addAttribution(text: String): this 5622 // Adds an attribution text (e.g. `'Vector data © Mapbox'`). 5623 addAttribution: function (text) { 5624 if (!text) { return this; } 5625 5626 if (!this._attributions[text]) { 5627 this._attributions[text] = 0; 5628 } 5629 this._attributions[text]++; 5630 5631 this._update(); 5632 5633 return this; 5634 }, 5635 5636 // @method removeAttribution(text: String): this 5637 // Removes an attribution text. 5638 removeAttribution: function (text) { 5639 if (!text) { return this; } 5640 5641 if (this._attributions[text]) { 5642 this._attributions[text]--; 5643 this._update(); 5644 } 5645 5646 return this; 5647 }, 5648 5649 _update: function () { 5650 if (!this._map) { return; } 5651 5652 var attribs = []; 5653 5654 for (var i in this._attributions) { 5655 if (this._attributions[i]) { 5656 attribs.push(i); 5657 } 5658 } 5659 5660 var prefixAndAttribs = []; 5661 5662 if (this.options.prefix) { 5663 prefixAndAttribs.push(this.options.prefix); 5664 } 5665 if (attribs.length) { 5666 prefixAndAttribs.push(attribs.join(', ')); 5667 } 5668 5669 this._container.innerHTML = prefixAndAttribs.join(' | '); 5670 } 5671}); 5672 5673// @namespace Map 5674// @section Control options 5675// @option attributionControl: Boolean = true 5676// Whether a [attribution control](#control-attribution) is added to the map by default. 5677Map.mergeOptions({ 5678 attributionControl: true 5679}); 5680 5681Map.addInitHook(function () { 5682 if (this.options.attributionControl) { 5683 new Attribution().addTo(this); 5684 } 5685}); 5686 5687// @namespace Control.Attribution 5688// @factory L.control.attribution(options: Control.Attribution options) 5689// Creates an attribution control. 5690var attribution = function (options) { 5691 return new Attribution(options); 5692}; 5693 5694Control.Layers = Layers; 5695Control.Zoom = Zoom; 5696Control.Scale = Scale; 5697Control.Attribution = Attribution; 5698 5699control.layers = layers; 5700control.zoom = zoom; 5701control.scale = scale; 5702control.attribution = attribution; 5703 5704/* 5705 L.Handler is a base class for handler classes that are used internally to inject 5706 interaction features like dragging to classes like Map and Marker. 5707*/ 5708 5709// @class Handler 5710// @aka L.Handler 5711// Abstract class for map interaction handlers 5712 5713var Handler = Class.extend({ 5714 initialize: function (map) { 5715 this._map = map; 5716 }, 5717 5718 // @method enable(): this 5719 // Enables the handler 5720 enable: function () { 5721 if (this._enabled) { return this; } 5722 5723 this._enabled = true; 5724 this.addHooks(); 5725 return this; 5726 }, 5727 5728 // @method disable(): this 5729 // Disables the handler 5730 disable: function () { 5731 if (!this._enabled) { return this; } 5732 5733 this._enabled = false; 5734 this.removeHooks(); 5735 return this; 5736 }, 5737 5738 // @method enabled(): Boolean 5739 // Returns `true` if the handler is enabled 5740 enabled: function () { 5741 return !!this._enabled; 5742 } 5743 5744 // @section Extension methods 5745 // Classes inheriting from `Handler` must implement the two following methods: 5746 // @method addHooks() 5747 // Called when the handler is enabled, should add event hooks. 5748 // @method removeHooks() 5749 // Called when the handler is disabled, should remove the event hooks added previously. 5750}); 5751 5752// @section There is static function which can be called without instantiating L.Handler: 5753// @function addTo(map: Map, name: String): this
5754// Adds a new Handler to the given map with the given name. 5755Handler.addTo = function (map, name) { 5756 map.addHandler(name, this); 5757 return this; 5758}; 5759 5760var Mixin = {Events: Events}; 5761 5762/* 5763 * @class Draggable 5764 * @aka L.Draggable 5765 * @inherits Evented 5766 * 5767 * A class for making DOM elements draggable (including touch support). 5768 * Used internally for map and marker dragging. Only works for elements 5769 * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition). 5770 * 5771 * @example 5772 * ```js 5773 * var draggable = new L.Draggable(elementToDrag); 5774 * draggable.enable(); 5775 * ``` 5776 */ 5777 5778var START = touch ? 'touchstart mousedown' : 'mousedown'; 5779var END = { 5780 mousedown: 'mouseup', 5781 touchstart: 'touchend', 5782 pointerdown: 'touchend', 5783 MSPointerDown: 'touchend' 5784}; 5785var MOVE = { 5786 mousedown: 'mousemove', 5787 touchstart: 'touchmove', 5788 pointerdown: 'touchmove', 5789 MSPointerDown: 'touchmove' 5790}; 5791 5792 5793var Draggable = Evented.extend({ 5794 5795 options: { 5796 // @section 5797 // @aka Draggable options 5798 // @option clickTolerance: Number = 3 5799 // The max number of pixels a user can shift the mouse pointer during a click 5800 // for it to be considered a valid click (as opposed to a mouse drag). 5801 clickTolerance: 3 5802 }, 5803 5804 // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options) 5805 // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default). 5806 initialize: function (element, dragStartTarget, preventOutline$$1, options) { 5807 setOptions(this, options); 5808 5809 this._element = element; 5810 this._dragStartTarget = dragStartTarget || element; 5811 this._preventOutline = preventOutline$$1; 5812 }, 5813 5814 // @method enable() 5815 // Enables the dragging ability 5816 enable: function () { 5817 if (this._enabled) { return; } 5818 5819 on(this._dragStartTarget, START, this._onDown, this); 5820 5821 this._enabled = true; 5822 }, 5823 5824 // @method disable() 5825 // Disables the dragging ability 5826 disable: function () { 5827 if (!this._enabled) { return; } 5828 5829 // If we're currently dragging this draggable, 5830 // disabling it counts as first ending the drag. 5831 if (Draggable._dragging === this) { 5832 this.finishDrag(); 5833 } 5834 5835 off(this._dragStartTarget, START, this._onDown, this); 5836 5837 this._enabled = false; 5838 this._moved = false; 5839 }, 5840 5841 _onDown: function (e) { 5842 // Ignore simulated events, since we handle both touch and 5843 // mouse explicitly; otherwise we risk getting duplicates of 5844 // touch events, see #4315. 5845 // Also ignore the event if disabled; this happens in IE11 5846 // under some circumstances, see #3666. 5847 if (e._simulated || !this._enabled) { return; } 5848 5849 this._moved = false; 5850 5851 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; } 5852 5853 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; } 5854 Draggable._dragging = this; // Prevent dragging multiple objects at once. 5855 5856 if (this._preventOutline) { 5857 preventOutline(this._element); 5858 } 5859 5860 disableImageDrag(); 5861 disableTextSelection(); 5862 5863 if (this._moving) { return; } 5864 5865 // @event down: Event 5866 // Fired when a drag is about to start. 5867 this.fire('down'); 5868 5869 var first = e.touches ? e.touches[0] : e, 5870 sizedParent = getSizedParentNode(this._element); 5871 5872 this._startPoint = new Point(first.clientX, first.clientY); 5873 5874 // Cache the scale, so that we can continuously compensate for it during drag (_onMove). 5875 this._parentScale = getScale(sizedParent); 5876 5877 on(document, MOVE[e.type], this._onMove, this); 5878 on(document, END[e.type], this._onUp, this); 5879 }, 5880 5881 _onMove: function (e) { 5882 // Ignore simulated events, since we handle both touch and 5883 // mouse explicitly; otherwise we risk getting duplicates of 5884 // touch events, see #4315. 5885 // Also ignore the event if disabled; this happens in IE11 5886 // under some circumstances, see #3666. 5887 if (e._simulated || !this._enabled) { return; } 5888 5889 if (e.touches && e.touches.length > 1) { 5890 this._moved = true; 5891 return; 5892 } 5893 5894 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), 5895 offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint); 5896 5897 if (!offset.x && !offset.y) { return; } 5898 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; } 5899 5900 // We assume that the parent container's position, border and scale do not change for the duration of the drag. 5901 // Therefore there is no need to account for the position and border (they are eliminated by the subtraction) 5902 // and we can use the cached value for the scale. 5903 offset.x /= this._parentScale.x; 5904 offset.y /= this._parentScale.y; 5905 5906 preventDefault(e); 5907 5908 if (!this._moved) { 5909 // @event dragstart: Event 5910 // Fired when a drag starts 5911 this.fire('dragstart'); 5912 5913 this._moved = true; 5914 this._startPos = getPosition(this._element).subtract(offset); 5915 5916 addClass(document.body, 'leaflet-dragging'); 5917 5918 this._lastTarget = e.target || e.srcElement; 5919 // IE and Edge do not give the <use> element, so fetch it 5920 // if necessary 5921 if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) { 5922 this._lastTarget = this._lastTarget.correspondingUseElement; 5923 } 5924 addClass(this._lastTarget, 'leaflet-drag-target'); 5925 } 5926 5927 this._newPos = this._startPos.add(offset); 5928 this._moving = true; 5929
5930 cancelAnimFrame(this._animRequest); 5931 this._lastEvent = e; 5932 this._animRequest = requestAnimFrame(this._updatePosition, this, true); 5933 }, 5934 5935 _updatePosition: function () { 5936 var e = {originalEvent: this._lastEvent}; 5937 5938 // @event predrag: Event 5939 // Fired continuously during dragging *before* each corresponding 5940 // update of the element's position. 5941 this.fire('predrag', e); 5942 setPosition(this._element, this._newPos); 5943 5944 // @event drag: Event 5945 // Fired continuously during dragging. 5946 this.fire('drag', e); 5947 }, 5948 5949 _onUp: function (e) { 5950 // Ignore simulated events, since we handle both touch and 5951 // mouse explicitly; otherwise we risk getting duplicates of 5952 // touch events, see #4315. 5953 // Also ignore the event if disabled; this happens in IE11 5954 // under some circumstances, see #3666. 5955 if (e._simulated || !this._enabled) { return; } 5956 this.finishDrag(); 5957 }, 5958 5959 finishDrag: function () { 5960 removeClass(document.body, 'leaflet-dragging'); 5961 5962 if (this._lastTarget) { 5963 removeClass(this._lastTarget, 'leaflet-drag-target'); 5964 this._lastTarget = null; 5965 } 5966 5967 for (var i in MOVE) { 5968 off(document, MOVE[i], this._onMove, this); 5969 off(document, END[i], this._onUp, this); 5970 } 5971 5972 enableImageDrag(); 5973 enableTextSelection(); 5974 5975 if (this._moved && this._moving) { 5976 // ensure drag is not fired after dragend 5977 cancelAnimFrame(this._animRequest); 5978 5979 // @event dragend: DragEndEvent 5980 // Fired when the drag ends. 5981 this.fire('dragend', { 5982 distance: this._newPos.distanceTo(this._startPos) 5983 }); 5984 } 5985 5986 this._moving = false; 5987 Draggable._dragging = false; 5988 } 5989 5990}); 5991 5992/* 5993 * @namespace LineUtil 5994 * 5995 * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast. 5996 */ 5997 5998// Simplify polyline with vertex reduction and Douglas-Peucker simplification. 5999// Improves rendering performance dramatically by lessening the number of points to draw. 6000 6001// @function simplify(points: Point[], tolerance: Number): Point[] 6002// Dramatically reduces the number of points in a polyline while retaining 6003// its shape and returns a new array of simplified points, using the 6004// [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm). 6005// Used for a huge performance boost when processing/displaying Leaflet polylines for 6006// each zoom level and also reducing visual noise. tolerance affects the amount of 6007// simplification (lesser value means higher quality but slower and with more points). 6008// Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/). 6009function simplify(points, tolerance) { 6010 if (!tolerance || !points.length) { 6011 return points.slice(); 6012 } 6013 6014 var sqTolerance = tolerance * tolerance; 6015 6016 // stage 1: vertex reduction 6017 points = _reducePoints(points, sqTolerance); 6018 6019 // stage 2: Douglas-Peucker simplification 6020 points = _simplifyDP(points, sqTolerance); 6021 6022 return points; 6023} 6024 6025// @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number 6026// Returns the distance between point `p` and segment `p1` to `p2`. 6027function pointToSegmentDistance(p, p1, p2) { 6028 return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true)); 6029} 6030 6031// @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number 6032// Returns the closest point from a point `p` on a segment `p1` to `p2`. 6033function closestPointOnSegment(p, p1, p2) { 6034 return _sqClosestPointOnSegment(p, p1, p2); 6035} 6036 6037// Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/
vendor: 4,588 bytes, lines 6037-6230
6037Douglas-Peucker_algorithm 6038function _simplifyDP(points, sqTolerance) { 6039 6040 var len = points.length, 6041 ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array, 6042 markers = new ArrayConstructor(len); 6043 6044 markers[0] = markers[len - 1] = 1; 6045 6046 _simplifyDPStep(points, markers, sqTolerance, 0, len - 1); 6047 6048 var i, 6049 newPoints = []; 6050 6051 for (i = 0; i < len; i++) { 6052 if (markers[i]) { 6053 newPoints.push(points[i]); 6054 } 6055 } 6056 6057 return newPoints; 6058} 6059 6060function _simplifyDPStep(points, markers, sqTolerance, first, last) { 6061 6062 var maxSqDist = 0, 6063 index, i, sqDist; 6064 6065 for (i = first + 1; i <= last - 1; i++) { 6066 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true); 6067 6068 if (sqDist > maxSqDist) { 6069 index = i; 6070 maxSqDist = sqDist; 6071 } 6072 } 6073 6074 if (maxSqDist > sqTolerance) { 6075 markers[index] = 1; 6076 6077 _simplifyDPStep(points, markers, sqTolerance, first, index); 6078 _simplifyDPStep(points, markers, sqTolerance, index, last); 6079 } 6080} 6081 6082// reduce points that are too close to each other to a single point 6083function _reducePoints(points, sqTolerance) { 6084 var reducedPoints = [points[0]]; 6085 6086 for (var i = 1, prev = 0, len = points.length; i < len; i++) { 6087 if (_sqDist(points[i], points[prev]) > sqTolerance) { 6088 reducedPoints.push(points[i]); 6089 prev = i; 6090 } 6091 } 6092 if (prev < len - 1) { 6093 reducedPoints.push(points[len - 1]); 6094 } 6095 return reducedPoints; 6096} 6097 6098var _lastCode; 6099 6100// @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean 6101// Clips the segment a to b by rectangular bounds with the 6102// [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm) 6103// (modifying the segment points directly!). Used by Leaflet to only show polyline 6104// points that are on the screen or near, increasing performance. 6105function clipSegment(a, b, bounds, useLastCode, round) { 6106 var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds), 6107 codeB = _getBitCode(b, bounds), 6108 6109 codeOut, p, newCode; 6110 6111 // save 2nd code to avoid calculating it on the next segment 6112 _lastCode = codeB; 6113 6114 while (true) { 6115 // if a,b is inside the clip window (trivial accept) 6116 if (!(codeA | codeB)) { 6117 return [a, b]; 6118 } 6119 6120 // if a,b is outside the clip window (trivial reject) 6121 if (codeA & codeB) { 6122 return false; 6123 } 6124 6125 // other cases 6126 codeOut = codeA || codeB; 6127 p = _getEdgeIntersection(a, b, codeOut, bounds, round); 6128 newCode = _getBitCode(p, bounds); 6129 6130 if (codeOut === codeA) { 6131 a = p; 6132 codeA = newCode; 6133 } else { 6134 b = p; 6135 codeB = newCode; 6136 } 6137 } 6138} 6139 6140function _getEdgeIntersection(a, b, code, bounds, round) { 6141 var dx = b.x - a.x, 6142 dy = b.y - a.y, 6143 min = bounds.min, 6144 max = bounds.max, 6145 x, y; 6146 6147 if (code & 8) { // top 6148 x = a.x + dx * (max.y - a.y) / dy; 6149 y = max.y; 6150 6151 } else if (code & 4) { // bottom 6152 x = a.x + dx * (min.y - a.y) / dy; 6153 y = min.y; 6154 6155 } else if (code & 2) { // right 6156 x = max.x; 6157 y = a.y + dy * (max.x - a.x) / dx; 6158 6159 } else if (code & 1) { // left 6160 x = min.x; 6161 y = a.y + dy * (min.x - a.x) / dx; 6162 } 6163 6164 return new Point(x, y, round); 6165} 6166 6167function _getBitCode(p, bounds) { 6168 var code = 0; 6169 6170 if (p.x < bounds.min.x) { // left 6171 code |= 1; 6172 } else if (p.x > bounds.max.x) { // right 6173 code |= 2; 6174 } 6175 6176 if (p.y < bounds.min.y) { // bottom 6177 code |= 4; 6178 } else if (p.y > bounds.max.y) { // top 6179 code |= 8; 6180 } 6181 6182 return code; 6183} 6184 6185// square distance (to avoid unnecessary Math.sqrt calls) 6186function _sqDist(p1, p2) { 6187 var dx = p2.x - p1.x, 6188 dy = p2.y - p1.y; 6189 return dx * dx + dy * dy; 6190} 6191 6192// return closest point on segment or distance to that point 6193function _sqClosestPointOnSegment(p, p1, p2, sqDist) { 6194 var x = p1.x, 6195 y = p1.y, 6196 dx = p2.x - x, 6197 dy = p2.y - y, 6198 dot = dx * dx + dy * dy, 6199 t; 6200 6201 if (dot > 0) { 6202 t = ((p.x - x) * dx + (p.y - y) * dy) / dot; 6203 6204 if (t > 1) { 6205 x = p2.x; 6206 y = p2.y; 6207 } else if (t > 0) { 6208 x += dx * t; 6209 y += dy * t; 6210 } 6211 } 6212 6213 dx = p.x - x; 6214 dy = p.y - y; 6215 6216 return sqDist ? dx * dx + dy * dy : new Point(x, y); 6217} 6218 6219 6220// @function isFlat(latlngs: LatLng[]): Boolean 6221// Returns true if `latlngs` is a flat array, false is nested. 6222function isFlat(latlngs) { 6223 return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined'); 6224} 6225 6226function _flat(latlngs) { 6227 console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.'); 6228 return isFlat(latlngs); 6229} 6230
6231 6232var LineUtil = (Object.freeze || Object)({ 6233 simplify: simplify, 6234 pointToSegmentDistance: pointToSegmentDistance, 6235 closestPointOnSegment: closestPointOnSegment, 6236 clipSegment: clipSegment, 6237 _getEdgeIntersection: _getEdgeIntersection, 6238 _getBitCode: _getBitCode, 6239 _sqClosestPointOnSegment: _sqClosestPointOnSegment, 6240 isFlat: isFlat, 6241 _flat: _flat 6242}); 6243 6244/* 6245 * @namespace PolyUtil 6246 * Various utility functions for polygon geometries. 6247 */ 6248 6249/* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[] 6250 * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)). 6251 * Used by Leaflet to only show polygon points that are on the screen or near, increasing 6252 * performance. Note that polygon points needs different algorithm for clipping 6253 * than polyline, so there's a separate method for it. 6254 */ 6255function clipPolygon(points, bounds, round) { 6256 var clippedPoints, 6257 edges = [1, 4, 2, 8], 6258 i, j, k, 6259 a, b, 6260 len, edge, p; 6261 6262 for (i = 0, len = points.length; i < len; i++) { 6263 points[i]._code = _getBitCode(points[i], bounds); 6264 } 6265 6266 // for each edge (left, bottom, right, top) 6267 for (k = 0; k < 4; k++) { 6268 edge = edges[k]; 6269 clippedPoints = []; 6270 6271 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { 6272 a = points[i]; 6273 b = points[j]; 6274 6275 // if a is inside the clip window 6276 if (!(a._code & edge)) { 6277 // if b is outside the clip window (a->b goes out of screen) 6278 if (b._code & edge) { 6279 p = _getEdgeIntersection(b, a, edge, bounds, round); 6280 p._code = _getBitCode(p, bounds); 6281 clippedPoints.push(p); 6282 } 6283 clippedPoints.push(a); 6284 6285 // else if b is inside the clip window (a->b enters the screen) 6286 } else if (!(b._code & edge)) { 6287 p = _getEdgeIntersection(b, a, edge, bounds, round); 6288 p._code = _getBitCode(p, bounds); 6289 clippedPoints.push(p); 6290 } 6291 } 6292 points = clippedPoints; 6293 } 6294 6295 return points; 6296} 6297 6298 6299var PolyUtil = (Object.freeze || Object)({ 6300 clipPolygon: clipPolygon 6301}); 6302 6303/* 6304 * @namespace Projection 6305 * @section
vendor: 21,269 bytes, lines 6306-7049
6306 * Leaflet comes with a set of already defined Projections out of the box: 6307 * 6308 * @projection L.Projection.LonLat 6309 * 6310 * Equirectangular, or Plate Carree projection â the most simple projection, 6311 * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as 6312 * latitude. Also suitable for flat worlds, e.g. game maps. Used by the 6313 * `EPSG:4326` and `Simple` CRS. 6314 */ 6315 6316var LonLat = { 6317 project: function (latlng) { 6318 return new Point(latlng.lng, latlng.lat); 6319 }, 6320 6321 unproject: function (point) { 6322 return new LatLng(point.y, point.x); 6323 }, 6324 6325 bounds: new Bounds([-180, -90], [180, 90]) 6326}; 6327 6328/* 6329 * @namespace Projection 6330 * @projection L.Projection.Mercator 6331 * 6332 * Elliptical Mercator projection â more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS. 6333 */ 6334 6335var Mercator = { 6336 R: 6378137, 6337 R_MINOR: 6356752.314245179, 6338 6339 bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), 6340 6341 project: function (latlng) { 6342 var d = Math.PI / 180, 6343 r = this.R, 6344 y = latlng.lat * d, 6345 tmp = this.R_MINOR / r, 6346 e = Math.sqrt(1 - tmp * tmp), 6347 con = e * Math.sin(y); 6348 6349 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2); 6350 y = -r * Math.log(Math.max(ts, 1E-10)); 6351 6352 return new Point(latlng.lng * d * r, y); 6353 }, 6354 6355 unproject: function (point) { 6356 var d = 180 / Math.PI, 6357 r = this.R, 6358 tmp = this.R_MINOR / r, 6359 e = Math.sqrt(1 - tmp * tmp), 6360 ts = Math.exp(-point.y / r), 6361 phi = Math.PI / 2 - 2 * Math.atan(ts); 6362 6363 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) { 6364 con = e * Math.sin(phi); 6365 con = Math.pow((1 - con) / (1 + con), e / 2); 6366 dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi; 6367 phi += dphi; 6368 } 6369 6370 return new LatLng(phi * d, point.x * d / r); 6371 } 6372}; 6373 6374/* 6375 * @class Projection 6376 6377 * An object with methods for projecting geographical coordinates of the world onto 6378 * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection). 6379 6380 * @property bounds: Bounds 6381 * The bounds (specified in CRS units) where the projection is valid 6382 6383 * @method project(latlng: LatLng): Point 6384 * Projects geographical coordinates into a 2D point. 6385 * Only accepts actual `L.LatLng` instances, not arrays. 6386 6387 * @method unproject(point: Point): LatLng 6388 * The inverse of `project`. Projects a 2D point into a geographical location. 6389 * Only accepts actual `L.Point` instances, not arrays. 6390 6391 * Note that the projection instances do not inherit from Leafet's `Class` object, 6392 * and can't be instantiated. Also, new classes can't inherit from them, 6393 * and methods can't be added to them with the `include` function. 6394 6395 */ 6396 6397 6398 6399 6400var index = (Object.freeze || Object)({ 6401 LonLat: LonLat, 6402 Mercator: Mercator, 6403 SphericalMercator: SphericalMercator 6404}); 6405 6406/* 6407 * @namespace CRS 6408 * @crs L.CRS.EPSG3395 6409 * 6410 * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection. 6411 */ 6412var EPSG3395 = extend({}, Earth, { 6413 code: 'EPSG:3395', 6414 projection: Mercator, 6415 6416 transformation: (function () { 6417 var scale = 0.5 / (Math.PI * Mercator.R); 6418 return toTransformation(scale, 0.5, -scale, 0.5); 6419 }()) 6420}); 6421 6422/* 6423 * @namespace CRS 6424 * @crs L.CRS.EPSG4326 6425 * 6426 * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection. 6427 * 6428 * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic), 6429 * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer` 6430 * with this CRS, ensure that there are two 256x256 pixel tiles covering the 6431 * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90), 6432 * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set. 6433 */ 6434 6435var EPSG4326 = extend({}, Earth, { 6436 code: 'EPSG:4326', 6437 projection: LonLat, 6438 transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5) 6439}); 6440 6441/* 6442 * @namespace CRS 6443 * @crs L.CRS.Simple 6444 * 6445 * A simple CRS that maps longitude and latitude into `x` and `y` directly. 6446 * May be used for maps of flat surfaces (e.g. game maps). Note that the `y` 6447 * axis should still be inverted (going from bottom to top). `distance()` returns 6448 * simple euclidean distance. 6449 */ 6450 6451var Simple = extend({}, CRS, { 6452 projection: LonLat, 6453 transformation: toTransformation(1, 0, -1, 0), 6454 6455 scale: function (zoom) { 6456 return Math.pow(2, zoom); 6457 }, 6458 6459 zoom: function (scale) { 6460 return Math.log(scale) / Math.LN2; 6461 }, 6462 6463 distance: function (latlng1, latlng2) { 6464 var dx = latlng2.lng - latlng1.lng, 6465 dy = latlng2.lat - latlng1.lat; 6466 6467 return Math.sqrt(dx * dx + dy * dy); 6468 }, 6469 6470 infinite: true 6471}); 6472 6473CRS.Earth = Earth; 6474CRS.EPSG3395 = EPSG3395; 6475CRS.EPSG3857 = EPSG3857; 6476CRS.EPSG900913 = EPSG900913; 6477CRS.EPSG4326 = EPSG4326; 6478CRS.Simple = Simple; 6479 6480/* 6481 * @class Layer 6482 * @inherits Evented 6483 * @aka L.Layer 6484 * @aka ILayer 6485 * 6486 * A set of methods from the Layer base class that all Leaflet layers use. 6487 * Inherits all methods, options and events from `L.Evented`. 6488 * 6489 * @example 6490 * 6491 * ```js 6492 * var layer = L.Marker(latlng).addTo(map); 6493 * layer.addTo(map); 6494 * layer.remove(); 6495 * ``` 6496 * 6497 * @event add: Event 6498 * Fired after the layer is added to a map 6499 * 6500 * @event remove: Event 6501 * Fired after the layer is removed from a map 6502 */ 6503 6504 6505var Layer = Evented.extend({ 6506 6507 // Classes extending `L.Layer` will inherit the following options: 6508 options: { 6509 // @option pane: String = 'overlayPane' 6510 // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default. 6511 pane: 'overlayPane', 6512 6513 // @option attribution: String = null 6514 // String to be shown in the attribution control, e.g. "© OpenStreetMap contributors". It describes the layer data and is often a legal obligation towards copyright holders and tile providers. 6515 attribution: null, 6516 6517 bubblingMouseEvents: true 6518 }, 6519 6520 /* @section 6521 * Classes extending `L.Layer` will inherit the following methods: 6522 * 6523 * @method addTo(map: Map|LayerGroup): this 6524 * Adds the layer to the given map or layer group. 6525 */ 6526 addTo: function (map) { 6527 map.addLayer(this); 6528 return this; 6529 }, 6530 6531 // @method remove: this 6532 // Removes the layer from the map it is currently active on. 6533 remove: function () { 6534 return this.removeFrom(this._map || this._mapToAdd); 6535 }, 6536 6537 // @method removeFrom(map: Map): this 6538 // Removes the layer from the given map 6539 removeFrom: function (obj) { 6540 if (obj) { 6541 obj.removeLayer(this); 6542 } 6543 return this; 6544 }, 6545 6546 // @method getPane(name? : String): HTMLElement 6547 // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer. 6548 getPane: function (name) { 6549 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane); 6550 }, 6551 6552 addInteractiveTarget: function (targetEl) { 6553 this._map._targets[stamp(targetEl)] = this; 6554 return this; 6555 }, 6556 6557 removeInteractiveTarget: function (targetEl) { 6558 delete this._map._targets[stamp(targetEl)]; 6559 return this; 6560 }, 6561 6562 // @method getAttribution: String 6563 // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution). 6564 getAttribution: function () { 6565 return this.options.attribution; 6566 }, 6567 6568 _layerAdd: function (e) { 6569 var map = e.target; 6570 6571 // check in case layer gets added and then removed before the map is ready 6572 if (!map.hasLayer(this)) { return; } 6573 6574 this._map = map; 6575 this._zoomAnimated = map._zoomAnimated; 6576 6577 if (this.getEvents) { 6578 var events = this.getEvents(); 6579 map.on(events, this); 6580 this.once('remove', function () { 6581 map.off(events, this); 6582 }, this); 6583 } 6584 6585 this.onAdd(map); 6586 6587 if (this.getAttribution && map.attributionControl) { 6588 map.attributionControl.addAttribution(this.getAttribution()); 6589 } 6590 6591 this.fire('add'); 6592 map.fire('layeradd', {layer: this}); 6593 } 6594}); 6595 6596/* @section Extension methods 6597 * @uninheritable 6598 * 6599 * Every layer should extend from `L.Layer` and (re-)implement the following methods. 6600 * 6601 * @method onAdd(map: Map): this 6602 * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer). 6603 * 6604 * @method onRemove(map: Map): this 6605 * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer). 6606 * 6607 * @method getEvents(): Object 6608 * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer. 6609 * 6610 * @method getAttribution(): String 6611 * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible. 6612 * 6613 * @method beforeAdd(map: Map): this 6614 * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only. 6615 */ 6616 6617 6618/* @namespace Map 6619 * @section Layer events 6620 * 6621 * @event layeradd: LayerEvent 6622 * Fired when a new layer is added to the map. 6623 * 6624 * @event layerremove: LayerEvent 6625 * Fired when some layer is removed from the map 6626 * 6627 * @section Methods for Layers and Controls 6628 */ 6629Map.include({ 6630 // @method addLayer(layer: Layer): this 6631 // Adds the given layer to the map 6632 addLayer: function (layer) { 6633 if (!layer._layerAdd) { 6634 throw new Error('The provided object is not a Layer.'); 6635 } 6636 6637 var id = stamp(layer); 6638 if (this._layers[id]) { return this; } 6639 this._layers[id] = layer; 6640 6641 layer._mapToAdd = this; 6642 6643 if (layer.beforeAdd) { 6644 layer.beforeAdd(this); 6645 } 6646 6647 this.whenReady(layer._layerAdd, layer); 6648 6649 return this; 6650 }, 6651 6652 // @method removeLayer(layer: Layer): this 6653 // Removes the given layer from the map. 6654 removeLayer: function (layer) { 6655 var id = stamp(layer); 6656 6657 if (!this._layers[id]) { return this; } 6658 6659 if (this._loaded) { 6660 layer.onRemove(this); 6661 } 6662 6663 if (layer.getAttribution && this.attributionControl) { 6664 this.attributionControl.removeAttribution(layer.getAttribution()); 6665 } 6666 6667 delete this._layers[id]; 6668 6669 if (this._loaded) { 6670 this.fire('layerremove', {layer: layer}); 6671 layer.fire('remove'); 6672 } 6673 6674 layer._map = layer._mapToAdd = null; 6675 6676 return this; 6677 }, 6678 6679 // @method hasLayer(layer: Layer): Boolean 6680 // Returns `true` if the given layer is currently added to the map 6681 hasLayer: function (layer) { 6682 return !!layer && (stamp(layer) in this._layers); 6683 }, 6684 6685 /* @method eachLayer(fn: Function, context?: Object): this 6686 * Iterates over the layers of the map, optionally specifying context of the iterator function. 6687 * ``` 6688 * map.eachLayer(function(layer){ 6689 * layer.bindPopup('Hello'); 6690 * }); 6691 * ``` 6692 */ 6693 eachLayer: function (method, context) { 6694 for (var i in this._layers) { 6695 method.call(context, this._layers[i]); 6696 } 6697 return this; 6698 }, 6699 6700 _addLayers: function (layers) { 6701 layers = layers ? (isArray(layers) ? layers : [layers]) : []; 6702 6703 for (var i = 0, len = layers.length; i < len; i++) { 6704 this.addLayer(layers[i]); 6705 } 6706 }, 6707 6708 _addZoomLimit: function (layer) { 6709 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) { 6710 this._zoomBoundLayers[stamp(layer)] = layer; 6711 this._updateZoomLevels(); 6712 } 6713 }, 6714 6715 _removeZoomLimit: function (layer) { 6716 var id = stamp(layer); 6717 6718 if (this._zoomBoundLayers[id]) { 6719 delete this._zoomBoundLayers[id]; 6720 this._updateZoomLevels(); 6721 } 6722 }, 6723 6724 _updateZoomLevels: function () { 6725 var minZoom = Infinity, 6726 maxZoom = -Infinity, 6727 oldZoomSpan = this._getZoomSpan(); 6728 6729 for (var i in this._zoomBoundLayers) { 6730 var options = this._zoomBoundLayers[i].options; 6731 6732 minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom); 6733 maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom); 6734 } 6735 6736 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom; 6737 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom; 6738 6739 // @section Map state change events 6740 // @event zoomlevelschange: Event 6741 // Fired when the number of zoomlevels on the map is changed due 6742 // to adding or removing a layer. 6743 if (oldZoomSpan !== this._getZoomSpan()) { 6744 this.fire('zoomlevelschange'); 6745 } 6746 6747 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) { 6748 this.setZoom(this._layersMaxZoom); 6749 } 6750 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) { 6751 this.setZoom(this._layersMinZoom); 6752 } 6753 } 6754}); 6755 6756/* 6757 * @class LayerGroup 6758 * @aka L.LayerGroup 6759 * @inherits Layer 6760 * 6761 * Used to group several layers and handle them as one. If you add it to the map, 6762 * any layers added or removed from the group will be added/removed on the map as 6763 * well. Extends `Layer`. 6764 * 6765 * @example 6766 * 6767 * ```js 6768 * L.layerGroup([marker1, marker2]) 6769 * .addLayer(polyline) 6770 * .addTo(map); 6771 * ``` 6772 */ 6773 6774var LayerGroup = Layer.extend({ 6775 6776 initialize: function (layers, options) { 6777 setOptions(this, options); 6778 6779 this._layers = {}; 6780 6781 var i, len; 6782 6783 if (layers) { 6784 for (i = 0, len = layers.length; i < len; i++) { 6785 this.addLayer(layers[i]); 6786 } 6787 } 6788 }, 6789 6790 // @method addLayer(layer: Layer): this 6791 // Adds the given layer to the group. 6792 addLayer: function (layer) { 6793 var id = this.getLayerId(layer); 6794 6795 this._layers[id] = layer; 6796 6797 if (this._map) { 6798 this._map.addLayer(layer); 6799 } 6800 6801 return this; 6802 }, 6803 6804 // @method removeLayer(layer: Layer): this 6805 // Removes the given layer from the group. 6806 // @alternative 6807 // @method removeLayer(id: Number): this 6808 // Removes the layer with the given internal ID from the group. 6809 removeLayer: function (layer) { 6810 var id = layer in this._layers ? layer : this.getLayerId(layer); 6811 6812 if (this._map && this._layers[id]) { 6813 this._map.removeLayer(this._layers[id]); 6814 } 6815 6816 delete this._layers[id]; 6817 6818 return this; 6819 }, 6820 6821 // @method hasLayer(layer: Layer): Boolean 6822 // Returns `true` if the given layer is currently added to the group. 6823 // @alternative 6824 // @method hasLayer(id: Number): Boolean 6825 // Returns `true` if the given internal ID is currently added to the group. 6826 hasLayer: function (layer) { 6827 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers); 6828 }, 6829 6830 // @method clearLayers(): this 6831 // Removes all the layers from the group. 6832 clearLayers: function () { 6833 return this.eachLayer(this.removeLayer, this); 6834 }, 6835 6836 // @method invoke(methodName: String, â¦): this 6837 // Calls `methodName` on every layer contained in this group, passing any 6838 // additional parameters. Has no effect if the layers contained do not 6839 // implement `methodName`. 6840 invoke: function (methodName) { 6841 var args = Array.prototype.slice.call(arguments, 1), 6842 i, layer; 6843 6844 for (i in this._layers) { 6845 layer = this._layers[i]; 6846 6847 if (layer[methodName]) { 6848 layer[methodName].apply(layer, args); 6849 } 6850 } 6851 6852 return this; 6853 }, 6854 6855 onAdd: function (map) { 6856 this.eachLayer(map.addLayer, map); 6857 }, 6858 6859 onRemove: function (map) { 6860 this.eachLayer(map.removeLayer, map); 6861 }, 6862 6863 // @method eachLayer(fn: Function, context?: Object): this 6864 // Iterates over the layers of the group, optionally specifying context of the iterator function. 6865 // ```js 6866 // group.eachLayer(function (layer) { 6867 // layer.bindPopup('Hello'); 6868 // }); 6869 // ``` 6870 eachLayer: function (method, context) { 6871 for (var i in this._layers) { 6872 method.call(context, this._layers[i]); 6873 } 6874 return this; 6875 }, 6876 6877 // @method getLayer(id: Number): Layer 6878 // Returns the layer with the given internal ID. 6879 getLayer: function (id) { 6880 return this._layers[id]; 6881 }, 6882 6883 // @method getLayers(): Layer[] 6884 // Returns an array of all the layers added to the group. 6885 getLayers: function () { 6886 var layers = []; 6887 this.eachLayer(layers.push, layers); 6888 return layers; 6889 }, 6890 6891 // @method setZIndex(zIndex: Number): this 6892 // Calls `setZIndex` on every layer contained in this group, passing the z-index. 6893 setZIndex: function (zIndex) { 6894 return this.invoke('setZIndex', zIndex); 6895 }, 6896 6897 // @method getLayerId(layer: Layer): Number 6898 // Returns the internal ID for a layer 6899 getLayerId: function (layer) { 6900 return stamp(layer); 6901 } 6902}); 6903 6904 6905// @factory L.layerGroup(layers?: Layer[], options?: Object) 6906// Create a layer group, optionally given an initial set of layers and an `options` object. 6907var layerGroup = function (layers, options) { 6908 return new LayerGroup(layers, options); 6909}; 6910 6911/* 6912 * @class FeatureGroup 6913 * @aka L.FeatureGroup 6914 * @inherits LayerGroup 6915 * 6916 * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers: 6917 * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip)) 6918 * * Events are propagated to the `FeatureGroup`, so if the group has an event 6919 * handler, it will handle events from any of the layers. This includes mouse events 6920 * and custom events. 6921 * * Has `layeradd` and `layerremove` events 6922 * 6923 * @example 6924 * 6925 * ```js 6926 * L.featureGroup([marker1, marker2, polyline]) 6927 * .bindPopup('Hello world!') 6928 * .on('click', function() { alert('Clicked on a member of the group!'); }) 6929 * .addTo(map); 6930 * ``` 6931 */ 6932 6933var FeatureGroup = LayerGroup.extend({ 6934 6935 addLayer: function (layer) { 6936 if (this.hasLayer(layer)) { 6937 return this; 6938 } 6939 6940 layer.addEventParent(this); 6941 6942 LayerGroup.prototype.addLayer.call(this, layer); 6943 6944 // @event layeradd: LayerEvent 6945 // Fired when a layer is added to this `FeatureGroup` 6946 return this.fire('layeradd', {layer: layer}); 6947 }, 6948 6949 removeLayer: function (layer) { 6950 if (!this.hasLayer(layer)) { 6951 return this; 6952 } 6953 if (layer in this._layers) { 6954 layer = this._layers[layer]; 6955 } 6956 6957 layer.removeEventParent(this); 6958 6959 LayerGroup.prototype.removeLayer.call(this, layer); 6960 6961 // @event layerremove: LayerEvent 6962 // Fired when a layer is removed from this `FeatureGroup` 6963 return this.fire('layerremove', {layer: layer}); 6964 }, 6965 6966 // @method setStyle(style: Path options): this 6967 // Sets the given path options to each layer of the group that has a `setStyle` method. 6968 setStyle: function (style) { 6969 return this.invoke('setStyle', style); 6970 }, 6971 6972 // @method bringToFront(): this 6973 // Brings the layer group to the top of all other layers 6974 bringToFront: function () { 6975 return this.invoke('bringToFront'); 6976 }, 6977 6978 // @method bringToBack(): this 6979 // Brings the layer group to the back of all other layers 6980 bringToBack: function () { 6981 return this.invoke('bringToBack'); 6982 }, 6983 6984 // @method getBounds(): LatLngBounds 6985 // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children). 6986 getBounds: function () { 6987 var bounds = new LatLngBounds(); 6988 6989 for (var id in this._layers) { 6990 var layer = this._layers[id]; 6991 bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng()); 6992 } 6993 return bounds; 6994 } 6995}); 6996 6997// @factory L.featureGroup(layers: Layer[]) 6998// Create a feature group, optionally given an initial set of layers. 6999var featureGroup = function (layers) { 7000 return new FeatureGroup(layers); 7001}; 7002 7003/* 7004 * @class Icon 7005 * @aka L.Icon 7006 * 7007 * Represents an icon to provide when creating a marker. 7008 * 7009 * @example 7010 * 7011 * ```js 7012 * var myIcon = L.icon({ 7013 * iconUrl: 'my-icon.png', 7014 * iconRetinaUrl: '[email protected]', 7015 * iconSize: [38, 95], 7016 * iconAnchor: [22, 94], 7017 * popupAnchor: [-3, -76], 7018 * shadowUrl: 'my-icon-shadow.png', 7019 * shadowRetinaUrl: '[email protected]', 7020 * shadowSize: [68, 95], 7021 * shadowAnchor: [22, 94] 7022 * }); 7023 * 7024 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); 7025 * ``` 7026 * 7027 * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default. 7028 * 7029 */ 7030 7031var Icon = Class.extend({ 7032 7033 /* @section 7034 * @aka Icon options 7035 * 7036 * @option iconUrl: String = null 7037 * **(required)** The URL to the icon image (absolute or relative to your script path). 7038 * 7039 * @option iconRetinaUrl: String = null 7040 * The URL to a retina sized version of the icon image (absolute or relative to your 7041 * script path). Used for Retina screen devices. 7042 * 7043 * @option iconSize: Point = null 7044 * Size of the icon image in pixels. 7045 * 7046 * @option iconAnchor: Point = null 7047 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon 7048 * will be aligned so that this point is at the marker's geographical location. Centered 7049 * by default if size is specified, also can be set in CSS with
7049negative margins. 7050 * 7051 * @option popupAnchor: Point = [0, 0] 7052 * The coordinates of the point from which popups will "open", relative to the icon anchor. 7053 * 7054 * @option tooltipAnchor: Point = [0, 0] 7055 * The coordinates of the point from which tooltips will "open", relative to the icon anchor. 7056 * 7057 * @option shadowUrl: String = null 7058 * The URL to the icon shadow image. If not specified, no shadow image will be created. 7059 * 7060 * @option shadowRetinaUrl: String = null 7061 * 7062 * @option shadowSize: Point = null 7063 * Size of the shadow image in pixels. 7064 * 7065 * @option shadowAnchor: Point = null 7066 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same 7067 * as iconAnchor if not specified). 7068 * 7069 * @option className: String = '' 7070 * A custom class name to assign to both icon and shadow images. Empty by default. 7071 */ 7072 7073 options: { 7074 popupAnchor: [0, 0], 7075 tooltipAnchor: [0, 0] 7076 }, 7077 7078 initialize: function (options) { 7079 setOptions(this, options); 7080 }, 7081 7082 // @method createIcon(oldIcon?: HTMLElement): HTMLElement 7083 // Called internally when the icon has to be shown, returns a `<img>` HTML element 7084 // styled according to the options. 7085 createIcon: function (oldIcon) { 7086 return this._createIcon('icon', oldIcon); 7087 }, 7088 7089 // @method createShadow(oldIcon?: HTMLElement): HTMLElement 7090 // As `createIcon`, but for the shadow beneath it. 7091 createShadow: function (oldIcon) { 7092 return this._createIcon('shadow', oldIcon); 7093 }, 7094 7095 _createIcon: function (name, oldIcon) { 7096 var src = this._getIconUrl(name); 7097 7098 if (!src) { 7099 if (name === 'icon') { 7100 throw new Error('iconUrl not set in Icon options (see the docs).'); 7101 } 7102 return null; 7103 } 7104 7105 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null); 7106 this._setIconStyles(img, name); 7107 7108 return img; 7109 }, 7110 7111 _setIconStyles: function (img, name) { 7112 var options = this.options; 7113 var sizeOption = options[name + 'Size']; 7114 7115 if (typeof sizeOption === 'number') { 7116 sizeOption = [sizeOption, sizeOption]; 7117 } 7118 7119 var size = toPoint(sizeOption), 7120 anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor || 7121 size && size.divideBy(2, true)); 7122 7123 img.className = 'leaflet-marker-' + name + ' ' + (options.className || ''); 7124 7125 if (anchor) { 7126 img.style.marginLeft = (-anchor.x) + 'px'; 7127 img.style.marginTop = (-anchor.y) + 'px'; 7128 } 7129 7130 if (size) { 7131 img.style.width = size.x + 'px'; 7132 img.style.height = size.y + 'px'; 7133 } 7134 }, 7135 7136 _createImg: function (src, el) { 7137 el = el || document.createElement('img'); 7138 el.src = src; 7139 return el; 7140 }, 7141 7142 _getIconUrl: function (name) { 7143 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url']; 7144 } 7145}); 7146 7147 7148// @factory L.icon(options: Icon options) 7149// Creates an icon instance with the given options. 7150function icon(options) { 7151 return new Icon(options); 7152} 7153 7154/* 7155 * @miniclass Icon.Default (Icon) 7156 * @aka L.Icon.Default 7157 * @section 7158 * 7159 * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when 7160 * no icon is specified. Points to the blue marker image distributed with Leaflet 7161 * releases. 7162 * 7163 * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options` 7164 * (which is a set of `Icon options`). 7165 * 7166 * If you want to _completely_ replace the default icon, override the 7167 * `L.Marker.prototype.options.icon` with your own icon instead. 7168 */ 7169 7170var IconDefault = Icon.extend({ 7171 7172 options: { 7173 iconUrl: 'marker-icon.png', 7174 iconRetinaUrl: 'marker-icon-2x.png', 7175 shadowUrl: 'marker-shadow.png', 7176 iconSize: [25, 41], 7177 iconAnchor: [12, 41], 7178 popupAnchor: [1, -34], 7179 tooltipAnchor: [16, -28], 7180 shadowSize: [41, 41] 7181 }, 7182 7183 _getIconUrl: function (name) { 7184 if (!IconDefault.imagePath) { // Deprecated, backwards-compatibility only 7185 IconDefault.imagePath = this._detectIconPath(); 7186 } 7187 7188 // @option imagePath: String 7189 // `Icon.Default` will try to auto-detect the location of the 7190 // blue icon images. If you are placing these images in a non-standard 7191 // way, set this option to point to the right path. 7192 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name); 7193 }, 7194 7195 _detectIconPath: function () { 7196 var el = create$1('div', 'leaflet-default-icon-path', document.body); 7197 var path = getStyle(el, 'background-image') || 7198 getStyle(el, 'backgroundImage'); // IE8 7199 7200 document.body.removeChild(el); 7201 7202 if (path === null || path.indexOf('url') !== 0) { 7203 path = ''; 7204 } else { 7205 path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, ''); 7206 } 7207 7208 return path; 7209 } 7210}); 7211 7212/* 7213 * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. 7214 */ 7215 7216 7217/* @namespace Marker 7218 * @section Interaction handlers 7219 * 7220 * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example: 7221 * 7222 * ```js 7223 * marker.dragging.disable(); 7224 * ``` 7225 * 7226 * @property dragging: Handler 7227 * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)). 7228 */ 7229 7230var MarkerDrag = Handler.extend({ 7231 initialize: function (marker) { 7232 this._marker = marker; 7233 }, 7234 7235 addHooks: function () { 7236 var icon = this._marker._icon; 7237 7238 if (!this._draggable) { 7239 this._draggable = new Draggable(icon, icon, true); 7240 } 7241 7242 this._draggable.on({ 7243 dragstart: this._onDragStart, 7244 predrag: this._onPreDrag, 7245 drag: this._onDrag, 7246 dragend: this._onDragEnd 7247 }, this).enable(); 7248 7249 addClass(icon, 'leaflet-marker-draggable'); 7250 }, 7251 7252 removeHooks: function () { 7253 this._draggable.off({ 7254 dragstart: this._onDragStart, 7255 predrag: this._onPreDrag, 7256 drag: this._onDrag, 7257 dragend: this._onDragEnd 7258 }, this).disable(); 7259 7260 if (this._marker._icon) { 7261 removeClass(this._marker._icon, 'leaflet-marker-draggable'); 7262 } 7263 }, 7264 7265 moved: function () { 7266 return this._draggable && this._draggable._moved; 7267 }, 7268 7269 _adjustPan: function (e) { 7270 var marker = this._marker, 7271 map = marker._map, 7272 speed = this._marker.options.autoPanSpeed, 7273 padding = this._marker.options.autoPanPadding, 7274 iconPos = getPosition(marker._icon), 7275 bounds = map.getPixelBounds(), 7276 origin = map.getPixelOrigin(); 7277 7278 var panBounds = toBounds( 7279 bounds.min._subtract(origin).add(padding), 7280 bounds.max._subtract(origin).subtract(padding) 7281 ); 7282 7283 if (!panBounds.contains(iconPos)) { 7284 // Compute incremental movement 7285 var movement = toPoint( 7286 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) - 7287 (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x), 7288 7289 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) - 7290 (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y) 7291 ).multiplyBy(speed); 7292 7293 map.panBy(movement, {animate: false}); 7294 7295 this._draggable._newPos._add(movement); 7296 this._draggable._startPos._add(movement); 7297 7298 setPosition(marker._icon, this._draggable._newPos); 7299 this._onDrag(e); 7300
vendor: 20,622 bytes, lines 7301-8069
7301 this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); 7302 } 7303 }, 7304 7305 _onDragStart: function () { 7306 // @section Dragging events 7307 // @event dragstart: Event 7308 // Fired when the user starts dragging the marker. 7309 7310 // @event movestart: Event 7311 // Fired when the marker starts moving (because of dragging). 7312 7313 this._oldLatLng = this._marker.getLatLng(); 7314 this._marker 7315 .closePopup() 7316 .fire('movestart') 7317 .fire('dragstart'); 7318 }, 7319 7320 _onPreDrag: function (e) { 7321 if (this._marker.options.autoPan) { 7322 cancelAnimFrame(this._panRequest); 7323 this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); 7324 } 7325 }, 7326 7327 _onDrag: function (e) { 7328 var marker = this._marker, 7329 shadow = marker._shadow, 7330 iconPos = getPosition(marker._icon), 7331 latlng = marker._map.layerPointToLatLng(iconPos); 7332 7333 // update shadow position 7334 if (shadow) { 7335 setPosition(shadow, iconPos); 7336 } 7337 7338 marker._latlng = latlng; 7339 e.latlng = latlng; 7340 e.oldLatLng = this._oldLatLng; 7341 7342 // @event drag: Event 7343 // Fired repeatedly while the user drags the marker. 7344 marker 7345 .fire('move', e) 7346 .fire('drag', e); 7347 }, 7348 7349 _onDragEnd: function (e) { 7350 // @event dragend: DragEndEvent 7351 // Fired when the user stops dragging the marker. 7352 7353 cancelAnimFrame(this._panRequest); 7354 7355 // @event moveend: Event 7356 // Fired when the marker stops moving (because of dragging). 7357 delete this._oldLatLng; 7358 this._marker 7359 .fire('moveend') 7360 .fire('dragend', e); 7361 } 7362}); 7363 7364/* 7365 * @class Marker 7366 * @inherits Interactive layer 7367 * @aka L.Marker 7368 * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`. 7369 * 7370 * @example 7371 * 7372 * ```js 7373 * L.marker([50.5, 30.5]).addTo(map); 7374 * ``` 7375 */ 7376 7377var Marker = Layer.extend({ 7378 7379 // @section 7380 // @aka Marker options 7381 options: { 7382 // @option icon: Icon = * 7383 // Icon instance to use for rendering the marker. 7384 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon. 7385 // If not specified, a common instance of `L.Icon.Default` is used. 7386 icon: new IconDefault(), 7387 7388 // Option inherited from "Interactive layer" abstract class 7389 interactive: true, 7390 7391 // @option keyboard: Boolean = true 7392 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter. 7393 keyboard: true, 7394 7395 // @option title: String = '' 7396 // Text for the browser tooltip that appear on marker hover (no tooltip by default). 7397 title: '', 7398 7399 // @option alt: String = '' 7400 // Text for the `alt` attribute of the icon image (useful for accessibility). 7401 alt: '', 7402 7403 // @option zIndexOffset: Number = 0 7404 // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively). 7405 zIndexOffset: 0, 7406 7407 // @option opacity: Number = 1.0 7408 // The opacity of the marker. 7409 opacity: 1, 7410 7411 // @option riseOnHover: Boolean = false 7412 // If `true`, the marker will get on top of others when you hover the mouse over it. 7413 riseOnHover: false, 7414 7415 // @option riseOffset: Number = 250 7416 // The z-index offset used for the `riseOnHover` feature. 7417 riseOffset: 250, 7418 7419 // @option pane: String = 'markerPane' 7420 // `Map pane` where the markers icon will be added. 7421 pane: 'markerPane', 7422 7423 // @option bubblingMouseEvents: Boolean = false 7424 // When `true`, a mouse event on this marker will trigger the same event on the map 7425 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). 7426 bubblingMouseEvents: false, 7427 7428 // @section Draggable marker options 7429 // @option draggable: Boolean = false 7430 // Whether the marker is draggable with mouse/touch or not. 7431 draggable: false, 7432 7433 // @option autoPan: Boolean = false 7434 // Whether to pan the map when dragging this marker near its edge or not. 7435 autoPan: false, 7436 7437 // @option autoPanPadding: Point = Point(50, 50) 7438 // Distance (in pixels to the left/right and to the top/bottom) of the 7439 // map edge to start panning the map. 7440 autoPanPadding: [50, 50], 7441 7442 // @option autoPanSpeed: Number = 10 7443 // Number of pixels the map should pan by. 7444 autoPanSpeed: 10 7445 }, 7446 7447 /* @section 7448 * 7449 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods: 7450 */ 7451 7452 initialize: function (latlng, options) { 7453 setOptions(this, options); 7454 this._latlng = toLatLng(latlng); 7455 }, 7456 7457 onAdd: function (map) { 7458 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation; 7459 7460 if (this._zoomAnimated) { 7461 map.on('zoomanim', this._animateZoom, this); 7462 } 7463 7464 this._initIcon(); 7465 this.update(); 7466 }, 7467 7468 onRemove: function (map) { 7469 if (this.dragging && this.dragging.enabled()) { 7470 this.options.draggable = true; 7471 this.dragging.removeHooks(); 7472 } 7473 delete this.dragging; 7474 7475 if (this._zoomAnimated) { 7476 map.off('zoomanim', this._animateZoom, this); 7477 } 7478 7479 this._removeIcon(); 7480 this._removeShadow(); 7481 }, 7482 7483 getEvents: function () { 7484 return { 7485 zoom: this.update, 7486 viewreset: this.update 7487 }; 7488 }, 7489 7490 // @method getLatLng: LatLng 7491 // Returns the current geographical position of the marker. 7492 getLatLng: function () { 7493 return this._latlng; 7494 }, 7495 7496 // @method setLatLng(latlng: LatLng): this 7497 // Changes the marker position to the given point. 7498 setLatLng: function (latlng) { 7499 var oldLatLng = this._latlng; 7500 this._latlng = toLatLng(latlng); 7501 this.update(); 7502 7503 // @event move: Event 7504 // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`. 7505 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); 7506 }, 7507 7508 // @method setZIndexOffset(offset: Number): this 7509 // Changes the [zIndex offset](#marker-zindexoffset) of the marker. 7510 setZIndexOffset: function (offset) { 7511 this.options.zIndexOffset = offset; 7512 return this.update(); 7513 }, 7514 7515 // @method setIcon(icon: Icon): this 7516 // Changes the marker icon. 7517 setIcon: function (icon) { 7518 7519 this.options.icon = icon; 7520 7521 if (this._map) { 7522 this._initIcon(); 7523 this.update(); 7524 } 7525 7526 if (this._popup) { 7527 this.bindPopup(this._popup, this._popup.options); 7528 } 7529 7530 return this; 7531 }, 7532 7533 getElement: function () { 7534 return this._icon; 7535 }, 7536 7537 update: function () { 7538 7539 if (this._icon && this._map) { 7540 var pos = this._map.latLngToLayerPoint(this._latlng).round(); 7541 this._setPos(pos); 7542 } 7543 7544 return this; 7545 }, 7546 7547 _initIcon: function () { 7548 var options = this.options, 7549 classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); 7550 7551 var icon = options.icon.createIcon(this._icon), 7552 addIcon = false; 7553 7554 // if we're not reusing the icon, remove the old one and init new one 7555 if (icon !== this._icon) { 7556 if (this._icon) { 7557 this._removeIcon(); 7558 } 7559 addIcon = true; 7560 7561 if (options.title) { 7562 icon.title = options.title; 7563 } 7564 7565 if (icon.tagName === 'IMG') { 7566 icon.alt = options.alt || ''; 7567 } 7568 } 7569 7570 addClass(icon, classToAdd); 7571 7572 if (options.keyboard) { 7573 icon.tabIndex = '0'; 7574 } 7575 7576 this._icon = icon; 7577 7578 if (options.riseOnHover) { 7579 this.on({ 7580 mouseover: this._bringToFront, 7581 mouseout: this._resetZIndex 7582 }); 7583 } 7584 7585 var newShadow = options.icon.createShadow(this._shadow), 7586 addShadow = false; 7587 7588 if (newShadow !== this._shadow) { 7589 this._removeShadow(); 7590 addShadow = true; 7591 } 7592 7593 if (newShadow) { 7594 addClass(newShadow, classToAdd); 7595 newShadow.alt = ''; 7596 } 7597 this._shadow = newShadow; 7598 7599 7600 if (options.opacity < 1) { 7601 this._updateOpacity(); 7602 } 7603 7604 7605 if (addIcon) { 7606 this.getPane().appendChild(this._icon); 7607 } 7608 this._initInteraction(); 7609 if (newShadow && addShadow) { 7610 this.getPane('shadowPane').appendChild(this._shadow); 7611 } 7612 }, 7613 7614 _removeIcon: function () { 7615 if (this.options.riseOnHover) { 7616 this.off({ 7617 mouseover: this._bringToFront, 7618 mouseout: this._resetZIndex 7619 }); 7620 } 7621 7622 remove(this._icon); 7623 this.removeInteractiveTarget(this._icon); 7624 7625 this._icon = null; 7626 }, 7627 7628 _removeShadow: function () { 7629 if (this._shadow) { 7630 remove(this._shadow); 7631 } 7632 this._shadow = null; 7633 }, 7634 7635 _setPos: function (pos) { 7636 setPosition(this._icon, pos); 7637 7638 if (this._shadow) { 7639 setPosition(this._shadow, pos); 7640 } 7641 7642 this._zIndex = pos.y + this.options.zIndexOffset; 7643 7644 this._resetZIndex(); 7645 }, 7646 7647 _updateZIndex: function (offset) { 7648 this._icon.style.zIndex = this._zIndex + offset; 7649 }, 7650 7651 _animateZoom: function (opt) { 7652 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); 7653 7654 this._setPos(pos); 7655 }, 7656 7657 _initInteraction: function () { 7658 7659 if (!this.options.interactive) { return; } 7660 7661 addClass(this._icon, 'leaflet-interactive'); 7662 7663 this.addInteractiveTarget(this._icon); 7664 7665 if (MarkerDrag) { 7666 var draggable = this.options.draggable; 7667 if (this.dragging) { 7668 draggable = this.dragging.enabled(); 7669 this.dragging.disable(); 7670 } 7671 7672 this.dragging = new MarkerDrag(this); 7673 7674 if (draggable) { 7675 this.dragging.enable(); 7676 } 7677 } 7678 }, 7679 7680 // @method setOpacity(opacity: Number): this 7681 // Changes the opacity of the marker. 7682 setOpacity: function (opacity) { 7683 this.options.opacity = opacity; 7684 if (this._map) { 7685 this._updateOpacity(); 7686 } 7687 7688 return this; 7689 }, 7690 7691 _updateOpacity: function () { 7692 var opacity = this.options.opacity; 7693 7694 setOpacity(this._icon, opacity); 7695 7696 if (this._shadow) { 7697 setOpacity(this._shadow, opacity); 7698 } 7699 }, 7700 7701 _bringToFront: function () { 7702 this._updateZIndex(this.options.riseOffset); 7703 }, 7704 7705 _resetZIndex: function () { 7706 this._updateZIndex(0); 7707 }, 7708 7709 _getPopupAnchor: function () { 7710 return this.options.icon.options.popupAnchor; 7711 }, 7712 7713 _getTooltipAnchor: function () { 7714 return this.options.icon.options.tooltipAnchor; 7715 } 7716}); 7717 7718 7719// factory L.marker(latlng: LatLng, options? : Marker options) 7720 7721// @factory L.marker(latlng: LatLng, options? : Marker options) 7722// Instantiates a Marker object given a geographical point and optionally an options object. 7723function marker(latlng, options) { 7724 return new Marker(latlng, options); 7725} 7726 7727/* 7728 * @class Path 7729 * @aka L.Path 7730 * @inherits Interactive layer 7731 * 7732 * An abstract class that contains options and constants shared between vector 7733 * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`. 7734 */ 7735 7736var Path = Layer.extend({ 7737 7738 // @section 7739 // @aka Path options 7740 options: { 7741 // @option stroke: Boolean = true 7742 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles. 7743 stroke: true, 7744 7745 // @option color: String = '#3388ff' 7746 // Stroke color 7747 color: '#3388ff', 7748 7749 // @option weight: Number = 3 7750 // Stroke width in pixels 7751 weight: 3, 7752 7753 // @option opacity: Number = 1.0 7754 // Stroke opacity 7755 opacity: 1, 7756 7757 // @option lineCap: String= 'round' 7758 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke. 7759 lineCap: 'round', 7760 7761 // @option lineJoin: String = 'round' 7762 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke. 7763 lineJoin: 'round', 7764 7765 // @option dashArray: String = null 7766 // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). 7767 dashArray: null, 7768 7769 // @option dashOffset: String = null 7770 // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility). 7771 dashOffset: null, 7772 7773 // @option fill: Boolean = depends 7774 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles. 7775 fill: false, 7776 7777 // @option fillColor: String = * 7778 // Fill color. Defaults to the value of the [`color`](#path-color) option 7779 fillColor: null, 7780 7781 // @option fillOpacity: Number = 0.2 7782 // Fill opacity. 7783 fillOpacity: 0.2, 7784 7785 // @option fillRule: String = 'evenodd' 7786 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined. 7787 fillRule: 'evenodd', 7788 7789 // className: '', 7790 7791 // Option inherited from "Interactive layer" abstract class 7792 interactive: true, 7793 7794 // @option bubblingMouseEvents: Boolean = true 7795 // When `true`, a mouse event on this path will trigger the same event on the map 7796 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). 7797 bubblingMouseEvents: true 7798 }, 7799 7800 beforeAdd: function (map) { 7801 // Renderer is set here because we need to call renderer.getEvents 7802 // before this.getEvents. 7803 this._renderer = map.getRenderer(this); 7804 }, 7805 7806 onAdd: function () { 7807 this._renderer._initPath(this); 7808 this._reset(); 7809 this._renderer._addPath(this); 7810 }, 7811 7812 onRemove: function () { 7813 this._renderer._removePath(this); 7814 }, 7815 7816 // @method redraw(): this 7817 // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses. 7818 redraw: function () { 7819 if (this._map) { 7820 this._renderer._updatePath(this); 7821 } 7822 return this; 7823 }, 7824 7825 // @method setStyle(style: Path options): this 7826 // Changes the appearance of a Path based on the options in the `Path options` object. 7827 setStyle: function (style) { 7828 setOptions(this, style); 7829 if (this._renderer) { 7830 this._renderer._updateStyle(this); 7831 } 7832 return this; 7833 }, 7834 7835 // @method bringToFront(): this 7836 // Brings the layer to the top of all path layers. 7837 bringToFront: function () { 7838 if (this._renderer) { 7839 this._renderer._bringToFront(this); 7840 } 7841 return this; 7842 }, 7843 7844 // @method bringToBack(): this 7845 // Brings the layer to the bottom of all path layers. 7846 bringToBack: function () { 7847 if (this._renderer) { 7848 this._renderer._bringToBack(this); 7849 } 7850 return this; 7851 }, 7852 7853 getElement: function () { 7854 return this._path; 7855 }, 7856 7857 _reset: function () { 7858 // defined in child classes 7859 this._project(); 7860 this._update(); 7861 }, 7862 7863 _clickTolerance: function () { 7864 // used when doing hit detection for Canvas layers 7865 return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance; 7866 } 7867}); 7868 7869/* 7870 * @class CircleMarker 7871 * @aka L.CircleMarker 7872 * @inherits Path 7873 * 7874 * A circle of a fixed size with radius specified in pixels. Extends `Path`. 7875 */ 7876 7877var CircleMarker = Path.extend({ 7878 7879 // @section 7880 // @aka CircleMarker options 7881 options: { 7882 fill: true, 7883 7884 // @option radius: Number = 10 7885 // Radius of the circle marker, in pixels 7886 radius: 10 7887 }, 7888 7889 initialize: function (latlng, options) { 7890 setOptions(this, options); 7891 this._latlng = toLatLng(latlng); 7892 this._radius = this.options.radius; 7893 }, 7894 7895 // @method setLatLng(latLng: LatLng): this 7896 // Sets the position of a circle marker to a new location. 7897 setLatLng: function (latlng) { 7898 this._latlng = toLatLng(latlng); 7899 this.redraw(); 7900 return this.fire('move', {latlng: this._latlng}); 7901 }, 7902 7903 // @method getLatLng(): LatLng 7904 // Returns the current geographical position of the circle marker 7905 getLatLng: function () { 7906 return this._latlng; 7907 }, 7908 7909 // @method setRadius(radius: Number): this 7910 // Sets the radius of a circle marker. Units are in pixels. 7911 setRadius: function (radius) { 7912 this.options.radius = this._radius = radius; 7913 return this.redraw(); 7914 }, 7915 7916 // @method getRadius(): Number 7917 // Returns the current radius of the circle 7918 getRadius: function () { 7919 return this._radius; 7920 }, 7921 7922 setStyle : function (options) { 7923 var radius = options && options.radius || this._radius; 7924 Path.prototype.setStyle.call(this, options); 7925 this.setRadius(radius); 7926 return this; 7927 }, 7928 7929 _project: function () { 7930 this._point = this._map.latLngToLayerPoint(this._latlng); 7931 this._updateBounds(); 7932 }, 7933 7934 _updateBounds: function () { 7935 var r = this._radius, 7936 r2 = this._radiusY || r, 7937 w = this._clickTolerance(), 7938 p = [r + w, r2 + w]; 7939 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p)); 7940 }, 7941 7942 _update: function () { 7943 if (this._map) { 7944 this._updatePath(); 7945 } 7946 }, 7947 7948 _updatePath: function () { 7949 this._renderer._updateCircle(this); 7950 }, 7951 7952 _empty: function () { 7953 return this._radius && !this._renderer._bounds.intersects(this._pxBounds); 7954 }, 7955 7956 // Needed by the `Canvas` renderer for interactivity 7957 _containsPoint: function (p) { 7958 return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); 7959 } 7960}); 7961 7962 7963// @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options) 7964// Instantiates a circle marker object given a geographical point, and an optional options object. 7965function circleMarker(latlng, options) { 7966 return new CircleMarker(latlng, options); 7967} 7968 7969/* 7970 * @class Circle 7971 * @aka L.Circle 7972 * @inherits CircleMarker 7973 * 7974 * A class for drawing circle overlays on a map. Extends `CircleMarker`. 7975 * 7976 * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion). 7977 * 7978 * @example 7979 * 7980 * ```js 7981 * L.circle([50.5, 30.5], {radius: 200}).addTo(map); 7982 * ``` 7983 */ 7984 7985var Circle = CircleMarker.extend({ 7986 7987 initialize: function (latlng, options, legacyOptions) { 7988 if (typeof options === 'number') { 7989 // Backwards compatibility with 0.7.x factory (latlng, radius, options?) 7990 options = extend({}, legacyOptions, {radius: options}); 7991 } 7992 setOptions(this, options); 7993 this._latlng = toLatLng(latlng); 7994 7995 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); } 7996 7997 // @section 7998 // @aka Circle options 7999 // @option radius: Number; Radius of the circle, in meters. 8000 this._mRadius = this.options.radius; 8001 }, 8002 8003 // @method setRadius(radius: Number): this 8004 // Sets the radius of a circle. Units are in meters. 8005 setRadius: function (radius) { 8006 this._mRadius = radius; 8007 return this.redraw(); 8008 }, 8009 8010 // @method getRadius(): Number 8011 // Returns the current radius of a circle. Units are in meters. 8012 getRadius: function () { 8013 return this._mRadius; 8014 }, 8015 8016 // @method getBounds(): LatLngBounds 8017 // Returns the `LatLngBounds` of the path. 8018 getBounds: function () { 8019 var half = [this._radius, this._radiusY || this._radius]; 8020 8021 return new LatLngBounds( 8022 this._map.layerPointToLatLng(this._point.subtract(half)), 8023 this._map.layerPointToLatLng(this._point.add(half))); 8024 }, 8025 8026 setStyle: Path.prototype.setStyle, 8027 8028 _project: function () { 8029 8030 var lng = this._latlng.lng, 8031 lat = this._latlng.lat, 8032 map = this._map, 8033 crs = map.options.crs; 8034 8035 if (crs.distance === Earth.distance) { 8036 var d = Math.PI / 180, 8037 latR = (this._mRadius / Earth.R) / d, 8038 top = map.project([lat + latR, lng]), 8039 bottom = map.project([lat - latR, lng]), 8040 p = top.add(bottom).divideBy(2), 8041 lat2 = map.unproject(p).lat, 8042 lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) / 8043 (Math.cos(lat * d) * Math.cos(lat2 * d))) / d; 8044 8045 if (isNaN(lngR) || lngR === 0) { 8046 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425 8047 } 8048 8049 this._point = p.subtract(map.getPixelOrigin()); 8050 this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x; 8051 this._radiusY = p.y - top.y; 8052 8053 } else { 8054 var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0])); 8055 8056 this._point = map.latLngToLayerPoint(this._latlng); 8057 this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x; 8058 } 8059 8060 this._updateBounds(); 8061 } 8062}); 8063 8064// @factory L.circle(latlng: LatLng, options?: Circle options) 8065// Instantiates a circle object given a geographical point, and an options object 8066// which contains the circle radius. 8067// @alternative 8068// @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options) 8069// Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
8070// Do not use in new applications or plugins. 8071function circle(latlng, options, legacyOptions) { 8072 return new Circle(latlng, options, legacyOptions); 8073} 8074 8075/* 8076 * @class Polyline 8077 * @aka L.Polyline 8078 * @inherits Path 8079 * 8080 * A class for drawing polyline overlays on a map. Extends `Path`. 8081 * 8082 * @example 8083 * 8084 * ```js 8085 * // create a red polyline from an array of LatLng points 8086 * var latlngs = [ 8087 * [45.51, -122.68], 8088 * [37.77, -122.43], 8089 * [34.04, -118.2] 8090 * ]; 8091 * 8092 * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map); 8093 * 8094 * // zoom the map to the polyline 8095 * map.fitBounds(polyline.getBounds()); 8096 * ``` 8097 * 8098 * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape: 8099 * 8100 * ```js 8101 * // create a red polyline from an array of arrays of LatLng points 8102 * var latlngs = [ 8103 * [[45.51, -122.68], 8104 * [37.77, -122.43], 8105 * [34.04, -118.2]], 8106 * [[40.78, -73.91], 8107 * [41.83, -87.62], 8108 * [32.76, -96.72]] 8109 * ]; 8110 * ``` 8111 */ 8112 8113 8114var Polyline = Path.extend({ 8115 8116 // @section 8117 // @aka Polyline options 8118 options: { 8119 // @option smoothFactor: Number = 1.0 8120 // How much to simplify the polyline on each zoom level. More means 8121 // better performance and smoother look, and less means more accurate representation. 8122 smoothFactor: 1.0, 8123 8124 // @option noClip: Boolean = false 8125 // Disable polyline clipping. 8126 noClip: false 8127 }, 8128 8129 initialize: function (latlngs, options) { 8130 setOptions(this, options); 8131 this._setLatLngs(latlngs); 8132 }, 8133 8134 // @method getLatLngs(): LatLng[] 8135 // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline. 8136 getLatLngs: function () { 8137 return this._latlngs; 8138 }, 8139 8140 // @method setLatLngs(latlngs: LatLng[]): this 8141 // Replaces all the points in the polyline with the given array of geographical points. 8142 setLatLngs: function (latlngs) { 8143 this._setLatLngs(latlngs); 8144 return this.redraw(); 8145 }, 8146 8147 // @method isEmpty(): Boolean 8148 // Returns `true` if the Polyline has no LatLngs. 8149 isEmpty: function () { 8150 return !this._latlngs.length; 8151 }, 8152 8153 // @method closestLayerPoint(p: Point): Point 8154 // Returns the point closest to `p` on the Polyline. 8155 closestLayerPoint: function (p) { 8156 var minDistance = Infinity, 8157 minPoint = null, 8158 closest = _sqClosestPointOnSegment, 8159 p1, p2; 8160 8161 for (var j = 0, jLen = this._parts.length; j < jLen; j++) { 8162 var points = this._parts[j]; 8163 8164 for (var i = 1, len = points.length; i < len; i++) { 8165 p1 = points[i - 1]; 8166 p2 = points[i]; 8167 8168 var sqDist = closest(p, p1, p2, true); 8169 8170 if (sqDist < minDistance) { 8171 minDistance = sqDist; 8172 minPoint = closest(p, p1, p2); 8173 } 8174 } 8175 } 8176 if (minPoint) { 8177 minPoint.distance = Math.sqrt(minDistance); 8178 } 8179 return minPoint; 8180 }, 8181 8182 // @method getCenter(): LatLng 8183 // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline. 8184 getCenter: function () { 8185 // throws error when not yet added to map as this center calculation requires projected coordinates 8186 if (!this._map) { 8187 throw new Error('Must add layer to map before using getCenter()'); 8188 } 8189 8190 var i, halfDist, segDist, dist, p1, p2, ratio, 8191 points = this._rings[0], 8192 len = points.length; 8193 8194 if (!len) { return null; } 8195 8196 // polyline centroid algorithm; only uses the first ring if there are multiple 8197 8198 for (i = 0, halfDist = 0; i < len - 1; i++) { 8199 halfDist += points[i].distanceTo(points[i + 1]) / 2; 8200 } 8201 8202 // The line is so small in the current view that all points are on the same pixel. 8203 if (halfDist === 0) { 8204 return this._map.layerPointToLatLng(points[0]); 8205 } 8206 8207 for (i = 0, dist = 0; i < len - 1; i++) { 8208 p1 = points[i]; 8209 p2 = points[i + 1]; 8210 segDist = p1.distanceTo(p2); 8211 dist += segDist; 8212 8213 if (dist > halfDist) { 8214 ratio = (dist - halfDist) / segDist; 8215 return this._map.layerPointToLatLng([ 8216 p2.x - ratio * (p2.x - p1.x), 8217 p2.y - ratio * (p2.y - p1.y) 8218 ]); 8219 } 8220 } 8221 }, 8222 8223 // @method getBounds(): LatLngBounds 8224 // Returns the `LatLngBounds` of the path. 8225 getBounds: function () { 8226 return this._bounds; 8227 }, 8228 8229 // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this 8230 // Adds a given point to the polyline. By default, adds to the first ring of 8231 // the polyline in case of a multi-polyline, but can be overridden by passing 8232 // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)). 8233 addLatLng: function (latlng, latlngs) { 8234 latlngs = latlngs || this._defaultShape(); 8235 latlng = toLatLng(latlng); 8236 latlngs.push(latlng); 8237 this._bounds.extend(latlng); 8238 return this.redraw(); 8239 }, 8240 8241 _setLatLngs: function (latlngs) {
vendor: 12,310 bytes, lines 8242-8691
8242 this._bounds = new LatLngBounds(); 8243 this._latlngs = this._convertLatLngs(latlngs); 8244 }, 8245 8246 _defaultShape: function () { 8247 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0]; 8248 }, 8249 8250 // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way 8251 _convertLatLngs: function (latlngs) { 8252 var result = [], 8253 flat = isFlat(latlngs); 8254 8255 for (var i = 0, len = latlngs.length; i < len; i++) { 8256 if (flat) { 8257 result[i] = toLatLng(latlngs[i]); 8258 this._bounds.extend(result[i]); 8259 } else { 8260 result[i] = this._convertLatLngs(latlngs[i]); 8261 } 8262 } 8263 8264 return result; 8265 }, 8266 8267 _project: function () { 8268 var pxBounds = new Bounds(); 8269 this._rings = []; 8270 this._projectLatlngs(this._latlngs, this._rings, pxBounds); 8271 8272 var w = this._clickTolerance(), 8273 p = new Point(w, w); 8274 8275 if (this._bounds.isValid() && pxBounds.isValid()) { 8276 pxBounds.min._subtract(p); 8277 pxBounds.max._add(p); 8278 this._pxBounds = pxBounds; 8279 } 8280 }, 8281 8282 // recursively turns latlngs into a set of rings with projected coordinates 8283 _projectLatlngs: function (latlngs, result, projectedBounds) { 8284 var flat = latlngs[0] instanceof LatLng, 8285 len = latlngs.length, 8286 i, ring; 8287 8288 if (flat) { 8289 ring = []; 8290 for (i = 0; i < len; i++) { 8291 ring[i] = this._map.latLngToLayerPoint(latlngs[i]); 8292 projectedBounds.extend(ring[i]); 8293 } 8294 result.push(ring); 8295 } else { 8296 for (i = 0; i < len; i++) { 8297 this._projectLatlngs(latlngs[i], result, projectedBounds); 8298 } 8299 } 8300 }, 8301 8302 // clip polyline by renderer bounds so that we have less to render for performance 8303 _clipPoints: function () { 8304 var bounds = this._renderer._bounds; 8305 8306 this._parts = []; 8307 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { 8308 return; 8309 } 8310 8311 if (this.options.noClip) { 8312 this._parts = this._rings; 8313 return; 8314 } 8315 8316 var parts = this._parts, 8317 i, j, k, len, len2, segment, points; 8318 8319 for (i = 0, k = 0, len = this._rings.length; i < len; i++) { 8320 points = this._rings[i]; 8321 8322 for (j = 0, len2 = points.length; j < len2 - 1; j++) { 8323 segment = clipSegment(points[j], points[j + 1], bounds, j, true); 8324 8325 if (!segment) { continue; } 8326 8327 parts[k] = parts[k] || []; 8328 parts[k].push(segment[0]); 8329 8330 // if segment goes out of screen, or it's the last one, it's the end of the line part 8331 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) { 8332 parts[k].push(segment[1]); 8333 k++; 8334 } 8335 } 8336 } 8337 }, 8338 8339 // simplify each clipped part of the polyline for performance 8340 _simplifyPoints: function () { 8341 var parts = this._parts, 8342 tolerance = this.options.smoothFactor; 8343 8344 for (var i = 0, len = parts.length; i < len; i++) { 8345 parts[i] = simplify(parts[i], tolerance); 8346 } 8347 }, 8348 8349 _update: function () { 8350 if (!this._map) { return; } 8351 8352 this._clipPoints(); 8353 this._simplifyPoints(); 8354 this._updatePath(); 8355 }, 8356 8357 _updatePath: function () { 8358 this._renderer._updatePoly(this); 8359 }, 8360 8361 // Needed by the `Canvas` renderer for interactivity 8362 _containsPoint: function (p, closed) { 8363 var i, j, k, len, len2, part, 8364 w = this._clickTolerance(); 8365 8366 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; } 8367 8368 // hit detection for polylines 8369 for (i = 0, len = this._parts.length; i < len; i++) { 8370 part = this._parts[i]; 8371 8372 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { 8373 if (!closed && (j === 0)) { continue; } 8374 8375 if (pointToSegmentDistance(p, part[k], part[j]) <= w) { 8376 return true; 8377 } 8378 } 8379 } 8380 return false; 8381 } 8382}); 8383 8384// @factory L.polyline(latlngs: LatLng[], options?: Polyline options) 8385// Instantiates a polyline object given an array of geographical points and 8386// optionally an options object. You can create a `Polyline` object with 8387// multiple separate lines (`MultiPolyline`) by passing an array of arrays 8388// of geographic points. 8389function polyline(latlngs, options) { 8390 return new Polyline(latlngs, options); 8391} 8392 8393// Retrocompat. Allow plugins to support Leaflet versions before and after 1.1. 8394Polyline._flat = _flat; 8395 8396/* 8397 * @class Polygon 8398 * @aka L.Polygon 8399 * @inherits Polyline 8400 * 8401 * A class for drawing polygon overlays on a map. Extends `Polyline`. 8402 * 8403 * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one â it's better to filter out such points. 8404 * 8405 * 8406 * @example 8407 * 8408 * ```js 8409 * // create a red polygon from an array of LatLng points 8410 * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]]; 8411 * 8412 * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map); 8413 * 8414 * // zoom the map to the polygon 8415 * map.fitBounds(polygon.getBounds()); 8416 * ``` 8417 * 8418 * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape: 8419 * 8420 * ```js 8421 * var latlngs = [ 8422 * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring 8423 * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole 8424 * ]; 8425 * ``` 8426 * 8427 * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape. 8428 * 8429 * ```js 8430 * var latlngs = [ 8431 * [ // first polygon 8432 * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring 8433 * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole 8434 * ], 8435 * [ // second polygon 8436 * [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]] 8437 * ] 8438 * ]; 8439 * ``` 8440 */ 8441 8442var Polygon = Polyline.extend({ 8443 8444 options: { 8445 fill: true 8446 }, 8447 8448 isEmpty: function () { 8449 return !this._latlngs.length || !this._latlngs[0].length; 8450 }, 8451 8452 getCenter: function () { 8453 // throws error when not yet added to map as this center calculation requires projected coordinates 8454 if (!this._map) { 8455 throw new Error('Must add layer to map before using getCenter()'); 8456 } 8457 8458 var i, j, p1, p2, f, area, x, y, center, 8459 points = this._rings[0], 8460 len = points.length; 8461 8462 if (!len) { return null; } 8463 8464 // polygon centroid algorithm; only uses the first ring if there are multiple 8465 8466 area = x = y = 0; 8467 8468 for (i = 0, j = len - 1; i < len; j = i++) { 8469 p1 = points[i]; 8470 p2 = points[j]; 8471 8472 f = p1.y * p2.x - p2.y * p1.x; 8473 x += (p1.x + p2.x) * f; 8474 y += (p1.y + p2.y) * f; 8475 area += f * 3; 8476 } 8477 8478 if (area === 0) { 8479 // Polygon is so small that all points are on same pixel. 8480 center = points[0]; 8481 } else { 8482 center = [x / area, y / area]; 8483 } 8484 return this._map.layerPointToLatLng(center); 8485 }, 8486 8487 _convertLatLngs: function (latlngs) { 8488 var result = Polyline.prototype._convertLatLngs.call(this, latlngs), 8489 len = result.length; 8490 8491 // remove last point if it equals first one 8492 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) { 8493 result.pop(); 8494 } 8495 return result; 8496 }, 8497 8498 _setLatLngs: function (latlngs) { 8499 Polyline.prototype._setLatLngs.call(this, latlngs); 8500 if (isFlat(this._latlngs)) { 8501 this._latlngs = [this._latlngs]; 8502 } 8503 }, 8504 8505 _defaultShape: function () { 8506 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0]; 8507 }, 8508 8509 _clipPoints: function () { 8510 // polygons need a different clipping algorithm so we redefine that 8511 8512 var bounds = this._renderer._bounds, 8513 w = this.options.weight, 8514 p = new Point(w, w); 8515 8516 // increase clip padding by stroke width to avoid stroke on clip edges 8517 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p)); 8518 8519 this._parts = []; 8520 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { 8521 return; 8522 } 8523 8524 if (this.options.noClip) { 8525 this._parts = this._rings; 8526 return; 8527 } 8528 8529 for (var i = 0, len = this._rings.length, clipped; i < len; i++) { 8530 clipped = clipPolygon(this._rings[i], bounds, true); 8531 if (clipped.length) { 8532 this._parts.push(clipped); 8533 } 8534 } 8535 }, 8536 8537 _updatePath: function () { 8538 this._renderer._updatePoly(this, true); 8539 }, 8540 8541 // Needed by the `Canvas` renderer for interactivity 8542 _containsPoint: function (p) { 8543 var inside = false, 8544 part, p1, p2, i, j, k, len, len2; 8545 8546 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; } 8547 8548 // ray casting algorithm for detecting if point is in polygon 8549 for (i = 0, len = this._parts.length; i < len; i++) { 8550 part = this._parts[i]; 8551 8552 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { 8553 p1 = part[j]; 8554 p2 = part[k]; 8555 8556 if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) { 8557 inside = !inside; 8558 } 8559 } 8560 } 8561 8562 // also check if it's on polygon stroke 8563 return inside || Polyline.prototype._containsPoint.call(this, p, true); 8564 } 8565 8566}); 8567 8568 8569// @factory L.polygon(latlngs: LatLng[], options?: Polyline options) 8570function polygon(latlngs, options) { 8571 return new Polygon(latlngs, options); 8572} 8573 8574/* 8575 * @class GeoJSON 8576 * @aka L.GeoJSON 8577 * @inherits FeatureGroup 8578 * 8579 * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse 8580 * GeoJSON data and display it on the map. Extends `FeatureGroup`. 8581 * 8582 * @example 8583 * 8584 * ```js 8585 * L.geoJSON(data, { 8586 * style: function (feature) { 8587 * return {color: feature.properties.color}; 8588 * } 8589 * }).bindPopup(function (layer) { 8590 * return layer.feature.properties.description; 8591 * }).addTo(map); 8592 * ``` 8593 */ 8594 8595var GeoJSON = FeatureGroup.extend({ 8596 8597 /* @section 8598 * @aka GeoJSON options 8599 * 8600 * @option pointToLayer: Function = * 8601 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally 8602 * called when data is added, passing the GeoJSON point feature and its `LatLng`. 8603 * The default is to spawn a default `Marker`: 8604 * ```js 8605 * function(geoJsonPoint, latlng) { 8606 * return L.marker(latlng); 8607 * } 8608 * ``` 8609 * 8610 * @option style: Function = * 8611 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons, 8612 * called internally when data is added. 8613 * The default value is to not override any defaults: 8614 * ```js 8615 * function (geoJsonFeature) { 8616 * return {} 8617 * } 8618 * ``` 8619 * 8620 * @option onEachFeature: Function = * 8621 * A `Function` that will be called once for each created `Feature`, after it has 8622 * been created and styled. Useful for attaching events and popups to features. 8623 * The default is to do nothing with the newly created layers: 8624 * ```js 8625 * function (feature, layer) {} 8626 * ``` 8627 * 8628 * @option filter: Function = * 8629 * A `Function` that will be used to decide whether to include a feature or not. 8630 * The default is to include all features: 8631 * ```js 8632 * function (geoJsonFeature) { 8633 * return true; 8634 * } 8635 * ``` 8636 * Note: dynamically changing the `filter` option will have effect only on newly 8637 * added data. It will _not_ re-evaluate already included features. 8638 * 8639 * @option coordsToLatLng: Function = * 8640 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s. 8641 * The default is the `coordsToLatLng` static method. 8642 */ 8643 8644 initialize: function (geojson, options) { 8645 setOptions(this, options); 8646 8647 this._layers = {}; 8648 8649 if (geojson) { 8650 this.addData(geojson); 8651 } 8652 }, 8653 8654 // @method addData( <GeoJSON> data ): this 8655 // Adds a GeoJSON object to the layer. 8656 addData: function (geojson) { 8657 var features = isArray(geojson) ? geojson : geojson.features, 8658 i, len, feature; 8659 8660 if (features) { 8661 for (i = 0, len = features.length; i < len; i++) { 8662 // only add this if geometry or geometries are set and not null 8663 feature = features[i]; 8664 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) { 8665 this.addData(feature); 8666 } 8667 } 8668 return this; 8669 } 8670 8671 var options = this.options; 8672 8673 if (options.filter && !options.filter(geojson)) { return this; } 8674 8675 var layer = geometryToLayer(geojson, options); 8676 if (!layer) { 8677 return this; 8678 } 8679 layer.feature = asFeature(geojson); 8680 8681 layer.defaultOptions = layer.options; 8682 this.resetStyle(layer); 8683 8684 if (options.onEachFeature) { 8685 options.onEachFeature(geojson, layer); 8686 } 8687 8688 return this.addLayer(layer); 8689 }, 8690 8691 // @method resetStyle( <Path>
8691 layer ): this 8692 // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events. 8693 resetStyle: function (layer) { 8694 // reset any custom styles 8695 layer.options = extend({}, layer.defaultOptions); 8696 this._setLayerStyle(layer, this.options.style); 8697 return this; 8698 }, 8699 8700 // @method setStyle( <Function> style ): this 8701 // Changes styles of GeoJSON vector layers with the given style function. 8702 setStyle: function (style) { 8703 return this.eachLayer(function (layer) { 8704 this._setLayerStyle(layer, style); 8705 }, this); 8706 }, 8707 8708 _setLayerStyle: function (layer, style) { 8709 if (typeof style === 'function') { 8710 style = style(layer.feature); 8711 } 8712 if (layer.setStyle) { 8713 layer.setStyle(style); 8714 } 8715 } 8716}); 8717 8718// @section 8719// There are several static functions which can be called without instantiating L.GeoJSON: 8720 8721// @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer 8722// Creates a `Layer` from a given GeoJSON feature. Can use a custom 8723// [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng) 8724// functions if provided as options. 8725function geometryToLayer(geojson, options) { 8726 8727 var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson, 8728 coords = geometry ? geometry.coordinates : null, 8729 layers = [], 8730 pointToLayer = options && options.pointToLayer, 8731 _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng, 8732 latlng, latlngs, i, len; 8733 8734 if (!coords && !geometry) { 8735 return null; 8736 } 8737 8738 switch (geometry.type) { 8739 case 'Point': 8740 latlng = _coordsToLatLng(coords); 8741 return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng); 8742 8743 case 'MultiPoint': 8744 for (i = 0, len = coords.length; i < len; i++) { 8745 latlng = _coordsToLatLng(coords[i]); 8746 layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng)); 8747 } 8748 return new FeatureGroup(layers); 8749 8750 case 'LineString': 8751 case 'MultiLineString': 8752 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng); 8753 return new Polyline(latlngs, options); 8754 8755 case 'Polygon': 8756 case 'MultiPolygon': 8757 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng); 8758 return new Polygon(latlngs, options); 8759 8760 case 'GeometryCollection': 8761 for (i = 0, len = geometry.geometries.length; i < len; i++) { 8762 var layer = geometryToLayer({ 8763 geometry: geometry.geometries[i], 8764 type: 'Feature', 8765 properties: geojson.properties 8766 }, options); 8767 8768 if (layer) { 8769 layers.push(layer); 8770 } 8771 } 8772 return new FeatureGroup(layers); 8773 8774 default: 8775 throw new Error('Invalid GeoJSON object.'); 8776 } 8777} 8778 8779// @function coordsToLatLng(coords: Array): LatLng 8780// Creates a `LatLng` object from an array of 2 numbers (longitude, latitude) 8781// or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points. 8782function coordsToLatLng(coords) { 8783 return new LatLng(coords[1], coords[0], coords[2]); 8784} 8785 8786// @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array 8787// Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array. 8788// `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default). 8789// Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function. 8790function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) { 8791 var latlngs = []; 8792 8793 for (var i = 0, len = coords.length, latlng; i < len; i++) { 8794 latlng = levelsDeep ? 8795 coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) : 8796 (_coordsToLatLng || coordsToLatLng)(coords[i]); 8797 8798 latlngs.push(latlng); 8799 } 8800 8801 return latlngs; 8802} 8803 8804// @function latLngToCoords(latlng: LatLng, precision?: Number): Array 8805// Reverse of [`coordsToLatLng`](#geojson-coordstolatlng) 8806function latLngToCoords(latlng, precision) { 8807 precision = typeof precision === 'number' ? precision : 6; 8808 return latlng.alt !== undefined ?
vendor: 12,134 bytes, lines 8809-9245
8809 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] : 8810 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)]; 8811} 8812 8813// @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array 8814// Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs) 8815// `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default. 8816function latLngsToCoords(latlngs, levelsDeep, closed, precision) { 8817 var coords = []; 8818 8819 for (var i = 0, len = latlngs.length; i < len; i++) { 8820 coords.push(levelsDeep ? 8821 latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) : 8822 latLngToCoords(latlngs[i], precision)); 8823 } 8824 8825 if (!levelsDeep && closed) { 8826 coords.push(coords[0]); 8827 } 8828 8829 return coords; 8830} 8831 8832function getFeature(layer, newGeometry) { 8833 return layer.feature ? 8834 extend({}, layer.feature, {geometry: newGeometry}) : 8835 asFeature(newGeometry); 8836} 8837 8838// @function asFeature(geojson: Object): Object 8839// Normalize GeoJSON geometries/features into GeoJSON features. 8840function asFeature(geojson) { 8841 if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') { 8842 return geojson; 8843 } 8844 8845 return { 8846 type: 'Feature', 8847 properties: {}, 8848 geometry: geojson 8849 }; 8850} 8851 8852var PointToGeoJSON = { 8853 toGeoJSON: function (precision) { 8854 return getFeature(this, { 8855 type: 'Point', 8856 coordinates: latLngToCoords(this.getLatLng(), precision) 8857 }); 8858 } 8859}; 8860 8861// @namespace Marker 8862// @method toGeoJSON(): Object 8863// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature). 8864Marker.include(PointToGeoJSON); 8865 8866// @namespace CircleMarker 8867// @method toGeoJSON(): Object 8868// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature). 8869Circle.include(PointToGeoJSON); 8870CircleMarker.include(PointToGeoJSON); 8871 8872 8873// @namespace Polyline 8874// @method toGeoJSON(): Object 8875// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature). 8876Polyline.include({ 8877 toGeoJSON: function (precision) { 8878 var multi = !isFlat(this._latlngs); 8879 8880 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision); 8881 8882 return getFeature(this, { 8883 type: (multi ? 'Multi' : '') + 'LineString', 8884 coordinates: coords 8885 }); 8886 } 8887}); 8888 8889// @namespace Polygon 8890// @method toGeoJSON(): Object 8891// Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature). 8892Polygon.include({ 8893 toGeoJSON: function (precision) { 8894 var holes = !isFlat(this._latlngs), 8895 multi = holes && !isFlat(this._latlngs[0]); 8896 8897 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision); 8898 8899 if (!holes) { 8900 coords = [coords]; 8901 } 8902 8903 return getFeature(this, { 8904 type: (multi ? 'Multi' : '') + 'Polygon', 8905 coordinates: coords 8906 }); 8907 } 8908}); 8909 8910 8911// @namespace LayerGroup 8912LayerGroup.include({ 8913 toMultiPoint: function (precision) { 8914 var coords = []; 8915 8916 this.eachLayer(function (layer) { 8917 coords.push(layer.toGeoJSON(precision).geometry.coordinates); 8918 }); 8919 8920 return getFeature(this, { 8921 type: 'MultiPoint', 8922 coordinates: coords 8923 }); 8924 }, 8925 8926 // @method toGeoJSON(): Object 8927 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`). 8928 toGeoJSON: function (precision) { 8929 8930 var type = this.feature && this.feature.geometry && this.feature.geometry.type; 8931 8932 if (type === 'MultiPoint') { 8933 return this.toMultiPoint(precision); 8934 } 8935 8936 var isGeometryCollection = type === 'GeometryCollection', 8937 jsons = []; 8938 8939 this.eachLayer(function (layer) { 8940 if (layer.toGeoJSON) { 8941 var json = layer.toGeoJSON(precision); 8942 if (isGeometryCollection) { 8943 jsons.push(json.geometry); 8944 } else { 8945 var feature = asFeature(json); 8946 // Squash nested feature collections 8947 if (feature.type === 'FeatureCollection') { 8948 jsons.push.apply(jsons, feature.features); 8949 } else { 8950 jsons.push(feature); 8951 } 8952 } 8953 } 8954 }); 8955 8956 if (isGeometryCollection) { 8957 return getFeature(this, { 8958 geometries: jsons, 8959 type: 'GeometryCollection' 8960 }); 8961 } 8962 8963 return { 8964 type: 'FeatureCollection', 8965 features: jsons 8966 }; 8967 } 8968}); 8969 8970// @namespace GeoJSON 8971// @factory L.geoJSON(geojson?: Object, options?: GeoJSON options) 8972// Creates a GeoJSON layer. Optionally accepts an object in 8973// [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map 8974// (you can alternatively add it later with `addData` method) and an `options` object. 8975function geoJSON(geojson, options) { 8976 return new GeoJSON(geojson, options); 8977} 8978 8979// Backward compatibility. 8980var geoJson = geoJSON; 8981 8982/* 8983 * @class ImageOverlay 8984 * @aka L.ImageOverlay 8985 * @inherits Interactive layer 8986 * 8987 * Used to load and display a single image over specific bounds of the map. Extends `Layer`. 8988 * 8989 * @example 8990 * 8991 * ```js 8992 * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg', 8993 * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]]; 8994 * L.imageOverlay(imageUrl, imageBounds).addTo(map); 8995 * ``` 8996 */ 8997 8998var ImageOverlay = Layer.extend({ 8999 9000 // @section 9001 // @aka ImageOverlay options 9002 options: { 9003 // @option opacity: Number = 1.0 9004 // The opacity of the image overlay. 9005 opacity: 1, 9006 9007 // @option alt: String = '' 9008 // Text for the `alt` attribute of the image (useful for accessibility). 9009 alt: '', 9010 9011 // @option interactive: Boolean = false 9012 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered. 9013 interactive: false, 9014 9015 // @option crossOrigin: Boolean|String = false 9016 // Whether the crossOrigin attribute will be added to the image. 9017 // If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data. 9018 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values. 9019 crossOrigin: false, 9020 9021 // @option errorOverlayUrl: String = '' 9022 // URL to the overlay image to show in place of the overlay that failed to load. 9023 errorOverlayUrl: '', 9024 9025 // @option zIndex: Number = 1 9026 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer. 9027 zIndex: 1, 9028 9029 // @option className: String = '' 9030 // A custom class name to assign to the image. Empty by default. 9031 className: '' 9032 }, 9033 9034 initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) 9035 this._url = url; 9036 this._bounds = toLatLngBounds(bounds); 9037 9038 setOptions(this, options); 9039 }, 9040 9041 onAdd: function () { 9042 if (!this._image) { 9043 this._initImage(); 9044 9045 if (this.options.opacity < 1) { 9046 this._updateOpacity(); 9047 } 9048 } 9049 9050 if (this.options.interactive) { 9051 addClass(this._image, 'leaflet-interactive'); 9052 this.addInteractiveTarget(this._image); 9053 } 9054 9055 this.getPane().appendChild(this._image); 9056 this._reset(); 9057 }, 9058 9059 onRemove: function () { 9060 remove(this._image); 9061 if (this.options.interactive) { 9062 this.removeInteractiveTarget(this._image); 9063 } 9064 }, 9065 9066 // @method setOpacity(opacity: Number): this 9067 // Sets the opacity of the overlay. 9068 setOpacity: function (opacity) { 9069 this.options.opacity = opacity; 9070 9071 if (this._image) { 9072 this._updateOpacity(); 9073 } 9074 return this; 9075 }, 9076 9077 setStyle: function (styleOpts) { 9078 if (styleOpts.opacity) { 9079 this.setOpacity(styleOpts.opacity); 9080 } 9081 return this; 9082 }, 9083 9084 // @method bringToFront(): this 9085 // Brings the layer to the top of all overlays. 9086 bringToFront: function () { 9087 if (this._map) { 9088 toFront(this._image); 9089 } 9090 return this; 9091 }, 9092 9093 // @method bringToBack(): this 9094 // Brings the layer to the bottom of all overlays. 9095 bringToBack: function () { 9096 if (this._map) { 9097 toBack(this._image); 9098 } 9099 return this; 9100 }, 9101 9102 // @method setUrl(url: String): this 9103 // Changes the URL of the image. 9104 setUrl: function (url) { 9105 this._url = url; 9106 9107 if (this._image) { 9108 this._image.src = url; 9109 } 9110 return this; 9111 }, 9112 9113 // @method setBounds(bounds: LatLngBounds): this 9114 // Update the bounds that this ImageOverlay covers 9115 setBounds: function (bounds) { 9116 this._bounds = toLatLngBounds(bounds); 9117 9118 if (this._map) { 9119 this._reset(); 9120 } 9121 return this; 9122 }, 9123 9124 getEvents: function () { 9125 var events = { 9126 zoom: this._reset, 9127 viewreset: this._reset 9128 }; 9129 9130 if (this._zoomAnimated) { 9131 events.zoomanim = this._animateZoom; 9132 } 9133 9134 return events; 9135 }, 9136 9137 // @method setZIndex(value: Number): this 9138 // Changes the [zIndex](#imageoverlay-zindex) of the image overlay. 9139 setZIndex: function (value) { 9140 this.options.zIndex = value; 9141 this._updateZIndex(); 9142 return this; 9143 }, 9144 9145 // @method getBounds(): LatLngBounds 9146 // Get the bounds that this ImageOverlay covers 9147 getBounds: function () { 9148 return this._bounds; 9149 }, 9150 9151 // @method getElement(): HTMLElement 9152 // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement) 9153 // used by this overlay. 9154 getElement: function () { 9155 return this._image; 9156 }, 9157 9158 _initImage: function () { 9159 var wasElementSupplied = this._url.tagName === 'IMG'; 9160 var img = this._image = wasElementSupplied ? this._url : create$1('img'); 9161 9162 addClass(img, 'leaflet-image-layer'); 9163 if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); } 9164 if (this.options.className) { addClass(img, this.options.className); } 9165 9166 img.onselectstart = falseFn; 9167 img.onmousemove = falseFn; 9168 9169 // @event load: Event 9170 // Fired when the ImageOverlay layer has loaded its image 9171 img.onload = bind(this.fire, this, 'load'); 9172 img.onerror = bind(this._overlayOnError, this, 'error'); 9173 9174 if (this.options.crossOrigin || this.options.crossOrigin === '') { 9175 img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin; 9176 } 9177 9178 if (this.options.zIndex) { 9179 this._updateZIndex(); 9180 } 9181 9182 if (wasElementSupplied) { 9183 this._url = img.src; 9184 return; 9185 } 9186 9187 img.src = this._url; 9188 img.alt = this.options.alt; 9189 }, 9190 9191 _animateZoom: function (e) { 9192 var scale = this._map.getZoomScale(e.zoom), 9193 offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min; 9194 9195 setTransform(this._image, offset, scale); 9196 }, 9197 9198 _reset: function () { 9199 var image = this._image, 9200 bounds = new Bounds( 9201 this._map.latLngToLayerPoint(this._bounds.getNorthWest()), 9202 this._map.latLngToLayerPoint(this._bounds.getSouthEast())), 9203 size = bounds.getSize(); 9204 9205 setPosition(image, bounds.min); 9206 9207 image.style.width = size.x + 'px'; 9208 image.style.height = size.y + 'px'; 9209 }, 9210 9211 _updateOpacity: function () { 9212 setOpacity(this._image, this.options.opacity); 9213 }, 9214 9215 _updateZIndex: function () { 9216 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) { 9217 this._image.style.zIndex = this.options.zIndex; 9218 } 9219 }, 9220 9221 _overlayOnError: function () { 9222 // @event error: Event 9223 // Fired when the ImageOverlay layer fails to load its image 9224 this.fire('error'); 9225 9226 var errorUrl = this.options.errorOverlayUrl; 9227 if (errorUrl && this._url !== errorUrl) { 9228 this._url = errorUrl; 9229 this._image.src = errorUrl; 9230 } 9231 } 9232}); 9233 9234// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options) 9235// Instantiates an image overlay object given the URL of the image and the 9236// geographical bounds it is tied to. 9237var imageOverlay = function (url, bounds, options) { 9238 return new ImageOverlay(url, bounds, options); 9239}; 9240 9241/* 9242 * @class VideoOverlay 9243 * @aka L.VideoOverlay 9244 * @inherits ImageOverlay 9245 *
9246 * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`. 9247 * 9248 * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video) 9249 * HTML5 element. 9250 * 9251 * @example 9252 * 9253 * ```js 9254 * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm', 9255 * videoBounds = [[ 32, -130], [ 13, -100]]; 9256 * L.videoOverlay(videoUrl, videoBounds ).addTo(map); 9257 * ``` 9258 */ 9259 9260var VideoOverlay = ImageOverlay.extend({ 9261 9262 // @section 9263 // @aka VideoOverlay options 9264 options: { 9265 // @option autoplay: Boolean = true 9266 // Whether the video starts playing automatically when loaded. 9267 autoplay: true, 9268 9269 // @option loop: Boolean = true 9270 // Whether the video will loop back to the beginning when played. 9271 loop: true 9272 }, 9273 9274 _initImage: function () { 9275 var wasElementSupplied = this._url.tagName === 'VIDEO'; 9276 var vid = this._image = wasElementSupplied ? this._url : create$1('video'); 9277 9278 addClass(vid, 'leaflet-image-layer'); 9279 if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); } 9280 9281 vid.onselectstart = falseFn; 9282 vid.onmousemove = falseFn; 9283 9284 // @event load: Event 9285 // Fired when the video has finished loading the first frame 9286 vid.onloadeddata = bind(this.fire, this, 'load'); 9287 9288 if (wasElementSupplied) { 9289 var sourceElements = vid.getElementsByTagName('source'); 9290 var sources = []; 9291 for (var j = 0; j < sourceElements.length; j++) { 9292 sources.push(sourceElements[j].src); 9293 } 9294 9295 this._url = (sourceElements.length > 0) ? sources : [vid.src]; 9296 return; 9297 } 9298 9299 if (!isArray(this._url)) { this._url = [this._url]; } 9300 9301 vid.autoplay = !!this.options.autoplay; 9302 vid.loop = !!this.options.loop; 9303 for (var i = 0; i < this._url.length; i++) { 9304 var source = create$1('source'); 9305 source.src = this._url[i]; 9306 vid.appendChild(source); 9307 } 9308 } 9309 9310 // @method getElement(): HTMLVideoElement 9311 // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement) 9312 // used by this overlay. 9313}); 9314 9315 9316// @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options) 9317// Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the 9318// geographical bounds it is tied to. 9319 9320function videoOverlay(video, bounds, options) { 9321 return new VideoOverlay(video, bounds, options); 9322} 9323 9324/* 9325 * @class DivOverlay 9326 * @inherits Layer 9327 * @aka L.DivOverlay 9328 * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins. 9329 */ 9330 9331// @namespace DivOverlay 9332var DivOverlay = Layer.extend({ 9333 9334 // @section 9335 // @aka DivOverlay options 9336 options: { 9337 // @option offset: Point = Point(0, 7) 9338 // The offset of the popup position. Useful to control the anchor 9339 // of the popup when opening it on some overlays. 9340 offset: [0, 7], 9341 9342 // @option className: String = '' 9343 // A custom CSS class name to assign to the popup. 9344 className: '', 9345 9346 // @option pane: String = 'popupPane' 9347 // `Map pane` where the popup will be added. 9348 pane: 'popupPane' 9349 }, 9350 9351 initialize: function (options, source) { 9352 setOptions(this, options); 9353 9354 this._source = source; 9355 }, 9356 9357 onAdd: function (map) { 9358 this._zoomAnimated = map._zoomAnimated; 9359 9360 if (!this._container) { 9361 this._initLayout(); 9362 } 9363 9364 if (map._fadeAnimated) { 9365 setOpacity(this._container, 0); 9366 } 9367 9368 clearTimeout(this._removeTimeout); 9369 this.getPane().appendChild(this._container); 9370 this.update(); 9371 9372 if (map._fadeAnimated) { 9373 setOpacity(this._container, 1); 9374 } 9375 9376 this.bringToFront(); 9377 }, 9378 9379 onRemove: function (map) { 9380 if (map._fadeAnimated) { 9381 setOpacity(this._container, 0); 9382 this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200); 9383 } else { 9384 remove(this._container); 9385 } 9386 }, 9387 9388 // @namespace Popup 9389 // @method getLatLng: LatLng 9390 // Returns the geographical point of popup. 9391 getLatLng: function () { 9392 return this._latlng; 9393 }, 9394 9395 // @method setLatLng(latlng: LatLng): this 9396 // Sets the geographical point where the popup will open. 9397 setLatLng: function (latlng) { 9398 this._latlng = toLatLng(latlng); 9399 if (this._map) { 9400 this._updatePosition(); 9401 this._adjustPan(); 9402 } 9403 return this; 9404 }, 9405 9406 // @method getContent: String|HTMLElement 9407 // Returns the content of the popup. 9408 getContent: function () { 9409 return this._content; 9410 }, 9411 9412 // @method setContent(htmlContent: String|HTMLElement|Function): this 9413 // Sets the HTML content of the popup. If a function is passed the source layer will be passed to the function. The function should return a `String` or `HTMLElement` to be used in the popup. 9414 setContent: function (content) { 9415 this._content = content; 9416 this.update(); 9417 return this; 9418 }, 9419 9420 // @method getElement: String|HTMLElement 9421 // Alias for [getContent()](#popup-getcontent) 9422 getElement: function () { 9423 return this._container; 9424 }, 9425 9426 // @method update: null
vendor: 9,805 bytes, lines 9427-9758
9427 // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded. 9428 update: function () { 9429 if (!this._map) { return; } 9430 9431 this._container.style.visibility = 'hidden'; 9432 9433 this._updateContent(); 9434 this._updateLayout(); 9435 this._updatePosition(); 9436 9437 this._container.style.visibility = ''; 9438 9439 this._adjustPan(); 9440 }, 9441 9442 getEvents: function () { 9443 var events = { 9444 zoom: this._updatePosition, 9445 viewreset: this._updatePosition 9446 }; 9447 9448 if (this._zoomAnimated) { 9449 events.zoomanim = this._animateZoom; 9450 } 9451 return events; 9452 }, 9453 9454 // @method isOpen: Boolean 9455 // Returns `true` when the popup is visible on the map. 9456 isOpen: function () { 9457 return !!this._map && this._map.hasLayer(this); 9458 }, 9459 9460 // @method bringToFront: this 9461 // Brings this popup in front of other popups (in the same map pane). 9462 bringToFront: function () { 9463 if (this._map) { 9464 toFront(this._container); 9465 } 9466 return this; 9467 }, 9468 9469 // @method bringToBack: this 9470 // Brings this popup to the back of other popups (in the same map pane). 9471 bringToBack: function () { 9472 if (this._map) { 9473 toBack(this._container); 9474 } 9475 return this; 9476 }, 9477 9478 _updateContent: function () { 9479 if (!this._content) { return; } 9480 9481 var node = this._contentNode; 9482 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content; 9483 9484 if (typeof content === 'string') { 9485 node.innerHTML = content; 9486 } else { 9487 while (node.hasChildNodes()) { 9488 node.removeChild(node.firstChild); 9489 } 9490 node.appendChild(content); 9491 } 9492 this.fire('contentupdate'); 9493 }, 9494 9495 _updatePosition: function () { 9496 if (!this._map) { return; } 9497 9498 var pos = this._map.latLngToLayerPoint(this._latlng), 9499 offset = toPoint(this.options.offset), 9500 anchor = this._getAnchor(); 9501 9502 if (this._zoomAnimated) { 9503 setPosition(this._container, pos.add(anchor)); 9504 } else { 9505 offset = offset.add(pos).add(anchor); 9506 } 9507 9508 var bottom = this._containerBottom = -offset.y, 9509 left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x; 9510 9511 // bottom position the popup in case the height of the popup changes (images loading etc) 9512 this._container.style.bottom = bottom + 'px'; 9513 this._container.style.left = left + 'px'; 9514 }, 9515 9516 _getAnchor: function () { 9517 return [0, 0]; 9518 } 9519 9520}); 9521 9522/* 9523 * @class Popup 9524 * @inherits DivOverlay 9525 * @aka L.Popup 9526 * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to 9527 * open popups while making sure that only one popup is open at one time 9528 * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want. 9529 * 9530 * @example 9531 * 9532 * If you want to just bind a popup to marker click and then open it, it's really easy: 9533 * 9534 * ```js 9535 * marker.bindPopup(popupContent).openPopup(); 9536 * ``` 9537 * Path overlays like polylines also have a `bindPopup` method. 9538 * Here's a more complicated way to open a popup on a map: 9539 * 9540 * ```js 9541 * var popup = L.popup() 9542 * .setLatLng(latlng) 9543 * .setContent('<p>Hello world!<br />This is a nice popup.</p>') 9544 * .openOn(map); 9545 * ``` 9546 */ 9547 9548 9549// @namespace Popup 9550var Popup = DivOverlay.extend({ 9551 9552 // @section 9553 // @aka Popup options 9554 options: { 9555 // @option maxWidth: Number = 300 9556 // Max width of the popup, in pixels. 9557 maxWidth: 300, 9558 9559 // @option minWidth: Number = 50 9560 // Min width of the popup, in pixels. 9561 minWidth: 50, 9562 9563 // @option maxHeight: Number = null 9564 // If set, creates a scrollable container of the given height 9565 // inside a popup if its content exceeds it. 9566 maxHeight: null, 9567 9568 // @option autoPan: Boolean = true 9569 // Set it to `false` if you don't want the map to do panning animation 9570 // to fit the opened popup. 9571 autoPan: true, 9572 9573 // @option autoPanPaddingTopLeft: Point = null 9574 // The margin between the popup and the top left corner of the map 9575 // view after autopanning was performed. 9576 autoPanPaddingTopLeft: null, 9577 9578 // @option autoPanPaddingBottomRight: Point = null 9579 // The margin between the popup and the bottom right corner of the map 9580 // view after autopanning was performed. 9581 autoPanPaddingBottomRight: null, 9582 9583 // @option autoPanPadding: Point = Point(5, 5) 9584 // Equivalent of setting both top left and bottom right autopan padding to the same value. 9585 autoPanPadding: [5, 5], 9586 9587 // @option keepInView: Boolean = false 9588 // Set it to `true` if you want to prevent users from panning the popup 9589 // off of the screen while it is open. 9590 keepInView: false, 9591 9592 // @option closeButton: Boolean = true 9593 // Controls the presence of a close button in the popup. 9594 closeButton: true, 9595 9596 // @option autoClose: Boolean = true 9597 // Set it to `false` if you want to override the default behavior of 9598 // the popup closing when another popup is opened. 9599 autoClose: true, 9600 9601 // @option closeOnEscapeKey: Boolean = true 9602 // Set it to `false` if you want to override the default behavior of 9603 // the ESC key for closing of the popup. 9604 closeOnEscapeKey: true, 9605 9606 // @option closeOnClick: Boolean = * 9607 // Set it if you want to override the default behavior of the popup closing when user clicks 9608 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option. 9609 9610 // @option className: String = '' 9611 // A custom CSS class name to assign to the popup. 9612 className: '' 9613 }, 9614 9615 // @namespace Popup 9616 // @method openOn(map: Map): this 9617 // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`. 9618 openOn: function (map) { 9619 map.openPopup(this); 9620 return this; 9621 }, 9622 9623 onAdd: function (map) { 9624 DivOverlay.prototype.onAdd.call(this, map); 9625 9626 // @namespace Map 9627 // @section Popup events 9628 // @event popupopen: PopupEvent 9629 // Fired when a popup is opened in the map 9630 map.fire('popupopen', {popup: this}); 9631 9632 if (this._source) { 9633 // @namespace Layer 9634 // @section Popup events 9635 // @event popupopen: PopupEvent 9636 // Fired when a popup bound to this layer is opened 9637 this._source.fire('popupopen', {popup: this}, true); 9638 // For non-path layers, we toggle the popup when clicking 9639 // again the layer, so prevent the map to reopen it. 9640 if (!(this._source instanceof Path)) { 9641 this._source.on('preclick', stopPropagation); 9642 } 9643 } 9644 }, 9645 9646 onRemove: function (map) { 9647 DivOverlay.prototype.onRemove.call(this, map); 9648 9649 // @namespace Map 9650 // @section Popup events 9651 // @event popupclose: PopupEvent 9652 // Fired when a popup in the map is closed 9653 map.fire('popupclose', {popup: this}); 9654 9655 if (this._source) { 9656 // @namespace Layer 9657 // @section Popup events 9658 // @event popupclose: PopupEvent 9659 // Fired when a popup bound to this layer is closed 9660 this._source.fire('popupclose', {popup: this}, true); 9661 if (!(this._source instanceof Path)) { 9662 this._source.off('preclick', stopPropagation); 9663 } 9664 } 9665 }, 9666 9667 getEvents: function () { 9668 var events = DivOverlay.prototype.getEvents.call(this); 9669 9670 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) { 9671 events.preclick = this._close; 9672 } 9673 9674 if (this.options.keepInView) { 9675 events.moveend = this._adjustPan; 9676 } 9677 9678 return events; 9679 }, 9680 9681 _close: function () { 9682 if (this._map) { 9683 this._map.closePopup(this); 9684 } 9685 }, 9686 9687 _initLayout: function () { 9688 var prefix = 'leaflet-popup', 9689 container = this._container = create$1('div', 9690 prefix + ' ' + (this.options.className || '') + 9691 ' leaflet-zoom-animated'); 9692 9693 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container); 9694 this._contentNode = create$1('div', prefix + '-content', wrapper); 9695 9696 disableClickPropagation(wrapper); 9697 disableScrollPropagation(this._contentNode); 9698 on(wrapper, 'contextmenu', stopPropagation); 9699 9700 this._tipContainer = create$1('div', prefix + '-tip-container', container); 9701 this._tip = create$1('div', prefix + '-tip', this._tipContainer); 9702 9703 if (this.options.closeButton) { 9704 var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container); 9705 closeButton.href = '#close'; 9706 closeButton.innerHTML = '×'; 9707 9708 on(closeButton, 'click', this._onCloseButtonClick, this); 9709 } 9710 }, 9711 9712 _updateLayout: function () { 9713 var container = this._contentNode, 9714 style = container.style; 9715 9716 style.width = ''; 9717 style.whiteSpace = 'nowrap'; 9718 9719 var width = container.offsetWidth; 9720 width = Math.min(width, this.options.maxWidth); 9721 width = Math.max(width, this.options.minWidth); 9722 9723 style.width = (width + 1) + 'px'; 9724 style.whiteSpace = ''; 9725 9726 style.height = ''; 9727 9728 var height = container.offsetHeight, 9729 maxHeight = this.options.maxHeight, 9730 scrolledClass = 'leaflet-popup-scrolled'; 9731 9732 if (maxHeight && height > maxHeight) { 9733 style.height = maxHeight + 'px'; 9734 addClass(container, scrolledClass); 9735 } else { 9736 removeClass(container, scrolledClass); 9737 } 9738 9739 this._containerWidth = this._container.offsetWidth; 9740 }, 9741 9742 _animateZoom: function (e) { 9743 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center), 9744 anchor = this._getAnchor(); 9745 setPosition(this._container, pos.add(anchor)); 9746 }, 9747 9748 _adjustPan: function () { 9749 if (!this.options.autoPan) { return; } 9750 if (this._map._panAnim) { this._map._panAnim.stop(); } 9751 9752 var map = this._map, 9753 marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0, 9754 containerHeight = this._container.offsetHeight + marginBottom, 9755 containerWidth = this._containerWidth, 9756 layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom); 9757 9758 layerPos._add(getPosition(this._container));
9759 9760 var containerPos = map.layerPointToContainerPoint(layerPos), 9761 padding = toPoint(this.options.autoPanPadding), 9762 paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding), 9763 paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding), 9764 size = map.getSize(), 9765 dx = 0, 9766 dy = 0; 9767 9768 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right 9769 dx = containerPos.x + containerWidth - size.x + paddingBR.x; 9770 } 9771 if (containerPos.x - dx - paddingTL.x < 0) { // left 9772 dx = containerPos.x - paddingTL.x; 9773 } 9774 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom 9775 dy = containerPos.y + containerHeight - size.y + paddingBR.y; 9776 } 9777 if (containerPos.y - dy - paddingTL.y < 0) { // top 9778 dy = containerPos.y - paddingTL.y; 9779 } 9780 9781 // @namespace Map 9782 // @section Popup events 9783 // @event autopanstart: Event 9784 // Fired when the map starts autopanning when opening a popup. 9785 if (dx || dy) { 9786 map 9787 .fire('autopanstart') 9788 .panBy([dx, dy]); 9789 } 9790 }, 9791 9792 _onCloseButtonClick: function (e) { 9793 this._close(); 9794 stop(e); 9795 }, 9796 9797 _getAnchor: function () { 9798 // Where should we anchor the popup on the source layer? 9799 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]); 9800 } 9801 9802}); 9803 9804// @namespace Popup 9805// @factory L.popup(options?: Popup options, source?: Layer) 9806// Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers. 9807var popup = function (options, source) { 9808 return new Popup(options, source); 9809}; 9810 9811 9812/* @namespace Map 9813 * @section Interaction Options 9814 * @option closePopupOnClick: Boolean = true 9815 * Set it to `false` if you don't want popups to close when user clicks the map. 9816 */ 9817Map.mergeOptions({ 9818 closePopupOnClick: true 9819}); 9820 9821 9822// @namespace Map 9823// @section Methods for Layers and Controls 9824Map.include({ 9825 // @method openPopup(popup: Popup): this 9826 // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability). 9827 // @alternative 9828 // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this 9829 // Creates a popup with the specified content and options and opens it in the given point on a map. 9830 openPopup: function (popup, latlng, options) { 9831 if (!(popup instanceof Popup)) { 9832 popup = new Popup(options).setContent(popup); 9833 } 9834 9835 if (latlng) { 9836 popup.setLatLng(latlng); 9837 } 9838 9839 if (this.hasLayer(popup)) { 9840 return this; 9841 } 9842 9843 if (this._popup && this._popup.options.autoClose) { 9844 this.closePopup(); 9845 } 9846 9847 this._popup = popup; 9848 return this.addLayer(popup); 9849 }, 9850 9851 // @method closePopup(popup?: Popup): this 9852 // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one). 9853 closePopup: function (popup) { 9854 if (!popup || popup === this._popup) { 9855 popup = this._popup; 9856 this._popup = null; 9857 } 9858 if (popup) { 9859 this.removeLayer(popup); 9860 } 9861 return this; 9862 } 9863}); 9864 9865/* 9866 * @namespace Layer 9867 * @section Popup methods example 9868 * 9869 * All layers share a set of methods convenient for binding popups to it. 9870 * 9871 * ```js 9872 * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map); 9873 * layer.openPopup(); 9874 * layer.closePopup(); 9875 * ``` 9876 * 9877 * Popups will also be automatically opened when the layer is clicked on and closed when the layer is removed from the map or another popup is opened. 9878 */ 9879 9880// @section Popup methods 9881Layer.include({ 9882 9883 // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this 9884 // Binds a popup to the layer with the passed `content` and sets up the 9885 // necessary event listeners. If a `Function` is passed it will receive 9886 // the layer as the first argument and should return a `String` or `HTMLElement`. 9887 bindPopup: function (content, options) { 9888 9889 if (content instanceof Popup) { 9890 setOptions(content, options); 9891 this._popup = content; 9892 content._source = this; 9893 } else { 9894 if (!this._popup || options) { 9895 this._popup = new Popup(options, this); 9896 } 9897 this._popup.setContent(content); 9898 } 9899 9900 if (!this._popupHandlersAdded) { 9901 this.on({ 9902 click: this._openPopup, 9903 keypress: this._onKeyPress, 9904 remove: this.closePopup, 9905 move: this._movePopup 9906 }); 9907 this._popupHandlersAdded = true; 9908 } 9909 9910 return this; 9911 }, 9912 9913 // @method unbindPopup(): this 9914 // Removes the popup previously bound with `bindPopup`. 9915 unbindPopup: function () { 9916 if (this._popup) { 9917 this.off({ 9918 click: this._openPopup, 9919 keypress: this._onKeyPress, 9920 remove: this.closePopup, 9921 move: this._movePopup 9922 }); 9923 this._popupHandlersAdded = false; 9924 this._popup = null; 9925 } 9926 return this; 9927 }, 9928 9929 // @method openPopup(latlng?: LatLng): this 9930 // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed. 9931 openPopup: function (layer, latlng) { 9932 if (!(layer instanceof Layer)) { 9933 latlng = layer; 9934 layer = this; 9935 } 9936 9937 if (layer instanceof FeatureGroup) { 9938 for (var id in this._layers) { 9939 layer = this._layers[id]; 9940 break; 9941 } 9942 } 9943 9944 if (!latlng) { 9945 latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng(); 9946 } 9947 9948 if (this._popup && this._map) { 9949 // set popup source to this layer 9950 this._popup._source = layer; 9951 9952 // update the popup (content, layout, ect...) 9953 this._popup.update(); 9954 9955 // open the popup on the map 9956 this._map.openPopup(this._popup, latlng); 9957 } 9958 9959 return this; 9960 }, 9961 9962 // @method closePopup(): this 9963 // Closes the popup bound to this layer if it is open. 9964 closePopup: function () { 9965 if (this._popup) { 9966 this._popup._close(); 9967 } 9968 return this; 9969 }, 9970 9971 // @method togglePopup(): this 9972 // Opens or closes the popup bound to this layer depending on its current state. 9973 togglePopup: function (target) { 9974 if (this._popup) { 9975 if (this._popup._map) { 9976 this.closePopup(); 9977 } else { 9978 this.openPopup(target); 9979 } 9980 } 9981 return this; 9982 }, 9983 9984 // @method isPopupOpen(): boolean 9985 // Returns `true` if the popup bound to this layer is currently open. 9986 isPopupOpen: function () { 9987 return (this._popup ? this._popup.isOpen() : false); 9988 }, 9989 9990 // @method setPopupContent(content: String|HTMLElement|Popup): this 9991 // Sets the content of the popup bound to this layer. 9992 setPopupContent: function (content) { 9993 if (this._popup) { 9994 this._popup.setContent(content); 9995 } 9996 return this; 9997 }, 9998 9999 // @method getPopup(): Popup 10000 // Returns the popup bound to this layer. 10001 getPopup: function () { 10002 return this._popup; 10003 }, 10004 10005 _openPopup: function (e) { 10006 var layer = e.layer || e.target; 10007 10008 if (!this._popup) { 10009 return; 10010 } 10011 10012 if (!this._map) { 10013 return; 10014 } 10015 10016 // prevent map click 10017 stop(e); 10018 10019 // if this inherits from Path its a vector and we can just 10020 // open the popup at the new location 10021 if (layer instanceof Path) { 10022 this.openPopup(e.layer || e.target, e.latlng); 10023 return; 10024 } 10025 10026 // otherwise treat it like a marker and figure out 10027 // if we should toggle it open/closed 10028 if (this._map.hasLayer(this._popup) && this._popup._source === layer) { 10029 this.closePopup(); 10030 } else { 10031 this.openPopup(layer, e.latlng); 10032 } 10033 }, 10034 10035 _movePopup: function (e) { 10036 this._popup.setLatLng(e.latlng); 10037 }, 10038 10039 _onKeyPress: function (e) { 10040 if (e.originalEvent.keyCode === 13) { 10041 this._openPopup(e); 10042 } 10043 } 10044}); 10045 10046/* 10047 * @class Tooltip 10048 * @inherits DivOverlay 10049 * @aka L.Tooltip 10050 * Used to display small texts on top of map layers. 10051 * 10052 * @example 10053 * 10054 * ```js 10055 * marker.bindTooltip("my tooltip text").openTooltip(); 10056 * ``` 10057 * Note about tooltip offset. Leaflet takes two options in consideration
10058 * for computing tooltip offsetting: 10059 * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip. 10060 * Add a positive x offset to move the tooltip to the right, and a positive y offset to 10061 * move it to the bottom. Negatives will move to the left and top. 10062 * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You 10063 * should adapt this value if you use a custom icon. 10064 */ 10065 10066 10067// @namespace Tooltip 10068var Tooltip = DivOverlay.extend({ 10069 10070 // @section 10071 // @aka Tooltip options 10072 options: { 10073 // @option pane: String = 'tooltipPane' 10074 // `Map pane` where the tooltip will be added. 10075 pane: 'tooltipPane', 10076 10077 // @option offset: Point = Point(0, 0) 10078 // Optional offset of the tooltip position. 10079 offset: [0, 0], 10080 10081 // @option direction: String = 'auto' 10082 // Direction where to open the tooltip. Possible values are: `right`, `left`, 10083 // `top`, `bottom`, `center`, `auto`. 10084 // `auto` will dynamically switch between `right` and `left` according to the tooltip 10085 // position on the map. 10086 direction: 'auto', 10087 10088 // @option permanent: Boolean = false 10089 // Whether to open the tooltip permanently or only on mouseover. 10090 permanent: false, 10091 10092 // @option sticky: Boolean = false 10093 // If true, the tooltip will follow the mouse instead of being fixed at the feature center. 10094 sticky: false, 10095 10096 // @option interactive: Boolean = false 10097 // If true, the tooltip will listen to the feature events. 10098 interactive: false, 10099 10100 // @option opacity: Number = 0.9 10101 // Tooltip container opacity. 10102 opacity: 0.9 10103 }, 10104 10105 onAdd: function (map) { 10106 DivOverlay.prototype.onAdd.call(this, map); 10107 this.setOpacity(this.options.opacity); 10108 10109 // @namespace Map 10110 // @section Tooltip events 10111 // @event tooltipopen: TooltipEvent 10112 // Fired when a tooltip is opened in the map. 10113 map.fire('tooltipopen', {tooltip: this}); 10114 10115 if (this._source) { 10116 // @namespace Layer 10117 // @section Tooltip events 10118 // @event tooltipopen: TooltipEvent 10119 // Fired when a tooltip bound to this layer is opened. 10120 this._source.fire('tooltipopen', {tooltip: this}, true); 10121 } 10122 }, 10123 10124 onRemove: function (map) { 10125 DivOverlay.prototype.onRemove.call(this, map); 10126 10127 // @namespace Map 10128 // @section Tooltip events 10129 // @event tooltipclose: TooltipEvent 10130 // Fired when a tooltip in the map is closed. 10131 map.fire('tooltipclose', {tooltip: this}); 10132 10133 if (this._source) { 10134 // @namespace Layer 10135 // @section Tooltip events 10136 // @event tooltipclose: TooltipEvent 10137 // Fired when a tooltip bound to this layer is closed. 10138 this._source.fire('tooltipclose', {tooltip: this}, true); 10139 } 10140 }, 10141 10142 getEvents: function () { 10143 var events = DivOverlay.prototype.getEvents.call(this); 10144 10145 if (touch && !this.options.permanent) { 10146 events.preclick = this._close; 10147 } 10148 10149 return events; 10150 }, 10151 10152 _close: function () { 10153 if (this._map) { 10154 this._map.closeTooltip(this); 10155 } 10156 }, 10157 10158 _initLayout: function () { 10159 var prefix = 'leaflet-tooltip', 10160 className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); 10161 10162 this._contentNode = this._container = create$1('div', className); 10163 }, 10164 10165 _updateLayout: function () {}, 10166 10167 _adjustPan: function () {}, 10168 10169 _setPosition: function (pos) { 10170 var map = this._map, 10171 container = this._container, 10172 centerPoint = map.latLngToContainerPoint(map.getCenter()), 10173 tooltipPoint = map.layerPointToContainerPoint(pos), 10174 direction = this.options.direction, 10175 tooltipWidth = container.offsetWidth, 10176 tooltipHeight = container.offsetHeight, 10177 offset = toPoint(this.options.offset), 10178 anchor = this._getAnchor(); 10179 10180 if (direction === 'top') { 10181 pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true)); 10182 } else if (direction === 'bottom') { 10183 pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true)); 10184 } else if (direction === 'center') { 10185 pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true)); 10186 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) { 10187 direction = 'right'; 10188 pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true)); 10189 } else { 10190 direction = 'left'; 10191 pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true)); 10192 } 10193 10194 removeClass(container, 'leaflet-tooltip-right'); 10195 removeClass(container, 'leaflet-tooltip-left'); 10196 removeClass(container, 'leaflet-tooltip-top'); 10197 removeClass(container, 'leaflet-tooltip-bottom');
10198 addClass(container, 'leaflet-tooltip-' + direction); 10199 setPosition(container, pos); 10200 }, 10201 10202 _updatePosition: function () { 10203 var pos = this._map.latLngToLayerPoint(this._latlng); 10204 this._setPosition(pos); 10205 }, 10206 10207 setOpacity: function (opacity) { 10208 this.options.opacity = opacity; 10209 10210 if (this._container) { 10211 setOpacity(this._container, opacity); 10212 } 10213 }, 10214 10215 _animateZoom: function (e) { 10216 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center); 10217 this._setPosition(pos); 10218 }, 10219 10220 _getAnchor: function () { 10221 // Where should we anchor the tooltip on the source layer? 10222 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]); 10223 } 10224 10225}); 10226 10227// @namespace Tooltip 10228// @factory L.tooltip(options?: Tooltip options, source?: Layer) 10229// Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers. 10230var tooltip = function (options, source) { 10231 return new Tooltip(options, source); 10232}; 10233 10234// @namespace Map 10235// @section Methods for Layers and Controls 10236Map.include({ 10237 10238 // @method openTooltip(tooltip: Tooltip): this 10239 // Opens the specified tooltip. 10240 // @alternative 10241 // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this 10242 // Creates a tooltip with the specified content and options and open it. 10243 openTooltip: function (tooltip, latlng, options) { 10244 if (!(tooltip instanceof Tooltip)) { 10245 tooltip = new Tooltip(options).setContent(tooltip); 10246 } 10247 10248 if (latlng) { 10249 tooltip.setLatLng(latlng); 10250 } 10251 10252 if (this.hasLayer(tooltip)) { 10253 return this; 10254 } 10255 10256 return this.addLayer(tooltip); 10257 }, 10258 10259 // @method closeTooltip(tooltip?: Tooltip): this 10260 // Closes the tooltip given as parameter. 10261 closeTooltip: function (tooltip) { 10262 if (tooltip) { 10263 this.removeLayer(tooltip); 10264 } 10265 return this; 10266 } 10267 10268}); 10269 10270/* 10271 * @namespace Layer 10272 * @section Tooltip methods example 10273 * 10274 * All layers share a set of methods convenient for binding tooltips to it. 10275 * 10276 * ```js 10277 * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map); 10278 * layer.openTooltip(); 10279 * layer.closeTooltip(); 10280 * ``` 10281 */ 10282 10283// @section Tooltip methods 10284Layer.include({ 10285 10286 // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this 10287 // Binds a tooltip to the layer with the passed `content` and sets up the 10288 // necessary event listeners. If a `Function` is passed it will receive 10289 // the layer as the first argument and should return a `String` or `HTMLElement`. 10290 bindTooltip: function (content, options) { 10291 10292 if (content instanceof Tooltip) { 10293 setOptions(content, options); 10294 this._tooltip = content; 10295 content._source = this; 10296 } else { 10297 if (!this._tooltip || options) { 10298 this._tooltip = new Tooltip(options, this); 10299 } 10300 this._tooltip.setContent(content); 10301 10302 } 10303 10304 this._initTooltipInteractions(); 10305 10306 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) { 10307 this.openTooltip(); 10308 } 10309 10310 return this; 10311 }, 10312 10313 // @method unbindTooltip(): this 10314 // Removes the tooltip previously bound with `bindTooltip`. 10315 unbindTooltip: function () { 10316 if (this._tooltip) { 10317 this._initTooltipInteractions(true); 10318 this.closeTooltip(); 10319 this._tooltip = null; 10320 } 10321 return this; 10322 }, 10323 10324 _initTooltipInteractions: function (remove$$1) { 10325 if (!remove$$1 && this._tooltipHandlersAdded) { return; } 10326 var onOff = remove$$1 ? 'off' : 'on', 10327 events = { 10328 remove: this.closeTooltip, 10329 move: this._moveTooltip 10330 }; 10331 if (!this._tooltip.options.permanent) { 10332 events.mouseover = this._openTooltip; 10333 events.mouseout = this.closeTooltip; 10334 if (this._tooltip.options.sticky) { 10335 events.mousemove = this._moveTooltip; 10336 } 10337 if (touch) { 10338 events.click = this._openTooltip; 10339 } 10340 } else { 10341 events.add = this._openTooltip; 10342 } 10343 this[onOff](events); 10344 this._tooltipHandlersAdded = !remove$$1; 10345 }, 10346 10347 // @method openTooltip(latlng?: LatLng): this 10348 // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed. 10349 openTooltip: function (layer, latlng) { 10350 if (!(layer instanceof Layer)) { 10351 latlng = layer; 10352 layer = this; 10353 } 10354 10355 if (layer instanceof FeatureGroup) { 10356 for (var id in this._layers) { 10357 layer = this._layers[id]; 10358 break; 10359 } 10360 } 10361 10362 if (!latlng) { 10363 latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng(); 10364 } 10365 10366 if (this._tooltip && this._map) { 10367 10368 // set tooltip source to this layer 10369 this._tooltip._source = layer; 10370 10371 // update the tooltip (content, layout, ect...) 10372 this._tooltip.update(); 10373 10374 // open the tooltip on the map 10375 this._map.openTooltip(this._tooltip, latlng); 10376 10377 // Tooltip container may not be defined if not permanent and never 10378 // opened. 10379 if (this._tooltip.options.interactive && this._tooltip._container) { 10380 addClass(this._tooltip._container, 'leaflet-clickable'); 10381 this.addInteractiveTarget(this._tooltip._container); 10382 } 10383 } 10384 10385 return this; 10386 }, 10387 10388 // @method closeTooltip(): this 10389 // Closes the tooltip bound to this layer if it is open. 10390 closeTooltip: function () { 10391 if (this._tooltip) { 10392 this._tooltip._close(); 10393 if (this._tooltip.options.interactive && this._tooltip._container) { 10394 removeClass(this._tooltip._container, 'leaflet-clickable'); 10395 this.removeInteractiveTarget(this._tooltip._container); 10396 } 10397 } 10398 return this; 10399 }, 10400 10401 // @method toggleTooltip(): this 10402 // Opens or closes the tooltip bound to this layer depending on its current state. 10403 toggleTooltip: function (target) { 10404 if (this._tooltip) { 10405 if (this._tooltip._map) { 10406 this.closeTooltip(); 10407 } else { 10408 this.openTooltip(target); 10409 } 10410 } 10411 return this; 10412 }, 10413 10414 // @method isTooltipOpen(): boolean 10415 // Returns `true` if the tooltip bound to this layer is currently open. 10416 isTooltipOpen: function () { 10417 return this._tooltip.isOpen(); 10418 }, 10419 10420 // @method setTooltipContent(content: String|HTMLElement|Tooltip): this 10421 // Sets the content of the tooltip bound to this layer. 10422 setTooltipContent: function (content) { 10423 if (this._tooltip) { 10424 this._tooltip.setContent(content); 10425 } 10426 return this; 10427 }, 10428 10429 // @method getTooltip(): Tooltip 10430 // Returns the tooltip bound to this layer. 10431 getTooltip: function () { 10432 return this._tooltip; 10433 }, 10434 10435 _openTooltip: function (e) { 10436 var layer = e.layer || e.target; 10437 10438 if (!this._tooltip || !this._map) { 10439 return; 10440 } 10441 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined); 10442 }, 10443 10444 _moveTooltip: function (e) { 10445 var latlng = e.latlng, containerPoint, layerPoint; 10446 if (this._tooltip.options.sticky && e.originalEvent) { 10447 containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent); 10448 layerPoint = this._map.containerPointToLayerPoint(containerPoint); 10449 latlng = this._map.layerPointToLatLng(layerPoint); 10450 } 10451 this._tooltip.setLatLng(latlng); 10452 } 10453}); 10454 10455/* 10456 * @class DivIcon 10457 * @aka L.DivIcon 10458 * @inherits Icon 10459 * 10460 * Represents a lightweight icon for markers that uses a simple `<div>` 10461 * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options. 10462 * 10463 * @example 10464 * ```js 10465 * var myIcon = L.divIcon({className: 'my-div-icon'});
10466 * // you can set .my-div-icon styles in CSS 10467 * 10468 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); 10469 * ``` 10470 * 10471 * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow. 10472 */ 10473 10474var DivIcon = Icon.extend({ 10475 options: { 10476 // @section 10477 // @aka DivIcon options 10478 iconSize: [12, 12], // also can be set through CSS 10479 10480 // iconAnchor: (Point), 10481 // popupAnchor: (Point), 10482 10483 // @option html: String = '' 10484 // Custom HTML code to put inside the div element, empty by default. 10485 html: false, 10486 10487 // @option bgPos: Point = [0, 0] 10488 // Optional relative position of the background, in pixels 10489 bgPos: null, 10490 10491 className: 'leaflet-div-icon' 10492 }, 10493 10494 createIcon: function (oldIcon) { 10495 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'), 10496 options = this.options; 10497 10498 div.innerHTML = options.html !== false ? options.html : ''; 10499 10500 if (options.bgPos) { 10501 var bgPos = toPoint(options.bgPos); 10502 div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px'; 10503 } 10504 this._setIconStyles(div, 'icon'); 10505 10506 return div; 10507 }, 10508 10509 createShadow: function () { 10510 return null; 10511 } 10512}); 10513 10514// @factory L.divIcon(options: DivIcon options) 10515// Creates a `DivIcon` instance with the given options. 10516function divIcon(options) { 10517 return new DivIcon(options); 10518} 10519 10520Icon.Default = IconDefault; 10521 10522/* 10523 * @class GridLayer 10524 * @inherits Layer 10525 * @aka L.GridLayer 10526 * 10527 * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`. 10528 * GridLayer can be extended to create a tiled grid of HTML elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you. 10529 * 10530 * 10531 * @section Synchronous usage 10532 * @example 10533 * 10534 * To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile. 10535 * 10536 * ```js 10537 * var CanvasLayer = L.GridLayer.extend({ 10538 * createTile: function(coords){ 10539 * // create a <canvas> element for drawing 10540 * var tile = L.DomUtil.create('canvas', 'leaflet-tile'); 10541 * 10542 * // setup tile width and height according to the options 10543 * var size = this.getTileSize(); 10544 * tile.width = size.x; 10545 * tile.height = size.y; 10546 * 10547 * // get a canvas context and draw something on it using coords.x, coords.y and coords.z 10548 * var ctx = tile.getContext('2d'); 10549 * 10550 * // return the tile so it can be rendered on screen 10551 * return tile; 10552 * } 10553 * }); 10554 * ``` 10555 * 10556 * @section Asynchronous usage 10557 * @example 10558 * 10559 * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback. 10560 * 10561 * ```js 10562 * var CanvasLayer = L.GridLayer.extend({ 10563 * createTile: function(coords, done){ 10564 * var error; 10565 * 10566 * // create a <canvas> element for drawing 10567 * var tile = L.DomUtil.create('canvas', 'leaflet-tile'); 10568 * 10569 * // setup tile width and height according to the options 10570 * var size = this.getTileSize(); 10571 * tile.width = size.x; 10572 * tile.height = size.y; 10573 * 10574 * // draw something asynchronously and pass the tile to the done() callback 10575 * setTimeout(function() { 10576 * done(error, tile); 10577 * }, 1000); 10578 * 10579 * return tile; 10580 * } 10581 * }); 10582 * ``` 10583 * 10584 * @section 10585 */ 10586 10587 10588var GridLayer = Layer.extend({ 10589 10590 // @section 10591 // @aka GridLayer options 10592 options: { 10593 // @option tileSize: Number|Point = 256 10594 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise. 10595 tileSize: 256, 10596 10597 // @option opacity: Number = 1.0 10598 // Opacity of the tiles. Can be used in the `createTile()` function. 10599 opacity: 1, 10600 10601 // @option updateWhenIdle: Boolean = (depends) 10602 // Load new tiles only when panning ends. 10603 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation. 10604 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the 10605 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers. 10606 updateWhenIdle: mobile, 10607
vendor: 24,395 bytes, lines 10608-11472
10608 // @option updateWhenZooming: Boolean = true 10609 // By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends. 10610 updateWhenZooming: true, 10611 10612 // @option updateInterval: Number = 200 10613 // Tiles will not update more than once every `updateInterval` milliseconds when panning. 10614 updateInterval: 200, 10615 10616 // @option zIndex: Number = 1 10617 // The explicit zIndex of the tile layer. 10618 zIndex: 1, 10619 10620 // @option bounds: LatLngBounds = undefined 10621 // If set, tiles will only be loaded inside the set `LatLngBounds`. 10622 bounds: null, 10623 10624 // @option minZoom: Number = 0 10625 // The minimum zoom level down to which this layer will be displayed (inclusive). 10626 minZoom: 0, 10627 10628 // @option maxZoom: Number = undefined 10629 // The maximum zoom level up to which this layer will be displayed (inclusive). 10630 maxZoom: undefined, 10631 10632 // @option maxNativeZoom: Number = undefined 10633 // Maximum zoom number the tile source has available. If it is specified, 10634 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded 10635 // from `maxNativeZoom` level and auto-scaled. 10636 maxNativeZoom: undefined, 10637 10638 // @option minNativeZoom: Number = undefined 10639 // Minimum zoom number the tile source has available. If it is specified, 10640 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded 10641 // from `minNativeZoom` level and auto-scaled. 10642 minNativeZoom: undefined, 10643 10644 // @option noWrap: Boolean = false 10645 // Whether the layer is wrapped around the antimeridian. If `true`, the 10646 // GridLayer will only be displayed once at low zoom levels. Has no 10647 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used 10648 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting 10649 // tiles outside the CRS limits. 10650 noWrap: false, 10651 10652 // @option pane: String = 'tilePane' 10653 // `Map pane` where the grid layer will be added. 10654 pane: 'tilePane', 10655 10656 // @option className: String = '' 10657 // A custom class name to assign to the tile layer. Empty by default. 10658 className: '', 10659 10660 // @option keepBuffer: Number = 2 10661 // When panning the map, keep this many rows and columns of tiles before unloading them. 10662 keepBuffer: 2 10663 }, 10664 10665 initialize: function (options) { 10666 setOptions(this, options); 10667 }, 10668 10669 onAdd: function () { 10670 this._initContainer(); 10671 10672 this._levels = {}; 10673 this._tiles = {}; 10674 10675 this._resetView(); 10676 this._update(); 10677 }, 10678 10679 beforeAdd: function (map) { 10680 map._addZoomLimit(this); 10681 }, 10682 10683 onRemove: function (map) { 10684 this._removeAllTiles(); 10685 remove(this._container); 10686 map._removeZoomLimit(this); 10687 this._container = null; 10688 this._tileZoom = undefined; 10689 }, 10690 10691 // @method bringToFront: this 10692 // Brings the tile layer to the top of all tile layers. 10693 bringToFront: function () { 10694 if (this._map) { 10695 toFront(this._container); 10696 this._setAutoZIndex(Math.max); 10697 } 10698 return this; 10699 }, 10700 10701 // @method bringToBack: this 10702 // Brings the tile layer to the bottom of all tile layers. 10703 bringToBack: function () { 10704 if (this._map) { 10705 toBack(this._container); 10706 this._setAutoZIndex(Math.min); 10707 } 10708 return this; 10709 }, 10710 10711 // @method getContainer: HTMLElement 10712 // Returns the HTML element that contains the tiles for this layer. 10713 getContainer: function () { 10714 return this._container; 10715 }, 10716 10717 // @method setOpacity(opacity: Number): this 10718 // Changes the [opacity](#gridlayer-opacity) of the grid layer. 10719 setOpacity: function (opacity) { 10720 this.options.opacity = opacity; 10721 this._updateOpacity(); 10722 return this; 10723 }, 10724 10725 // @method setZIndex(zIndex: Number): this 10726 // Changes the [zIndex](#gridlayer-zindex) of the grid layer. 10727 setZIndex: function (zIndex) { 10728 this.options.zIndex = zIndex; 10729 this._updateZIndex(); 10730 10731 return this; 10732 }, 10733 10734 // @method isLoading: Boolean 10735 // Returns `true` if any tile in the grid layer has not finished loading. 10736 isLoading: function () { 10737 return this._loading; 10738 }, 10739 10740 // @method redraw: this 10741 // Causes the layer to clear all the tiles and request them again. 10742 redraw: function () { 10743 if (this._map) { 10744 this._removeAllTiles(); 10745 this._update(); 10746 } 10747 return this; 10748 }, 10749 10750 getEvents: function () { 10751 var events = { 10752 viewprereset: this._invalidateAll, 10753 viewreset: this._resetView, 10754 zoom: this._resetView, 10755 moveend: this._onMoveEnd 10756 }; 10757 10758 if (!this.options.updateWhenIdle) { 10759 // update tiles on move, but not more often than once per given interval 10760 if (!this._onMove) { 10761 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this); 10762 } 10763 10764 events.move = this._onMove; 10765 } 10766 10767 if (this._zoomAnimated) { 10768 events.zoomanim = this._animateZoom; 10769 } 10770 10771 return events; 10772 }, 10773 10774 // @section Extension methods 10775 // Layers extending `GridLayer` shall reimplement the following method. 10776 // @method createTile(coords: Object, done?: Function): HTMLElement 10777 // Called only internally, must be overridden by classes extending `GridLayer`. 10778 // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback 10779 // is specified, it must be called when the tile has finished loading and drawing. 10780 createTile: function () { 10781 return document.createElement('div'); 10782 }, 10783 10784 // @section 10785 // @method getTileSize: Point 10786 // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method. 10787 getTileSize: function () { 10788 var s = this.options.tileSize; 10789 return s instanceof Point ? s : new Point(s, s); 10790 }, 10791 10792 _updateZIndex: function () { 10793 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) { 10794 this._container.style.zIndex = this.options.zIndex; 10795 } 10796 }, 10797 10798 _setAutoZIndex: function (compare) { 10799 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back) 10800 10801 var layers = this.getPane().children, 10802 edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min 10803 10804 for (var i = 0, len = layers.length, zIndex; i < len; i++) { 10805 10806 zIndex = layers[i].style.zIndex; 10807 10808 if (layers[i] !== this._container && zIndex) { 10809 edgeZIndex = compare(edgeZIndex, +zIndex); 10810 } 10811 } 10812 10813 if (isFinite(edgeZIndex)) { 10814 this.options.zIndex = edgeZIndex + compare(-1, 1); 10815 this._updateZIndex(); 10816 } 10817 }, 10818 10819 _updateOpacity: function () { 10820 if (!this._map) { return; } 10821 10822 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles 10823 if (ielt9) { return; } 10824 10825 setOpacity(this._container, this.options.opacity); 10826 10827 var now = +new Date(), 10828 nextFrame = false, 10829 willPrune = false; 10830 10831 for (var key in this._tiles) { 10832 var tile = this._tiles[key]; 10833 if (!tile.current || !tile.loaded) { continue; } 10834 10835 var fade = Math.min(1, (now - tile.loaded) / 200); 10836 10837 setOpacity(tile.el, fade); 10838 if (fade < 1) { 10839 nextFrame = true; 10840 } else { 10841 if (tile.active) { 10842 willPrune = true; 10843 } else { 10844 this._onOpaqueTile(tile); 10845 } 10846 tile.active = true; 10847 } 10848 } 10849 10850 if (willPrune && !this._noPrune) { this._pruneTiles(); } 10851 10852 if (nextFrame) { 10853 cancelAnimFrame(this._fadeFrame); 10854 this._fadeFrame = requestAnimFrame(this._updateOpacity, this); 10855 } 10856 }, 10857 10858 _onOpaqueTile: falseFn, 10859 10860 _initContainer: function () { 10861 if (this._container) { return; } 10862 10863 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || '')); 10864 this._updateZIndex(); 10865 10866 if (this.options.opacity < 1) { 10867 this._updateOpacity(); 10868 } 10869 10870 this.getPane().appendChild(this._container); 10871 }, 10872 10873 _updateLevels: function () { 10874 10875 var zoom = this._tileZoom, 10876 maxZoom = this.options.maxZoom; 10877 10878 if (zoom === undefined) { return undefined; } 10879 10880 for (var z in this._levels) { 10881 if (this._levels[z].el.children.length || z === zoom) { 10882 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z); 10883 this._onUpdateLevel(z); 10884 } else { 10885 remove(this._levels[z].el); 10886 this._removeTilesAtZoom(z); 10887 this._onRemoveLevel(z); 10888 delete this._levels[z]; 10889 } 10890 } 10891 10892 var level = this._levels[zoom], 10893 map = this._map; 10894 10895 if (!level) { 10896 level = this._levels[zoom] = {}; 10897 10898 level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container); 10899 level.el.style.zIndex = maxZoom; 10900 10901 level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round(); 10902 level.zoom = zoom; 10903 10904 this._setZoomTransform(level, map.getCenter(), map.getZoom()); 10905 10906 // force the browser to consider the newly added element for transition 10907 falseFn(level.el.offsetWidth); 10908 10909 this._onCreateLevel(level); 10910 } 10911 10912 this._level = level; 10913 10914 return level; 10915 }, 10916 10917 _onUpdateLevel: falseFn, 10918 10919 _onRemoveLevel: falseFn, 10920 10921 _onCreateLevel: falseFn, 10922 10923 _pruneTiles: function () { 10924 if (!this._map) { 10925 return; 10926 } 10927 10928 var key, tile; 10929 10930 var zoom = this._map.getZoom(); 10931 if (zoom > this.options.maxZoom || 10932 zoom < this.options.minZoom) { 10933 this._removeAllTiles(); 10934 return; 10935 } 10936 10937 for (key in this._tiles) { 10938 tile = this._tiles[key]; 10939 tile.retain = tile.current; 10940 } 10941 10942 for (key in this._tiles) { 10943 tile = this._tiles[key]; 10944 if (tile.current && !tile.active) { 10945 var coords = tile.coords; 10946 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) { 10947 this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2); 10948 } 10949 } 10950 } 10951 10952 for (key in this._tiles) { 10953 if (!this._tiles[key].retain) { 10954 this._removeTile(key); 10955 } 10956 } 10957 }, 10958 10959 _removeTilesAtZoom: function (zoom) { 10960 for (var key in this._tiles) { 10961 if (this._tiles[key].coords.z !== zoom) { 10962 continue; 10963 } 10964 this._removeTile(key); 10965 } 10966 }, 10967 10968 _removeAllTiles: function () { 10969 for (var key in this._tiles) { 10970 this._removeTile(key); 10971 } 10972 }, 10973 10974 _invalidateAll: function () { 10975 for (var z in this._levels) { 10976 remove(this._levels[z].el); 10977 this._onRemoveLevel(z); 10978 delete this._levels[z]; 10979 } 10980 this._removeAllTiles(); 10981 10982 this._tileZoom = undefined; 10983 }, 10984 10985 _retainParent: function (x, y, z, minZoom) { 10986 var x2 = Math.floor(x / 2), 10987 y2 = Math.floor(y / 2), 10988 z2 = z - 1, 10989 coords2 = new Point(+x2, +y2); 10990 coords2.z = +z2; 10991 10992 var key = this._tileCoordsToKey(coords2), 10993 tile = this._tiles[key]; 10994 10995 if (tile && tile.active) { 10996 tile.retain = true; 10997 return true; 10998 10999 } else if (tile && tile.loaded) { 11000 tile.retain = true; 11001 } 11002 11003 if (z2 > minZoom) { 11004 return this._retainParent(x2, y2, z2, minZoom); 11005 } 11006 11007 return false; 11008 }, 11009 11010 _retainChildren: function (x, y, z, maxZoom) { 11011 11012 for (var i = 2 * x; i < 2 * x + 2; i++) { 11013 for (var j = 2 * y; j < 2 * y + 2; j++) { 11014 11015 var coords = new Point(i, j); 11016 coords.z = z + 1; 11017 11018 var key = this._tileCoordsToKey(coords), 11019 tile = this._tiles[key]; 11020 11021 if (tile && tile.active) { 11022 tile.retain = true; 11023 continue; 11024 11025 } else if (tile && tile.loaded) { 11026 tile.retain = true; 11027 } 11028 11029 if (z + 1 < maxZoom) { 11030 this._retainChildren(i, j, z + 1, maxZoom); 11031 } 11032 } 11033 } 11034 }, 11035 11036 _resetView: function (e) { 11037 var animating = e && (e.pinch || e.flyTo); 11038 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating); 11039 }, 11040 11041 _animateZoom: function (e) { 11042 this._setView(e.center, e.zoom, true, e.noUpdate); 11043 }, 11044 11045 _clampZoom: function (zoom) { 11046 var options = this.options; 11047 11048 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) { 11049 return options.minNativeZoom; 11050 } 11051 11052 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) { 11053 return options.maxNativeZoom; 11054 } 11055 11056 return zoom; 11057 }, 11058 11059 _setView: function (center, zoom, noPrune, noUpdate) { 11060 var tileZoom = this._clampZoom(Math.round(zoom)); 11061 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) || 11062 (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) { 11063 tileZoom = undefined; 11064 } 11065 11066 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom); 11067 11068 if (!noUpdate || tileZoomChanged) { 11069 11070 this._tileZoom = tileZoom; 11071 11072 if (this._abortLoading) { 11073 this._abortLoading(); 11074 } 11075 11076 this._updateLevels(); 11077 this._resetGrid(); 11078 11079 if (tileZoom !== undefined) { 11080 this._update(center); 11081 } 11082 11083 if (!noPrune) { 11084 this._pruneTiles(); 11085 } 11086 11087 // Flag to prevent _updateOpacity from pruning tiles during 11088 // a zoom anim or a pinch gesture 11089 this._noPrune = !!noPrune; 11090 } 11091 11092 this._setZoomTransforms(center, zoom); 11093 }, 11094 11095 _setZoomTransforms: function (center, zoom) { 11096 for (var i in this._levels) { 11097 this._setZoomTransform(this._levels[i], center, zoom); 11098 } 11099 }, 11100 11101 _setZoomTransform: function (level, center, zoom) { 11102 var scale = this._map.getZoomScale(zoom, level.zoom), 11103 translate = level.origin.multiplyBy(scale) 11104 .subtract(this._map._getNewPixelOrigin(center, zoom)).round(); 11105 11106 if (any3d) { 11107 setTransform(level.el, translate, scale); 11108 } else { 11109 setPosition(level.el, translate); 11110 } 11111 }, 11112 11113 _resetGrid: function () { 11114 var map = this._map, 11115 crs = map.options.crs, 11116 tileSize = this._tileSize = this.getTileSize(), 11117 tileZoom = this._tileZoom; 11118 11119 var bounds = this._map.getPixelWorldBounds(this._tileZoom); 11120 if (bounds) { 11121 this._globalTileRange = this._pxBoundsToTileRange(bounds); 11122 } 11123 11124 this._wrapX = crs.wrapLng && !this.options.noWrap && [ 11125 Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x), 11126 Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y) 11127 ]; 11128 this._wrapY = crs.wrapLat && !this.options.noWrap && [ 11129 Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x), 11130 Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y) 11131 ]; 11132 }, 11133 11134 _onMoveEnd: function () { 11135 if (!this._map || this._map._animatingZoom) { return; } 11136 11137 this._update(); 11138 }, 11139 11140 _getTiledPixelBounds: function (center) { 11141 var map = this._map, 11142 mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(), 11143 scale = map.getZoomScale(mapZoom, this._tileZoom), 11144 pixelCenter = map.project(center, this._tileZoom).floor(), 11145 halfSize = map.getSize().divideBy(scale * 2); 11146 11147 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize)); 11148 }, 11149 11150 // Private method to load tiles in the grid's active zoom level according to map bounds 11151 _update: function (center) { 11152 var map = this._map; 11153 if (!map) { return; } 11154 var zoom = this._clampZoom(map.getZoom()); 11155 11156 if (center === undefined) { center = map.getCenter(); } 11157 if (this._tileZoom === undefined) { return; } // if out of minzoom/maxzoom 11158 11159 var pixelBounds = this._getTiledPixelBounds(center), 11160 tileRange = this._pxBoundsToTileRange(pixelBounds), 11161 tileCenter = tileRange.getCenter(), 11162 queue = [], 11163 margin = this.options.keepBuffer, 11164 noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]), 11165 tileRange.getTopRight().add([margin, -margin])); 11166 11167 // Sanity check: panic if the tile range contains Infinity somewhere. 11168 if (!(isFinite(tileRange.min.x) && 11169 isFinite(tileRange.min.y) && 11170 isFinite(tileRange.max.x) && 11171 isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); } 11172 11173 for (var key in this._tiles) { 11174 var c = this._tiles[key].coords; 11175 if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) { 11176 this._tiles[key].current = false; 11177 } 11178 } 11179 11180 // _update just loads more tiles. If the tile zoom level differs too much 11181 // from the map's, let _setView reset levels and prune old tiles. 11182 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; } 11183 11184 // create a queue of coordinates to load tiles from 11185 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) { 11186 for (var i = tileRange.min.x; i <= tileRange.max.x; i++) { 11187 var coords = new Point(i, j); 11188 coords.z = this._tileZoom; 11189 11190 if (!this._isValidTile(coords)) { continue; } 11191 11192 var tile = this._tiles[this._tileCoordsToKey(coords)]; 11193 if (tile) { 11194 tile.current = true; 11195 } else { 11196 queue.push(coords); 11197 } 11198 } 11199 } 11200 11201 // sort tile queue to load tiles in order of their distance to center 11202 queue.sort(function (a, b) { 11203 return a.distanceTo(tileCenter) - b.distanceTo(tileCenter); 11204 }); 11205 11206 if (queue.length !== 0) { 11207 // if it's the first batch of tiles to load 11208 if (!this._loading) { 11209 this._loading = true; 11210 // @event loading: Event 11211 // Fired when the grid layer starts loading tiles. 11212 this.fire('loading'); 11213 } 11214 11215 // create DOM fragment to append tiles in one batch 11216 var fragment = document.createDocumentFragment(); 11217 11218 for (i = 0; i < queue.length; i++) { 11219 this._addTile(queue[i], fragment); 11220 } 11221 11222 this._level.el.appendChild(fragment); 11223 } 11224 }, 11225 11226 _isValidTile: function (coords) { 11227 var crs = this._map.options.crs; 11228 11229 if (!crs.infinite) { 11230 // don't load tile if it's out of bounds and not wrapped 11231 var bounds = this._globalTileRange; 11232 if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) || 11233 (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; } 11234 } 11235 11236 if (!this.options.bounds) { return true; } 11237 11238 // don't load tile if it doesn't intersect the bounds in options 11239 var tileBounds = this._tileCoordsToBounds(coords); 11240 return toLatLngBounds(this.options.bounds).overlaps(tileBounds); 11241 }, 11242 11243 _keyToBounds: function (key) { 11244 return this._tileCoordsToBounds(this._keyToTileCoords(key)); 11245 }, 11246 11247 _tileCoordsToNwSe: function (coords) { 11248 var map = this._map, 11249 tileSize = this.getTileSize(), 11250 nwPoint = coords.scaleBy(tileSize), 11251 sePoint = nwPoint.add(tileSize), 11252 nw = map.unproject(nwPoint, coords.z), 11253 se = map.unproject(sePoint, coords.z); 11254 return [nw, se]; 11255 }, 11256 11257 // converts tile coordinates to its geographical bounds 11258 _tileCoordsToBounds: function (coords) { 11259 var bp = this._tileCoordsToNwSe(coords), 11260 bounds = new LatLngBounds(bp[0], bp[1]); 11261 11262 if (!this.options.noWrap) { 11263 bounds = this._map.wrapLatLngBounds(bounds); 11264 } 11265 return bounds; 11266 }, 11267 // converts tile coordinates to key for the tile cache 11268 _tileCoordsToKey: function (coords) { 11269 return coords.x + ':' + coords.y + ':' + coords.z; 11270 }, 11271 11272 // converts tile cache key to coordinates 11273 _keyToTileCoords: function (key) { 11274 var k = key.split(':'), 11275 coords = new Point(+k[0], +k[1]); 11276 coords.z = +k[2]; 11277 return coords; 11278 }, 11279 11280 _removeTile: function (key) { 11281 var tile = this._tiles[key]; 11282 if (!tile) { return; } 11283 11284 remove(tile.el); 11285 11286 delete this._tiles[key]; 11287 11288 // @event tileunload: TileEvent 11289 // Fired when a tile is removed (e.g. when a tile goes off the screen). 11290 this.fire('tileunload', { 11291 tile: tile.el, 11292 coords: this._keyToTileCoords(key) 11293 }); 11294 }, 11295 11296 _initTile: function (tile) { 11297 addClass(tile, 'leaflet-tile'); 11298 11299 var tileSize = this.getTileSize(); 11300 tile.style.width = tileSize.x + 'px'; 11301 tile.style.height = tileSize.y + 'px'; 11302 11303 tile.onselectstart = falseFn; 11304 tile.onmousemove = falseFn; 11305 11306 // update opacity on tiles in IE7-8 because of filter inheritance problems 11307 if (ielt9 && this.options.opacity < 1) { 11308 setOpacity(tile, this.options.opacity); 11309 } 11310 11311 // without this hack, tiles disappear after zoom on Chrome for Android 11312 // https://github.com/Leaflet/Leaflet/issues/2078 11313 if (android && !android23) { 11314 tile.style.WebkitBackfaceVisibility = 'hidden'; 11315 } 11316 }, 11317 11318 _addTile: function (coords, container) { 11319 var tilePos = this._getTilePos(coords), 11320 key = this._tileCoordsToKey(coords); 11321 11322 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords)); 11323 11324 this._initTile(tile); 11325 11326 // if createTile is defined with a second argument ("done" callback), 11327 // we know that tile is async and will be ready later; otherwise 11328 if (this.createTile.length < 2) { 11329 // mark tile as ready, but delay one frame for opacity animation to happen 11330 requestAnimFrame(bind(this._tileReady, this, coords, null, tile)); 11331 } 11332 11333 setPosition(tile, tilePos); 11334 11335 // save tile in cache 11336 this._tiles[key] = { 11337 el: tile, 11338 coords: coords, 11339 current: true 11340 }; 11341 11342 container.appendChild(tile); 11343 // @event tileloadstart: TileEvent 11344 // Fired when a tile is requested and starts loading. 11345 this.fire('tileloadstart', { 11346 tile: tile, 11347 coords: coords 11348 }); 11349 }, 11350 11351 _tileReady: function (coords, err, tile) { 11352 if (err) { 11353 // @event tileerror: TileErrorEvent 11354 // Fired when there is an error loading a tile. 11355 this.fire('tileerror', { 11356 error: err, 11357 tile: tile, 11358 coords: coords 11359 }); 11360 } 11361 11362 var key = this._tileCoordsToKey(coords); 11363 11364 tile = this._tiles[key]; 11365 if (!tile) { return; } 11366 11367 tile.loaded = +new Date(); 11368 if (this._map._fadeAnimated) { 11369 setOpacity(tile.el, 0); 11370 cancelAnimFrame(this._fadeFrame); 11371 this._fadeFrame = requestAnimFrame(this._updateOpacity, this); 11372 } else { 11373 tile.active = true; 11374 this._pruneTiles(); 11375 } 11376 11377 if (!err) { 11378 addClass(tile.el, 'leaflet-tile-loaded'); 11379 11380 // @event tileload: TileEvent 11381 // Fired when a tile loads. 11382 this.fire('tileload', { 11383 tile: tile.el, 11384 coords: coords 11385 }); 11386 } 11387 11388 if (this._noTilesToLoad()) { 11389 this._loading = false; 11390 // @event load: Event 11391 // Fired when the grid layer loaded all visible tiles. 11392 this.fire('load'); 11393 11394 if (ielt9 || !this._map._fadeAnimated) { 11395 requestAnimFrame(this._pruneTiles, this); 11396 } else { 11397 // Wait a bit more than 0.2 secs (the duration of the tile fade-in) 11398 // to trigger a pruning. 11399 setTimeout(bind(this._pruneTiles, this), 250); 11400 } 11401 } 11402 }, 11403 11404 _getTilePos: function (coords) { 11405 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin); 11406 }, 11407 11408 _wrapCoords: function (coords) { 11409 var newCoords = new Point( 11410 this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x, 11411 this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y); 11412 newCoords.z = coords.z; 11413 return newCoords; 11414 }, 11415 11416 _pxBoundsToTileRange: function (bounds) { 11417 var tileSize = this.getTileSize(); 11418 return new Bounds( 11419 bounds.min.unscaleBy(tileSize).floor(), 11420 bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1])); 11421 }, 11422 11423 _noTilesToLoad: function () { 11424 for (var key in this._tiles) { 11425 if (!this._tiles[key].loaded) { return false; } 11426 } 11427 return true; 11428 } 11429}); 11430 11431// @factory L.gridLayer(options?: GridLayer options) 11432// Creates a new instance of GridLayer with the supplied options. 11433function gridLayer(options) { 11434 return new GridLayer(options); 11435} 11436 11437/* 11438 * @class TileLayer 11439 * @inherits GridLayer 11440 * @aka L.TileLayer 11441 * Used to load and display tile layers on the map. Note that most tile servers require attribution, which you can set under `Layer`. Extends `GridLayer`. 11442 * 11443 * @example 11444 * 11445 * ```js 11446 * L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar', attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'}).addTo(map); 11447 * ``` 11448 * 11449 * @section URL template 11450 * @example 11451 * 11452 * A string of the following form: 11453 * 11454 * ``` 11455 * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png' 11456 * ``` 11457 * 11458 * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` â zoom level, `{x}` and `{y}` â tile coordinates. `{r}` can be used to add "@2x" to the URL to load retina tiles. 11459 * 11460 * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this: 11461 * 11462 * ``` 11463 * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'}); 11464 * ``` 11465 */ 11466 11467 11468var TileLayer = GridLayer.extend({ 11469 11470 // @section 11471 // @aka TileLayer options 11472 options: {
11473 // @option minZoom: Number = 0 11474 // The minimum zoom level down to which this layer will be displayed (inclusive). 11475 minZoom: 0, 11476 11477 // @option maxZoom: Number = 18 11478 // The maximum zoom level up to which this layer will be displayed (inclusive). 11479 maxZoom: 18, 11480 11481 // @option subdomains: String|String[] = 'abc' 11482 // Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings. 11483 subdomains: 'abc', 11484 11485 // @option errorTileUrl: String = '' 11486 // URL to the tile image to show in place of the tile that failed to load. 11487 errorTileUrl: '', 11488 11489 // @option zoomOffset: Number = 0 11490 // The zoom number used in tile URLs will be offset with this value. 11491 zoomOffset: 0, 11492 11493 // @option tms: Boolean = false 11494 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services). 11495 tms: false, 11496 11497 // @option zoomReverse: Boolean = false 11498 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`) 11499 zoomReverse: false, 11500 11501 // @option detectRetina: Boolean = false 11502 // If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution. 11503 detectRetina: false, 11504 11505 // @option crossOrigin: Boolean|String = false 11506 // Whether the crossOrigin attribute will be added to the tiles. 11507 // If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data. 11508 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values. 11509 crossOrigin: false 11510 }, 11511 11512 initialize: function (url, options) { 11513 11514 this._url = url; 11515 11516 options = setOptions(this, options); 11517 11518 // detecting retina displays, adjusting tileSize and zoom levels 11519 if (options.detectRetina && retina && options.maxZoom > 0) { 11520 11521 options.tileSize = Math.floor(options.tileSize / 2); 11522 11523 if (!options.zoomReverse) { 11524 options.zoomOffset++; 11525 options.maxZoom--; 11526 } else { 11527 options.zoomOffset--; 11528 options.minZoom++; 11529 } 11530 11531 options.minZoom = Math.max(0, options.minZoom); 11532 } 11533 11534 if (typeof options.subdomains === 'string') { 11535 options.subdomains = options.subdomains.split(''); 11536 } 11537 11538 // for https://github.com/Leaflet/Leaflet/issues/137 11539 if (!android) { 11540 this.on('tileunload', this._onTileRemove); 11541 } 11542 }, 11543 11544 // @method setUrl(url: String, noRedraw?: Boolean): this 11545 // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`). 11546 // If the URL does not change, the layer will not be redrawn unless 11547 // the noRedraw parameter is set to false. 11548 setUrl: function (url, noRedraw) { 11549 if (this._url === url && noRedraw === undefined) { 11550 noRedraw = true; 11551 } 11552 11553 this._url = url; 11554 11555 if (!noRedraw) { 11556 this.redraw(); 11557 } 11558 return this; 11559 }, 11560 11561 // @method createTile(coords: Object, done?: Function): HTMLElement 11562 // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile) 11563 // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done` 11564 // callback is called when the tile has been loaded. 11565 createTile: function (coords, done) { 11566 var tile = document.createElement('img'); 11567 11568 on(tile, 'load', bind(this._tileOnLoad, this, done, tile)); 11569 on(tile, 'error', bind(this._tileOnError, this, done, tile)); 11570 11571 if (this.options.crossOrigin || this.options.crossOrigin === '') { 11572 tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin; 11573 } 11574 11575 /* 11576 Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons 11577 http://www.w3.org/TR/WCAG20-TECHS/H67 11578 */ 11579 tile.alt = ''; 11580 11581 /* 11582 Set role="presentation" to force screen readers to ignore this 11583 https://www.w3.org/TR/wai-aria/roles#textalternativecomputation 11584 */ 11585 tile.setAttribute('role', 'presentation'); 11586 11587 tile.src = this.getTileUrl(coords);
vendor: 50,258 bytes, lines 11588-13459
11588 11589 return tile; 11590 }, 11591 11592 // @section Extension methods 11593 // @uninheritable 11594 // Layers extending `TileLayer` might reimplement the following method. 11595 // @method getTileUrl(coords: Object): String 11596 // Called only internally, returns the URL for a tile given its coordinates. 11597 // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes. 11598 getTileUrl: function (coords) { 11599 var data = { 11600 r: retina ? '@2x' : '', 11601 s: this._getSubdomain(coords), 11602 x: coords.x, 11603 y: coords.y, 11604 z: this._getZoomForUrl() 11605 }; 11606 if (this._map && !this._map.options.crs.infinite) { 11607 var invertedY = this._globalTileRange.max.y - coords.y; 11608 if (this.options.tms) { 11609 data['y'] = invertedY; 11610 } 11611 data['-y'] = invertedY; 11612 } 11613 11614 return template(this._url, extend(data, this.options)); 11615 }, 11616 11617 _tileOnLoad: function (done, tile) { 11618 // For https://github.com/Leaflet/Leaflet/issues/3332 11619 if (ielt9) { 11620 setTimeout(bind(done, this, null, tile), 0); 11621 } else { 11622 done(null, tile); 11623 } 11624 }, 11625 11626 _tileOnError: function (done, tile, e) { 11627 var errorUrl = this.options.errorTileUrl; 11628 if (errorUrl && tile.getAttribute('src') !== errorUrl) { 11629 tile.src = errorUrl; 11630 } 11631 done(e, tile); 11632 }, 11633 11634 _onTileRemove: function (e) { 11635 e.tile.onload = null; 11636 }, 11637 11638 _getZoomForUrl: function () { 11639 var zoom = this._tileZoom, 11640 maxZoom = this.options.maxZoom, 11641 zoomReverse = this.options.zoomReverse, 11642 zoomOffset = this.options.zoomOffset; 11643 11644 if (zoomReverse) { 11645 zoom = maxZoom - zoom; 11646 } 11647 11648 return zoom + zoomOffset; 11649 }, 11650 11651 _getSubdomain: function (tilePoint) { 11652 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length; 11653 return this.options.subdomains[index]; 11654 }, 11655 11656 // stops loading all tiles in the background layer 11657 _abortLoading: function () { 11658 var i, tile; 11659 for (i in this._tiles) { 11660 if (this._tiles[i].coords.z !== this._tileZoom) { 11661 tile = this._tiles[i].el; 11662 11663 tile.onload = falseFn; 11664 tile.onerror = falseFn; 11665 11666 if (!tile.complete) { 11667 tile.src = emptyImageUrl; 11668 remove(tile); 11669 delete this._tiles[i]; 11670 } 11671 } 11672 } 11673 }, 11674 11675 _removeTile: function (key) { 11676 var tile = this._tiles[key]; 11677 if (!tile) { return; } 11678 11679 // Cancels any pending http requests associated with the tile 11680 // unless we're on Android's stock browser, 11681 // see https://github.com/Leaflet/Leaflet/issues/137 11682 if (!androidStock) { 11683 tile.el.setAttribute('src', emptyImageUrl); 11684 } 11685 11686 return GridLayer.prototype._removeTile.call(this, key); 11687 }, 11688 11689 _tileReady: function (coords, err, tile) { 11690 if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) { 11691 return; 11692 } 11693 11694 return GridLayer.prototype._tileReady.call(this, coords, err, tile); 11695 } 11696}); 11697 11698 11699// @factory L.tilelayer(urlTemplate: String, options?: TileLayer options) 11700// Instantiates a tile layer object given a `URL template` and optionally an options object. 11701 11702function tileLayer(url, options) { 11703 return new TileLayer(url, options); 11704} 11705 11706/* 11707 * @class TileLayer.WMS 11708 * @inherits TileLayer 11709 * @aka L.TileLayer.WMS 11710 * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`. 11711 * 11712 * @example 11713 * 11714 * ```js 11715 * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", { 11716 * layers: 'nexrad-n0r-900913', 11717 * format: 'image/png', 11718 * transparent: true, 11719 * attribution: "Weather data © 2012 IEM Nexrad" 11720 * }); 11721 * ``` 11722 */ 11723 11724var TileLayerWMS = TileLayer.extend({ 11725 11726 // @section 11727 // @aka TileLayer.WMS options 11728 // If any custom options not documented here are used, they will be sent to the 11729 // WMS server as extra parameters in each request URL. This can be useful for 11730 // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html). 11731 defaultWmsParams: { 11732 service: 'WMS', 11733 request: 'GetMap', 11734 11735 // @option layers: String = '' 11736 // **(required)** Comma-separated list of WMS layers to show. 11737 layers: '', 11738 11739 // @option styles: String = '' 11740 // Comma-separated list of WMS styles. 11741 styles: '', 11742 11743 // @option format: String = 'image/jpeg' 11744 // WMS image format (use `'image/png'` for layers with transparency). 11745 format: 'image/jpeg', 11746 11747 // @option transparent: Boolean = false 11748 // If `true`, the WMS service will return images with transparency. 11749 transparent: false, 11750 11751 // @option version: String = '1.1.1' 11752 // Version of the WMS service to use 11753 version: '1.1.1' 11754 }, 11755 11756 options: { 11757 // @option crs: CRS = null 11758 // Coordinate Reference System to use for the WMS requests, defaults to 11759 // map CRS. Don't change this if you're not sure what it means. 11760 crs: null, 11761 11762 // @option uppercase: Boolean = false 11763 // If `true`, WMS request parameter keys will be uppercase. 11764 uppercase: false 11765 }, 11766 11767 initialize: function (url, options) { 11768 11769 this._url = url; 11770 11771 var wmsParams = extend({}, this.defaultWmsParams); 11772 11773 // all keys that are not TileLayer options go to WMS params 11774 for (var i in options) { 11775 if (!(i in this.options)) { 11776 wmsParams[i] = options[i]; 11777 } 11778 } 11779 11780 options = setOptions(this, options); 11781 11782 var realRetina = options.detectRetina && retina ? 2 : 1; 11783 var tileSize = this.getTileSize(); 11784 wmsParams.width = tileSize.x * realRetina; 11785 wmsParams.height = tileSize.y * realRetina; 11786 11787 this.wmsParams = wmsParams; 11788 }, 11789 11790 onAdd: function (map) { 11791 11792 this._crs = this.options.crs || map.options.crs; 11793 this._wmsVersion = parseFloat(this.wmsParams.version); 11794 11795 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs'; 11796 this.wmsParams[projectionKey] = this._crs.code; 11797 11798 TileLayer.prototype.onAdd.call(this, map); 11799 }, 11800 11801 getTileUrl: function (coords) { 11802 11803 var tileBounds = this._tileCoordsToNwSe(coords), 11804 crs = this._crs, 11805 bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])), 11806 min = bounds.min, 11807 max = bounds.max, 11808 bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ? 11809 [min.y, min.x, max.y, max.x] : 11810 [min.x, min.y, max.x, max.y]).join(','), 11811 url = TileLayer.prototype.getTileUrl.call(this, coords); 11812 return url + 11813 getParamString(this.wmsParams, url, this.options.uppercase) + 11814 (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox; 11815 }, 11816 11817 // @method setParams(params: Object, noRedraw?: Boolean): this 11818 // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true). 11819 setParams: function (params, noRedraw) { 11820 11821 extend(this.wmsParams, params); 11822 11823 if (!noRedraw) { 11824 this.redraw(); 11825 } 11826 11827 return this; 11828 } 11829}); 11830 11831 11832// @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options) 11833// Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object. 11834function tileLayerWMS(url, options) { 11835 return new TileLayerWMS(url, options); 11836} 11837 11838TileLayer.WMS = TileLayerWMS; 11839tileLayer.wms = tileLayerWMS; 11840 11841/* 11842 * @class Renderer 11843 * @inherits Layer 11844 * @aka L.Renderer 11845 * 11846 * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the 11847 * DOM container of the renderer, its bounds, and its zoom animation. 11848 * 11849 * A `Renderer` works as an implicit layer group for all `Path`s - the renderer 11850 * itself can be added or removed to the map. All paths use a renderer, which can 11851 * be implicit (the map will decide the type of renderer and use it automatically) 11852 * or explicit (using the [`renderer`](#path-renderer) option of the path). 11853 * 11854 * Do not use this class directly, use `SVG` and `Canvas` instead. 11855 * 11856 * @event update: Event 11857 * Fired when the renderer updates its bounds, center and zoom, for example when 11858 * its map has moved 11859 */ 11860 11861var Renderer = Layer.extend({ 11862 11863 // @section 11864 // @aka Renderer options 11865 options: { 11866 // @option padding: Number = 0.1 11867 // How much to extend the clip area around the map view (relative to its size) 11868 // e.g. 0.1 would be 10% of map view in each direction 11869 padding: 0.1, 11870 11871 // @option tolerance: Number = 0 11872 // How much to extend click tolerance round a path/object on the map 11873 tolerance : 0 11874 }, 11875 11876 initialize: function (options) { 11877 setOptions(this, options); 11878 stamp(this); 11879 this._layers = this._layers || {}; 11880 }, 11881 11882 onAdd: function () { 11883 if (!this._container) { 11884 this._initContainer(); // defined by renderer implementations 11885 11886 if (this._zoomAnimated) { 11887 addClass(this._container, 'leaflet-zoom-animated'); 11888 } 11889 } 11890 11891 this.getPane().appendChild(this._container); 11892 this._update(); 11893 this.on('update', this._updatePaths, this); 11894 }, 11895 11896 onRemove: function () { 11897 this.off('update', this._updatePaths, this); 11898 this._destroyContainer(); 11899 }, 11900 11901 getEvents: function () { 11902 var events = { 11903 viewreset: this._reset, 11904 zoom: this._onZoom, 11905 moveend: this._update, 11906 zoomend: this._onZoomEnd 11907 }; 11908 if (this._zoomAnimated) { 11909 events.zoomanim = this._onAnimZoom; 11910 } 11911 return events; 11912 }, 11913 11914 _onAnimZoom: function (ev) { 11915 this._updateTransform(ev.center, ev.zoom); 11916 }, 11917 11918 _onZoom: function () { 11919 this._updateTransform(this._map.getCenter(), this._map.getZoom()); 11920 }, 11921 11922 _updateTransform: function (center, zoom) { 11923 var scale = this._map.getZoomScale(zoom, this._zoom), 11924 position = getPosition(this._container), 11925 viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding), 11926 currentCenterPoint = this._map.project(this._center, zoom), 11927 destCenterPoint = this._map.project(center, zoom), 11928 centerOffset = destCenterPoint.subtract(currentCenterPoint), 11929 11930 topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset); 11931 11932 if (any3d) { 11933 setTransform(this._container, topLeftOffset, scale); 11934 } else { 11935 setPosition(this._container, topLeftOffset); 11936 } 11937 }, 11938 11939 _reset: function () { 11940 this._update(); 11941 this._updateTransform(this._center, this._zoom); 11942 11943 for (var id in this._layers) { 11944 this._layers[id]._reset(); 11945 } 11946 }, 11947 11948 _onZoomEnd: function () { 11949 for (var id in this._layers) { 11950 this._layers[id]._project(); 11951 } 11952 }, 11953 11954 _updatePaths: function () { 11955 for (var id in this._layers) { 11956 this._layers[id]._update(); 11957 } 11958 }, 11959 11960 _update: function () { 11961 // Update pixel bounds of renderer container (for positioning/sizing/clipping later) 11962 // Subclasses are responsible of firing the 'update' event. 11963 var p = this.options.padding, 11964 size = this._map.getSize(), 11965 min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round(); 11966 11967 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round()); 11968 11969 this._center = this._map.getCenter(); 11970 this._zoom = this._map.getZoom(); 11971 } 11972}); 11973 11974/* 11975 * @class Canvas 11976 * @inherits Renderer 11977 * @aka L.Canvas 11978 * 11979 * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API). 11980 * Inherits `Renderer`. 11981 * 11982 * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not 11983 * available in all web browsers, notably IE8, and overlapping geometries might 11984 * not display properly in some edge cases. 11985 * 11986 * @example 11987 * 11988 * Use Canvas by default for all paths in the map: 11989 * 11990 * ```js 11991 * var map = L.map('map', { 11992 * renderer: L.canvas() 11993 * }); 11994 * ``` 11995 * 11996 * Use a Canvas renderer with extra padding for specific vector geometries: 11997 * 11998 * ```js 11999 * var map = L.map('map'); 12000 * var myRenderer = L.canvas({ padding: 0.5 }); 12001 * var line = L.polyline( coordinates, { renderer: myRenderer } ); 12002 * var circle = L.circle( center, { renderer: myRenderer } ); 12003 * ``` 12004 */ 12005 12006var Canvas = Renderer.extend({ 12007 getEvents: function () { 12008 var events = Renderer.prototype.getEvents.call(this); 12009 events.viewprereset = this._onViewPreReset; 12010 return events; 12011 }, 12012 12013 _onViewPreReset: function () { 12014 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once 12015 this._postponeUpdatePaths = true; 12016 }, 12017 12018 onAdd: function () { 12019 Renderer.prototype.onAdd.call(this); 12020 12021 // Redraw vectors since canvas is cleared upon removal, 12022 // in case of removing the renderer itself from the map. 12023 this._draw(); 12024 }, 12025 12026 _initContainer: function () { 12027 var container = this._container = document.createElement('canvas'); 12028 12029 on(container, 'mousemove', throttle(this._onMouseMove, 32, this), this); 12030 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this); 12031 on(container, 'mouseout', this._handleMouseOut, this); 12032 12033 this._ctx = container.getContext('2d'); 12034 }, 12035 12036 _destroyContainer: function () { 12037 cancelAnimFrame(this._redrawRequest); 12038 delete this._ctx; 12039 remove(this._container); 12040 off(this._container); 12041 delete this._container; 12042 }, 12043 12044 _updatePaths: function () { 12045 if (this._postponeUpdatePaths) { return; } 12046 12047 var layer; 12048 this._redrawBounds = null; 12049 for (var id in this._layers) { 12050 layer = this._layers[id]; 12051 layer._update(); 12052 } 12053 this._redraw(); 12054 }, 12055 12056 _update: function () { 12057 if (this._map._animatingZoom && this._bounds) { return; } 12058 12059 Renderer.prototype._update.call(this); 12060 12061 var b = this._bounds, 12062 container = this._container, 12063 size = b.getSize(), 12064 m = retina ? 2 : 1; 12065 12066 setPosition(container, b.min); 12067 12068 // set canvas size (also clearing it); use double size on retina 12069 container.width = m * size.x; 12070 container.height = m * size.y; 12071 container.style.width = size.x + 'px'; 12072 container.style.height = size.y + 'px'; 12073 12074 if (retina) { 12075 this._ctx.scale(2, 2); 12076 } 12077 12078 // translate so we use the same path coordinates after canvas element moves 12079 this._ctx.translate(-b.min.x, -b.min.y); 12080 12081 // Tell paths to redraw themselves 12082 this.fire('update'); 12083 }, 12084 12085 _reset: function () { 12086 Renderer.prototype._reset.call(this); 12087 12088 if (this._postponeUpdatePaths) { 12089 this._postponeUpdatePaths = false; 12090 this._updatePaths(); 12091 } 12092 }, 12093 12094 _initPath: function (layer) { 12095 this._updateDashArray(layer); 12096 this._layers[stamp(layer)] = layer; 12097 12098 var order = layer._order = { 12099 layer: layer, 12100 prev: this._drawLast, 12101 next: null 12102 }; 12103 if (this._drawLast) { this._drawLast.next = order; } 12104 this._drawLast = order; 12105 this._drawFirst = this._drawFirst || this._drawLast; 12106 }, 12107 12108 _addPath: function (layer) { 12109 this._requestRedraw(layer); 12110 }, 12111 12112 _removePath: function (layer) { 12113 var order = layer._order; 12114 var next = order.next; 12115 var prev = order.prev; 12116 12117 if (next) { 12118 next.prev = prev; 12119 } else { 12120 this._drawLast = prev; 12121 } 12122 if (prev) { 12123 prev.next = next; 12124 } else { 12125 this._drawFirst = next; 12126 } 12127 12128 delete layer._order; 12129 12130 delete this._layers[stamp(layer)]; 12131 12132 this._requestRedraw(layer); 12133 }, 12134 12135 _updatePath: function (layer) { 12136 // Redraw the union of the layer's old pixel 12137 // bounds and the new pixel bounds. 12138 this._extendRedrawBounds(layer); 12139 layer._project(); 12140 layer._update(); 12141 // The redraw will extend the redraw bounds 12142 // with the new pixel bounds. 12143 this._requestRedraw(layer); 12144 }, 12145 12146 _updateStyle: function (layer) { 12147 this._updateDashArray(layer); 12148 this._requestRedraw(layer); 12149 }, 12150 12151 _updateDashArray: function (layer) { 12152 if (typeof layer.options.dashArray === 'string') { 12153 var parts = layer.options.dashArray.split(/[, ]+/), 12154 dashArray = [], 12155 dashValue, 12156 i; 12157 for (i = 0; i < parts.length; i++) { 12158 dashValue = Number(parts[i]); 12159 // Ignore dash array containing invalid lengths 12160 if (isNaN(dashValue)) { return; } 12161 dashArray.push(dashValue); 12162 } 12163 layer.options._dashArray = dashArray; 12164 } else { 12165 layer.options._dashArray = layer.options.dashArray; 12166 } 12167 }, 12168 12169 _requestRedraw: function (layer) { 12170 if (!this._map) { return; } 12171 12172 this._extendRedrawBounds(layer); 12173 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this); 12174 }, 12175 12176 _extendRedrawBounds: function (layer) { 12177 if (layer._pxBounds) { 12178 var padding = (layer.options.weight || 0) + 1; 12179 this._redrawBounds = this._redrawBounds || new Bounds(); 12180 this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding])); 12181 this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding])); 12182 } 12183 }, 12184 12185 _redraw: function () { 12186 this._redrawRequest = null; 12187 12188 if (this._redrawBounds) { 12189 this._redrawBounds.min._floor(); 12190 this._redrawBounds.max._ceil(); 12191 } 12192 12193 this._clear(); // clear layers in redraw bounds 12194 this._draw(); // draw layers 12195 12196 this._redrawBounds = null; 12197 }, 12198 12199 _clear: function () { 12200 var bounds = this._redrawBounds; 12201 if (bounds) { 12202 var size = bounds.getSize(); 12203 this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y); 12204 } else { 12205 this._ctx.clearRect(0, 0, this._container.width, this._container.height); 12206 } 12207 }, 12208 12209 _draw: function () { 12210 var layer, bounds = this._redrawBounds; 12211 this._ctx.save(); 12212 if (bounds) { 12213 var size = bounds.getSize(); 12214 this._ctx.beginPath(); 12215 this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y); 12216 this._ctx.clip(); 12217 } 12218 12219 this._drawing = true; 12220 12221 for (var order = this._drawFirst; order; order = order.next) { 12222 layer = order.layer; 12223 if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) { 12224 layer._updatePath(); 12225 } 12226 } 12227 12228 this._drawing = false; 12229 12230 this._ctx.restore(); // Restore state before clipping. 12231 }, 12232 12233 _updatePoly: function (layer, closed) { 12234 if (!this._drawing) { return; } 12235 12236 var i, j, len2, p, 12237 parts = layer._parts, 12238 len = parts.length, 12239 ctx = this._ctx; 12240 12241 if (!len) { return; } 12242 12243 ctx.beginPath(); 12244 12245 for (i = 0; i < len; i++) { 12246 for (j = 0, len2 = parts[i].length; j < len2; j++) { 12247 p = parts[i][j]; 12248 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y); 12249 } 12250 if (closed) { 12251 ctx.closePath(); 12252 } 12253 } 12254 12255 this._fillStroke(ctx, layer); 12256 12257 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature 12258 }, 12259 12260 _updateCircle: function (layer) { 12261 12262 if (!this._drawing || layer._empty()) { return; } 12263 12264 var p = layer._point, 12265 ctx = this._ctx, 12266 r = Math.max(Math.round(layer._radius), 1), 12267 s = (Math.max(Math.round(layer._radiusY), 1) || r) / r; 12268 12269 if (s !== 1) { 12270 ctx.save(); 12271 ctx.scale(1, s); 12272 } 12273 12274 ctx.beginPath(); 12275 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false); 12276 12277 if (s !== 1) { 12278 ctx.restore(); 12279 } 12280 12281 this._fillStroke(ctx, layer); 12282 }, 12283 12284 _fillStroke: function (ctx, layer) { 12285 var options = layer.options; 12286 12287 if (options.fill) { 12288 ctx.globalAlpha = options.fillOpacity; 12289 ctx.fillStyle = options.fillColor || options.color; 12290 ctx.fill(options.fillRule || 'evenodd'); 12291 } 12292 12293 if (options.stroke && options.weight !== 0) { 12294 if (ctx.setLineDash) { 12295 ctx.setLineDash(layer.options && layer.options._dashArray || []); 12296 } 12297 ctx.globalAlpha = options.opacity; 12298 ctx.lineWidth = options.weight; 12299 ctx.strokeStyle = options.color; 12300 ctx.lineCap = options.lineCap; 12301 ctx.lineJoin = options.lineJoin; 12302 ctx.stroke(); 12303 } 12304 }, 12305 12306 // Canvas obviously doesn't have mouse events for individual drawn objects, 12307 // so we emulate that by calculating what's under the mouse on mousemove/click manually 12308 12309 _onClick: function (e) { 12310 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer; 12311 12312 for (var order = this._drawFirst; order; order = order.next) { 12313 layer = order.layer; 12314 if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) { 12315 clickedLayer = layer; 12316 } 12317 } 12318 if (clickedLayer) { 12319 fakeStop(e); 12320 this._fireEvent([clickedLayer], e); 12321 } 12322 }, 12323 12324 _onMouseMove: function (e) { 12325 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; } 12326 12327 var point = this._map.mouseEventToLayerPoint(e); 12328 this._handleMouseHover(e, point); 12329 }, 12330 12331 12332 _handleMouseOut: function (e) { 12333 var layer = this._hoveredLayer; 12334 if (layer) { 12335 // if we're leaving the layer, fire mouseout 12336 removeClass(this._container, 'leaflet-interactive'); 12337 this._fireEvent([layer], e, 'mouseout'); 12338 this._hoveredLayer = null; 12339 } 12340 }, 12341 12342 _handleMouseHover: function (e, point) { 12343 var layer, candidateHoveredLayer; 12344 12345 for (var order = this._drawFirst; order; order = order.next) { 12346 layer = order.layer; 12347 if (layer.options.interactive && layer._containsPoint(point)) { 12348 candidateHoveredLayer = layer; 12349 } 12350 } 12351 12352 if (candidateHoveredLayer !== this._hoveredLayer) { 12353 this._handleMouseOut(e); 12354 12355 if (candidateHoveredLayer) { 12356 addClass(this._container, 'leaflet-interactive'); // change cursor 12357 this._fireEvent([candidateHoveredLayer], e, 'mouseover'); 12358 this._hoveredLayer = candidateHoveredLayer; 12359 } 12360 } 12361 12362 if (this._hoveredLayer) { 12363 this._fireEvent([this._hoveredLayer], e); 12364 } 12365 }, 12366 12367 _fireEvent: function (layers, e, type) { 12368 this._map._fireDOMEvent(e, type || e.type, layers); 12369 }, 12370 12371 _bringToFront: function (layer) { 12372 var order = layer._order; 12373 12374 if (!order) { return; } 12375 12376 var next = order.next; 12377 var prev = order.prev; 12378 12379 if (next) { 12380 next.prev = prev; 12381 } else { 12382 // Already last 12383 return; 12384 } 12385 if (prev) { 12386 prev.next = next; 12387 } else if (next) { 12388 // Update first entry unless this is the 12389 // single entry 12390 this._drawFirst = next; 12391 } 12392 12393 order.prev = this._drawLast; 12394 this._drawLast.next = order; 12395 12396 order.next = null; 12397 this._drawLast = order; 12398 12399 this._requestRedraw(layer); 12400 }, 12401 12402 _bringToBack: function (layer) { 12403 var order = layer._order; 12404 12405 if (!order) { return; } 12406 12407 var next = order.next; 12408 var prev = order.prev; 12409 12410 if (prev) { 12411 prev.next = next; 12412 } else { 12413 // Already first 12414 return; 12415 } 12416 if (next) { 12417 next.prev = prev; 12418 } else if (prev) { 12419 // Update last entry unless this is the 12420 // single entry 12421 this._drawLast = prev; 12422 } 12423 12424 order.prev = null; 12425 12426 order.next = this._drawFirst; 12427 this._drawFirst.prev = order; 12428 this._drawFirst = order; 12429 12430 this._requestRedraw(layer); 12431 } 12432}); 12433 12434// @factory L.canvas(options?: Renderer options) 12435// Creates a Canvas renderer with the given options. 12436function canvas$1(options) { 12437 return canvas ? new Canvas(options) : null; 12438} 12439 12440/* 12441 * Thanks to Dmitry Baranovsky and his Raphael library for inspiration! 12442 */ 12443 12444 12445var vmlCreate = (function () { 12446 try { 12447 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml'); 12448 return function (name) { 12449 return document.createElement('<lvml:' + name + ' class="lvml">'); 12450 }; 12451 } catch (e) { 12452 return function (name) { 12453 return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">'); 12454 }; 12455 } 12456})(); 12457 12458 12459/* 12460 * @class SVG 12461 * 12462 * 12463 * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility 12464 * with old versions of Internet Explorer. 12465 */ 12466 12467// mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences 12468var vmlMixin = { 12469 12470 _initContainer: function () { 12471 this._container = create$1('div', 'leaflet-vml-container'); 12472 }, 12473 12474 _update: function () { 12475 if (this._map._animatingZoom) { return; } 12476 Renderer.prototype._update.call(this); 12477 this.fire('update'); 12478 }, 12479 12480 _initPath: function (layer) { 12481 var container = layer._container = vmlCreate('shape'); 12482 12483 addClass(container, 'leaflet-vml-shape ' + (this.options.className || '')); 12484 12485 container.coordsize = '1 1'; 12486 12487 layer._path = vmlCreate('path'); 12488 container.appendChild(layer._path); 12489 12490 this._updateStyle(layer); 12491 this._layers[stamp(layer)] = layer; 12492 }, 12493 12494 _addPath: function (layer) { 12495 var container = layer._container; 12496 this._container.appendChild(container); 12497 12498 if (layer.options.interactive) { 12499 layer.addInteractiveTarget(container); 12500 } 12501 }, 12502 12503 _removePath: function (layer) { 12504 var container = layer._container; 12505 remove(container); 12506 layer.removeInteractiveTarget(container); 12507 delete this._layers[stamp(layer)]; 12508 }, 12509 12510 _updateStyle: function (layer) { 12511 var stroke = layer._stroke, 12512 fill = layer._fill, 12513 options = layer.options, 12514 container = layer._container; 12515 12516 container.stroked = !!options.stroke; 12517 container.filled = !!options.fill; 12518 12519 if (options.stroke) { 12520 if (!stroke) { 12521 stroke = layer._stroke = vmlCreate('stroke'); 12522 } 12523 container.appendChild(stroke); 12524 stroke.weight = options.weight + 'px'; 12525 stroke.color = options.color; 12526 stroke.opacity = options.opacity; 12527 12528 if (options.dashArray) { 12529 stroke.dashStyle = isArray(options.dashArray) ? 12530 options.dashArray.join(' ') : 12531 options.dashArray.replace(/( *, *)/g, ' '); 12532 } else { 12533 stroke.dashStyle = ''; 12534 } 12535 stroke.endcap = options.lineCap.replace('butt', 'flat'); 12536 stroke.joinstyle = options.lineJoin; 12537 12538 } else if (stroke) { 12539 container.removeChild(stroke); 12540 layer._stroke = null; 12541 } 12542 12543 if (options.fill) { 12544 if (!fill) { 12545 fill = layer._fill = vmlCreate('fill'); 12546 } 12547 container.appendChild(fill); 12548 fill.color = options.fillColor || options.color; 12549 fill.opacity = options.fillOpacity; 12550 12551 } else if (fill) { 12552 container.removeChild(fill); 12553 layer._fill = null; 12554 } 12555 }, 12556 12557 _updateCircle: function (layer) { 12558 var p = layer._point.round(), 12559 r = Math.round(layer._radius), 12560 r2 = Math.round(layer._radiusY || r); 12561 12562 this._setPath(layer, layer._empty() ? 'M0 0' : 12563 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360)); 12564 }, 12565 12566 _setPath: function (layer, path) { 12567 layer._path.v = path; 12568 }, 12569 12570 _bringToFront: function (layer) { 12571 toFront(layer._container); 12572 }, 12573 12574 _bringToBack: function (layer) { 12575 toBack(layer._container); 12576 } 12577}; 12578 12579var create$2 = vml ? vmlCreate : svgCreate; 12580 12581/* 12582 * @class SVG 12583 * @inherits Renderer 12584 * @aka L.SVG 12585 * 12586 * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG). 12587 * Inherits `Renderer`. 12588 * 12589 * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not 12590 * available in all web browsers, notably Android 2.x and 3.x. 12591 * 12592 * Although SVG is not available on IE7 and IE8, these browsers support 12593 * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language) 12594 * (a now deprecated technology), and the SVG renderer will fall back to VML in 12595 * this case. 12596 * 12597 * @example 12598 * 12599 * Use SVG by default for all paths in the map: 12600 * 12601 * ```js 12602 * var map = L.map('map', { 12603 * renderer: L.svg() 12604 * }); 12605 * ``` 12606 * 12607 * Use a SVG renderer with extra padding for specific vector geometries: 12608 * 12609 * ```js 12610 * var map = L.map('map'); 12611 * var myRenderer = L.svg({ padding: 0.5 }); 12612 * var line = L.polyline( coordinates, { renderer: myRenderer } ); 12613 * var circle = L.circle( center, { renderer: myRenderer } ); 12614 * ``` 12615 */ 12616 12617var SVG = Renderer.extend({ 12618 12619 getEvents: function () { 12620 var events = Renderer.prototype.getEvents.call(this); 12621 events.zoomstart = this._onZoomStart; 12622 return events; 12623 }, 12624 12625 _initContainer: function () { 12626 this._container = create$2('svg'); 12627 12628 // makes it possible to click through svg root; we'll reset it back in individual paths 12629 this._container.setAttribute('pointer-events', 'none'); 12630 12631 this._rootGroup = create$2('g'); 12632 this._container.appendChild(this._rootGroup); 12633 }, 12634 12635 _destroyContainer: function () { 12636 remove(this._container); 12637 off(this._container); 12638 delete this._container; 12639 delete this._rootGroup; 12640 delete this._svgSize; 12641 }, 12642 12643 _onZoomStart: function () { 12644 // Drag-then-pinch interactions might mess up the center and zoom. 12645 // In this case, the easiest way to prevent this is re-do the renderer 12646 // bounds and padding when the zooming starts. 12647 this._update(); 12648 }, 12649 12650 _update: function () { 12651 if (this._map._animatingZoom && this._bounds) { return; } 12652 12653 Renderer.prototype._update.call(this); 12654 12655 var b = this._bounds, 12656 size = b.getSize(), 12657 container = this._container; 12658 12659 // set size of svg-container if changed 12660 if (!this._svgSize || !this._svgSize.equals(size)) { 12661 this._svgSize = size; 12662 container.setAttribute('width', size.x); 12663 container.setAttribute('height', size.y); 12664 } 12665 12666 // movement: update container viewBox so that we don't have to change coordinates of individual layers 12667 setPosition(container, b.min); 12668 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' ')); 12669 12670 this.fire('update'); 12671 }, 12672 12673 // methods below are called by vector layers implementations 12674 12675 _initPath: function (layer) { 12676 var path = layer._path = create$2('path'); 12677 12678 // @namespace Path 12679 // @option className: String = null 12680 // Custom class name set on an element. Only for SVG renderer. 12681 if (layer.options.className) { 12682 addClass(path, layer.options.className); 12683 } 12684 12685 if (layer.options.interactive) { 12686 addClass(path, 'leaflet-interactive'); 12687 } 12688 12689 this._updateStyle(layer); 12690 this._layers[stamp(layer)] = layer; 12691 }, 12692 12693 _addPath: function (layer) { 12694 if (!this._rootGroup) { this._initContainer(); } 12695 this._rootGroup.appendChild(layer._path); 12696 layer.addInteractiveTarget(layer._path); 12697 }, 12698 12699 _removePath: function (layer) { 12700 remove(layer._path); 12701 layer.removeInteractiveTarget(layer._path); 12702 delete this._layers[stamp(layer)]; 12703 }, 12704 12705 _updatePath: function (layer) { 12706 layer._project(); 12707 layer._update(); 12708 }, 12709 12710 _updateStyle: function (layer) { 12711 var path = layer._path, 12712 options = layer.options; 12713 12714 if (!path) { return; } 12715 12716 if (options.stroke) { 12717 path.setAttribute('stroke', options.color); 12718 path.setAttribute('stroke-opacity', options.opacity); 12719 path.setAttribute('stroke-width', options.weight); 12720 path.setAttribute('stroke-linecap', options.lineCap); 12721 path.setAttribute('stroke-linejoin', options.lineJoin); 12722 12723 if (options.dashArray) { 12724 path.setAttribute('stroke-dasharray', options.dashArray); 12725 } else { 12726 path.removeAttribute('stroke-dasharray'); 12727 } 12728 12729 if (options.dashOffset) { 12730 path.setAttribute('stroke-dashoffset', options.dashOffset); 12731 } else { 12732 path.removeAttribute('stroke-dashoffset'); 12733 } 12734 } else { 12735 path.setAttribute('stroke', 'none'); 12736 } 12737 12738 if (options.fill) { 12739 path.setAttribute('fill', options.fillColor || options.color); 12740 path.setAttribute('fill-opacity', options.fillOpacity); 12741 path.setAttribute('fill-rule', options.fillRule || 'evenodd'); 12742 } else { 12743 path.setAttribute('fill', 'none'); 12744 } 12745 }, 12746 12747 _updatePoly: function (layer, closed) { 12748 this._setPath(layer, pointsToPath(layer._parts, closed)); 12749 }, 12750 12751 _updateCircle: function (layer) { 12752 var p = layer._point, 12753 r = Math.max(Math.round(layer._radius), 1), 12754 r2 = Math.max(Math.round(layer._radiusY), 1) || r, 12755 arc = 'a' + r + ',' + r2 + ' 0 1,0 '; 12756 12757 // drawing a circle with two half-arcs 12758 var d = layer._empty() ? 'M0 0' : 12759 'M' + (p.x - r) + ',' + p.y + 12760 arc + (r * 2) + ',0 ' + 12761 arc + (-r * 2) + ',0 '; 12762 12763 this._setPath(layer, d); 12764 }, 12765 12766 _setPath: function (layer, path) { 12767 layer._path.setAttribute('d', path); 12768 }, 12769 12770 // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements 12771 _bringToFront: function (layer) { 12772 toFront(layer._path); 12773 }, 12774 12775 _bringToBack: function (layer) { 12776 toBack(layer._path); 12777 } 12778}); 12779 12780if (vml) { 12781 SVG.include(vmlMixin); 12782} 12783 12784// @namespace SVG 12785// @factory L.svg(options?: Renderer options) 12786// Creates a SVG renderer with the given options. 12787function svg$1(options) { 12788 return svg || vml ? new SVG(options) : null; 12789} 12790 12791Map.include({ 12792 // @namespace Map; @method getRenderer(layer: Path): Renderer 12793 // Returns the instance of `Renderer` that should be used to render the given 12794 // `Path`. It will ensure that the `renderer` options of the map and paths 12795 // are respected, and that the renderers do exist on the map. 12796 getRenderer: function (layer) { 12797 // @namespace Path; @option renderer: Renderer 12798 // Use this specific instance of `Renderer` for this path. Takes 12799 // precedence over the map's [default renderer](#map-renderer). 12800 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer; 12801 12802 if (!renderer) { 12803 renderer = this._renderer = this._createRenderer(); 12804 } 12805 12806 if (!this.hasLayer(renderer)) { 12807 this.addLayer(renderer); 12808 } 12809 return renderer; 12810 }, 12811 12812 _getPaneRenderer: function (name) { 12813 if (name === 'overlayPane' || name === undefined) { 12814 return false; 12815 } 12816 12817 var renderer = this._paneRenderers[name]; 12818 if (renderer === undefined) { 12819 renderer = this._createRenderer({pane: name}); 12820 this._paneRenderers[name] = renderer; 12821 } 12822 return renderer; 12823 }, 12824 12825 _createRenderer: function (options) { 12826 // @namespace Map; @option preferCanvas: Boolean = false 12827 // Whether `Path`s should be rendered on a `Canvas` renderer. 12828 // By default, all `Path`s are rendered in a `SVG` renderer. 12829 return (this.options.preferCanvas && canvas$1(options)) || svg$1(options); 12830 } 12831}); 12832 12833/* 12834 * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object. 12835 */ 12836 12837/* 12838 * @class Rectangle 12839 * @aka L.Rectangle 12840 * @inherits Polygon 12841 * 12842 * A class for drawing rectangle overlays on a map. Extends `Polygon`. 12843 * 12844 * @example 12845 * 12846 * ```js 12847 * // define rectangle geographical bounds 12848 * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]]; 12849 * 12850 * // create an orange rectangle 12851 * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map); 12852 * 12853 * // zoom the map to the rectangle bounds 12854 * map.fitBounds(bounds); 12855 * ``` 12856 * 12857 */ 12858 12859 12860var Rectangle = Polygon.extend({ 12861 initialize: function (latLngBounds, options) { 12862 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options); 12863 }, 12864 12865 // @method setBounds(latLngBounds: LatLngBounds): this 12866 // Redraws the rectangle with the passed bounds. 12867 setBounds: function (latLngBounds) { 12868 return this.setLatLngs(this._boundsToLatLngs(latLngBounds)); 12869 }, 12870 12871 _boundsToLatLngs: function (latLngBounds) { 12872 latLngBounds = toLatLngBounds(latLngBounds); 12873 return [ 12874 latLngBounds.getSouthWest(), 12875 latLngBounds.getNorthWest(), 12876 latLngBounds.getNorthEast(), 12877 latLngBounds.getSouthEast() 12878 ]; 12879 } 12880}); 12881 12882 12883// @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options) 12884function rectangle(latLngBounds, options) { 12885 return new Rectangle(latLngBounds, options); 12886} 12887 12888SVG.create = create$2; 12889SVG.pointsToPath = pointsToPath; 12890 12891GeoJSON.geometryToLayer = geometryToLayer; 12892GeoJSON.coordsToLatLng = coordsToLatLng; 12893GeoJSON.coordsToLatLngs = coordsToLatLngs; 12894GeoJSON.latLngToCoords = latLngToCoords; 12895GeoJSON.latLngsToCoords = latLngsToCoords; 12896GeoJSON.getFeature = getFeature; 12897GeoJSON.asFeature = asFeature; 12898 12899/* 12900 * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map 12901 * (zoom to a selected bounding box), enabled by default. 12902 */ 12903 12904// @namespace Map 12905// @section Interaction Options 12906Map.mergeOptions({ 12907 // @option boxZoom: Boolean = true 12908 // Whether the map can be zoomed to a rectangular area specified by 12909 // dragging the mouse while pressing the shift key. 12910 boxZoom: true 12911}); 12912 12913var BoxZoom = Handler.extend({ 12914 initialize: function (map) { 12915 this._map = map; 12916 this._container = map._container; 12917 this._pane = map._panes.overlayPane; 12918 this._resetStateTimeout = 0; 12919 map.on('unload', this._destroy, this); 12920 }, 12921 12922 addHooks: function () { 12923 on(this._container, 'mousedown', this._onMouseDown, this); 12924 }, 12925 12926 removeHooks: function () { 12927 off(this._container, 'mousedown', this._onMouseDown, this); 12928 }, 12929 12930 moved: function () { 12931 return this._moved; 12932 }, 12933 12934 _destroy: function () { 12935 remove(this._pane); 12936 delete this._pane; 12937 }, 12938 12939 _resetState: function () { 12940 this._resetStateTimeout = 0; 12941 this._moved = false; 12942 }, 12943 12944 _clearDeferredResetState: function () { 12945 if (this._resetStateTimeout !== 0) { 12946 clearTimeout(this._resetStateTimeout); 12947 this._resetStateTimeout = 0; 12948 } 12949 }, 12950 12951 _onMouseDown: function (e) { 12952 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; } 12953 12954 // Clear the deferred resetState if it hasn't executed yet, otherwise it 12955 // will interrupt the interaction and orphan a box element in the container. 12956 this._clearDeferredResetState(); 12957 this._resetState(); 12958 12959 disableTextSelection(); 12960 disableImageDrag(); 12961 12962 this._startPoint = this._map.mouseEventToContainerPoint(e); 12963 12964 on(document, { 12965 contextmenu: stop, 12966 mousemove: this._onMouseMove, 12967 mouseup: this._onMouseUp, 12968 keydown: this._onKeyDown 12969 }, this); 12970 }, 12971 12972 _onMouseMove: function (e) { 12973 if (!this._moved) { 12974 this._moved = true; 12975 12976 this._box = create$1('div', 'leaflet-zoom-box', this._container); 12977 addClass(this._container, 'leaflet-crosshair'); 12978 12979 this._map.fire('boxzoomstart'); 12980 } 12981 12982 this._point = this._map.mouseEventToContainerPoint(e); 12983 12984 var bounds = new Bounds(this._point, this._startPoint), 12985 size = bounds.getSize(); 12986 12987 setPosition(this._box, bounds.min); 12988 12989 this._box.style.width = size.x + 'px'; 12990 this._box.style.height = size.y + 'px'; 12991 }, 12992 12993 _finish: function () { 12994 if (this._moved) { 12995 remove(this._box); 12996 removeClass(this._container, 'leaflet-crosshair'); 12997 } 12998 12999 enableTextSelection(); 13000 enableImageDrag(); 13001 13002 off(document, { 13003 contextmenu: stop, 13004 mousemove: this._onMouseMove, 13005 mouseup: this._onMouseUp, 13006 keydown: this._onKeyDown 13007 }, this); 13008 }, 13009 13010 _onMouseUp: function (e) { 13011 if ((e.which !== 1) && (e.button !== 1)) { return; } 13012 13013 this._finish(); 13014 13015 if (!this._moved) { return; } 13016 // Postpone to next JS tick so internal click event handling 13017 // still see it as "moved". 13018 this._clearDeferredResetState(); 13019 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0); 13020 13021 var bounds = new LatLngBounds( 13022 this._map.containerPointToLatLng(this._startPoint), 13023 this._map.containerPointToLatLng(this._point)); 13024 13025 this._map 13026 .fitBounds(bounds) 13027 .fire('boxzoomend', {boxZoomBounds: bounds}); 13028 }, 13029 13030 _onKeyDown: function (e) { 13031 if (e.keyCode === 27) { 13032 this._finish(); 13033 } 13034 } 13035}); 13036 13037// @section Handlers 13038// @property boxZoom: Handler 13039// Box (shift-drag with mouse) zoom handler. 13040Map.addInitHook('addHandler', 'boxZoom', BoxZoom); 13041 13042/* 13043 * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default. 13044 */ 13045 13046// @namespace Map 13047// @section Interaction Options 13048 13049Map.mergeOptions({ 13050 // @option doubleClickZoom: Boolean|String = true 13051 // Whether the map can be zoomed in by double clicking on it and 13052 // zoomed out by double clicking while holding shift. If passed 13053 // `'center'`, double-click zoom will zoom to the center of the 13054 // view regardless of where the mouse was. 13055 doubleClickZoom: true 13056}); 13057 13058var DoubleClickZoom = Handler.extend({ 13059 addHooks: function () { 13060 this._map.on('dblclick', this._onDoubleClick, this); 13061 }, 13062 13063 removeHooks: function () { 13064 this._map.off('dblclick', this._onDoubleClick, this); 13065 }, 13066 13067 _onDoubleClick: function (e) { 13068 var map = this._map, 13069 oldZoom = map.getZoom(), 13070 delta = map.options.zoomDelta, 13071 zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta; 13072 13073 if (map.options.doubleClickZoom === 'center') { 13074 map.setZoom(zoom); 13075 } else { 13076 map.setZoomAround(e.containerPoint, zoom); 13077 } 13078 } 13079}); 13080 13081// @section Handlers 13082// 13083// Map properties include interaction handlers that allow you to control 13084// interaction behavior in runtime, enabling or disabling certain features such 13085// as dragging or touch zoom (see `Handler` methods). For example: 13086// 13087// ```js 13088// map.doubleClickZoom.disable(); 13089// ``` 13090// 13091// @property doubleClickZoom: Handler 13092// Double click zoom handler. 13093Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom); 13094 13095/* 13096 * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default. 13097 */ 13098 13099// @namespace Map 13100// @section Interaction Options 13101Map.mergeOptions({ 13102 // @option dragging: Boolean = true 13103 // Whether the map be draggable with mouse/touch or not. 13104 dragging: true, 13105 13106 // @section Panning Inertia Options 13107 // @option inertia: Boolean = * 13108 // If enabled, panning of the map will have an inertia effect where 13109 // the map builds momentum while dragging and continues moving in 13110 // the same direction for some time. Feels especially nice on touch 13111 // devices. Enabled by default unless running on old Android devices. 13112 inertia: !android23, 13113 13114 // @option inertiaDeceleration: Number = 3000 13115 // The rate with which the inertial movement slows down, in pixels/second². 13116 inertiaDeceleration: 3400, // px/s^2 13117 13118 // @option inertiaMaxSpeed: Number = Infinity 13119 // Max speed of the inertial movement, in pixels/second. 13120 inertiaMaxSpeed: Infinity, // px/s 13121 13122 // @option easeLinearity: Number = 0.2 13123 easeLinearity: 0.2, 13124 13125 // TODO refactor, move to CRS 13126 // @option worldCopyJump: Boolean = false 13127 // With this option enabled, the map tracks when you pan to another "copy" 13128 // of the world and seamlessly jumps to the original one so that all overlays 13129 // like markers and vector layers are still visible. 13130 worldCopyJump: false, 13131 13132 // @option maxBoundsViscosity: Number = 0.0 13133 // If `maxBounds` is set, this option will control how solid the bounds 13134 // are when dragging the map around. The default value of `0.0` allows the 13135 // user to drag outside the bounds at normal speed, higher values will 13136 // slow down map dragging outside bounds, and `1.0` makes the bounds fully 13137 // solid, preventing the user from dragging outside the bounds. 13138 maxBoundsViscosity: 0.0 13139}); 13140 13141var Drag = Handler.extend({ 13142 addHooks: function () { 13143 if (!this._draggable) { 13144 var map = this._map; 13145 13146 this._draggable = new Draggable(map._mapPane, map._container); 13147 13148 this._draggable.on({ 13149 dragstart: this._onDragStart, 13150 drag: this._onDrag, 13151 dragend: this._onDragEnd 13152 }, this); 13153 13154 this._draggable.on('predrag', this._onPreDragLimit, this); 13155 if (map.options.worldCopyJump) { 13156 this._draggable.on('predrag', this._onPreDragWrap, this); 13157 map.on('zoomend', this._onZoomEnd, this); 13158 13159 map.whenReady(this._onZoomEnd, this); 13160 } 13161 } 13162 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag'); 13163 this._draggable.enable(); 13164 this._positions = []; 13165 this._times = []; 13166 }, 13167 13168 removeHooks: function () { 13169 removeClass(this._map._container, 'leaflet-grab'); 13170 removeClass(this._map._container, 'leaflet-touch-drag'); 13171 this._draggable.disable(); 13172 }, 13173 13174 moved: function () { 13175 return this._draggable && this._draggable._moved; 13176 }, 13177 13178 moving: function () { 13179 return this._draggable && this._draggable._moving; 13180 }, 13181 13182 _onDragStart: function () { 13183 var map = this._map; 13184 13185 map._stop(); 13186 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) { 13187 var bounds = toLatLngBounds(this._map.options.maxBounds); 13188 13189 this._offsetLimit = toBounds( 13190 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1), 13191 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1) 13192 .add(this._map.getSize())); 13193 13194 this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity)); 13195 } else { 13196 this._offsetLimit = null; 13197 } 13198 13199 map 13200 .fire('movestart') 13201 .fire('dragstart'); 13202 13203 if (map.options.inertia) { 13204 this._positions = []; 13205 this._times = []; 13206 } 13207 }, 13208 13209 _onDrag: function (e) { 13210 if (this._map.options.inertia) { 13211 var time = this._lastTime = +new Date(), 13212 pos = this._lastPos = this._draggable._absPos || this._draggable._newPos; 13213 13214 this._positions.push(pos); 13215 this._times.push(time); 13216 13217 this._prunePositions(time); 13218 } 13219 13220 this._map 13221 .fire('move', e) 13222 .fire('drag', e); 13223 }, 13224 13225 _prunePositions: function (time) { 13226 while (this._positions.length > 1 && time - this._times[0] > 50) { 13227 this._positions.shift(); 13228 this._times.shift(); 13229 } 13230 }, 13231 13232 _onZoomEnd: function () { 13233 var pxCenter = this._map.getSize().divideBy(2), 13234 pxWorldCenter = this._map.latLngToLayerPoint([0, 0]); 13235 13236 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x; 13237 this._worldWidth = this._map.getPixelWorldBounds().getSize().x; 13238 }, 13239 13240 _viscousLimit: function (value, threshold) { 13241 return value - (value - threshold) * this._viscosity; 13242 }, 13243 13244 _onPreDragLimit: function () { 13245 if (!this._viscosity || !this._offsetLimit) { return; } 13246 13247 var offset = this._draggable._newPos.subtract(this._draggable._startPos); 13248 13249 var limit = this._offsetLimit; 13250 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); } 13251 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); } 13252 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); } 13253 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); } 13254 13255 this._draggable._newPos = this._draggable._startPos.add(offset); 13256 }, 13257 13258 _onPreDragWrap: function () { 13259 // TODO refactor to be able to adjust map pane position after zoom 13260 var worldWidth = this._worldWidth, 13261 halfWidth = Math.round(worldWidth / 2), 13262 dx = this._initialWorldOffset, 13263 x = this._draggable._newPos.x, 13264 newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx, 13265 newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx, 13266 newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2; 13267 13268 this._draggable._absPos = this._draggable._newPos.clone(); 13269 this._draggable._newPos.x = newX; 13270 }, 13271 13272 _onDragEnd: function (e) { 13273 var map = this._map, 13274 options = map.options, 13275 13276 noInertia = !options.inertia || this._times.length < 2; 13277 13278 map.fire('dragend', e); 13279 13280 if (noInertia) { 13281 map.fire('moveend'); 13282 13283 } else { 13284 this._prunePositions(+new Date()); 13285 13286 var direction = this._lastPos.subtract(this._positions[0]), 13287 duration = (this._lastTime - this._times[0]) / 1000, 13288 ease = options.easeLinearity, 13289 13290 speedVector = direction.multiplyBy(ease / duration), 13291 speed = speedVector.distanceTo([0, 0]), 13292 13293 limitedSpeed = Math.min(options.inertiaMaxSpeed, speed), 13294 limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed), 13295 13296 decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease), 13297 offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round(); 13298 13299 if (!offset.x && !offset.y) { 13300 map.fire('moveend'); 13301 13302 } else { 13303 offset = map._limitOffset(offset, map.options.maxBounds); 13304 13305 requestAnimFrame(function () { 13306 map.panBy(offset, { 13307 duration: decelerationDuration, 13308 easeLinearity: ease, 13309 noMoveStart: true, 13310 animate: true 13311 }); 13312 }); 13313 } 13314 } 13315 } 13316}); 13317 13318// @section Handlers 13319// @property dragging: Handler 13320// Map dragging handler (by both mouse and touch). 13321Map.addInitHook('addHandler', 'dragging', Drag); 13322 13323/* 13324 * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default. 13325 */ 13326 13327// @namespace Map 13328// @section Keyboard Navigation Options 13329Map.mergeOptions({ 13330 // @option keyboard: Boolean = true 13331 // Makes the map focusable and allows users to navigate the map with keyboard 13332 // arrows and `+`/`-` keys. 13333 keyboard: true, 13334 13335 // @option keyboardPanDelta: Number = 80 13336 // Amount of pixels to pan when pressing an arrow key. 13337 keyboardPanDelta: 80 13338}); 13339 13340var Keyboard = Handler.extend({ 13341 13342 keyCodes: { 13343 left: [37], 13344 right: [39], 13345 down: [40], 13346 up: [38], 13347 zoomIn: [187, 107, 61, 171], 13348 zoomOut: [189, 109, 54, 173] 13349 }, 13350 13351 initialize: function (map) { 13352 this._map = map; 13353 13354 this._setPanDelta(map.options.keyboardPanDelta); 13355 this._setZoomDelta(map.options.zoomDelta); 13356 }, 13357 13358 addHooks: function () { 13359 var container = this._map._container; 13360 13361 // make the container focusable by tabbing 13362 if (container.tabIndex <= 0) { 13363 container.tabIndex = '0'; 13364 } 13365 13366 on(container, { 13367 focus: this._onFocus, 13368 blur: this._onBlur, 13369 mousedown: this._onMouseDown 13370 }, this); 13371 13372 this._map.on({ 13373 focus: this._addHooks, 13374 blur: this._removeHooks 13375 }, this); 13376 }, 13377 13378 removeHooks: function () { 13379 this._removeHooks(); 13380 13381 off(this._map._container, { 13382 focus: this._onFocus, 13383 blur: this._onBlur, 13384 mousedown: this._onMouseDown 13385 }, this); 13386 13387 this._map.off({ 13388 focus: this._addHooks, 13389 blur: this._removeHooks 13390 }, this); 13391 }, 13392 13393 _onMouseDown: function () { 13394 if (this._focused) { return; } 13395 13396 var body = document.body, 13397 docEl = document.documentElement, 13398 top = body.scrollTop || docEl.scrollTop, 13399 left = body.scrollLeft || docEl.scrollLeft; 13400 13401 this._map._container.focus(); 13402 13403 window.scrollTo(left, top); 13404 }, 13405 13406 _onFocus: function () { 13407 this._focused = true; 13408 this._map.fire('focus'); 13409 }, 13410 13411 _onBlur: function () { 13412 this._focused = false; 13413 this._map.fire('blur'); 13414 }, 13415 13416 _setPanDelta: function (panDelta) { 13417 var keys = this._panKeys = {}, 13418 codes = this.keyCodes, 13419 i, len; 13420 13421 for (i = 0, len = codes.left.length; i < len; i++) { 13422 keys[codes.left[i]] = [-1 * panDelta, 0]; 13423 } 13424 for (i = 0, len = codes.right.length; i < len; i++) { 13425 keys[codes.right[i]] = [panDelta, 0]; 13426 } 13427 for (i = 0, len = codes.down.length; i < len; i++) { 13428 keys[codes.down[i]] = [0, panDelta]; 13429 } 13430 for (i = 0, len = codes.up.length; i < len; i++) { 13431 keys[codes.up[i]] = [0, -1 * panDelta]; 13432 } 13433 }, 13434 13435 _setZoomDelta: function (zoomDelta) { 13436 var keys = this._zoomKeys = {}, 13437 codes = this.keyCodes, 13438 i, len; 13439 13440 for (i = 0, len = codes.zoomIn.length; i < len; i++) { 13441 keys[codes.zoomIn[i]] = zoomDelta; 13442 } 13443 for (i = 0, len = codes.zoomOut.length; i < len; i++) { 13444 keys[codes.zoomOut[i]] = -zoomDelta; 13445 } 13446 }, 13447 13448 _addHooks: function () { 13449 on(document, 'keydown', this._onKeyDown, this); 13450 }, 13451 13452 _removeHooks: function () { 13453 off(document, 'keydown', this._onKeyDown, this); 13454 }, 13455 13456 _onKeyDown: function (e) { 13457 if (e.altKey || e.ctrlKey || e.metaKey) { return; } 13458 13459 var key = e.keyCode,
13460 map = this._map, 13461 offset; 13462 13463 if (key in this._panKeys) { 13464 if (!map._panAnim || !map._panAnim._inProgress) { 13465 offset = this._panKeys[key]; 13466 if (e.shiftKey) { 13467 offset = toPoint(offset).multiplyBy(3); 13468 } 13469 13470 map.panBy(offset); 13471 13472 if (map.options.maxBounds) { 13473 map.panInsideBounds(map.options.maxBounds); 13474 } 13475 } 13476 } else if (key in this._zoomKeys) { 13477 map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]); 13478 13479 } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) { 13480 map.closePopup(); 13481 13482 } else { 13483 return; 13484 } 13485 13486 stop(e); 13487 } 13488}); 13489 13490// @section Handlers 13491// @section Handlers 13492// @property keyboard: Handler 13493// Keyboard navigation handler. 13494Map.addInitHook('addHandler', 'keyboard', Keyboard); 13495 13496/* 13497 * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map. 13498 */ 13499 13500// @namespace Map 13501// @section Interaction Options 13502Map.mergeOptions({ 13503 // @section Mousewheel options 13504 // @option scrollWheelZoom: Boolean|String = true 13505 // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`, 13506 // it will zoom to the center of the view regardless of where the mouse was. 13507 scrollWheelZoom: true, 13508 13509 // @option wheelDebounceTime: Number = 40 13510 // Limits the rate at which a wheel can fire (in milliseconds). By default 13511 // user can't zoom via wheel more often than once per 40 ms. 13512 wheelDebounceTime: 40, 13513 13514 // @option wheelPxPerZoomLevel: Number = 60 13515 // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta)) 13516 // mean a change of one full zoom level. Smaller values will make wheel-zooming 13517 // faster (and vice versa). 13518 wheelPxPerZoomLevel: 60 13519}); 13520 13521var ScrollWheelZoom = Handler.extend({ 13522 addHooks: function () { 13523 on(this._map._container, 'mousewheel', this._onWheelScroll, this); 13524 13525 this._delta = 0; 13526 }, 13527 13528 removeHooks: function () { 13529 off(this._map._container, 'mousewheel', this._onWheelScroll, this); 13530 }, 13531 13532 _onWheelScroll: function (e) { 13533 var delta = getWheelDelta(e); 13534 13535 var debounce = this._map.options.wheelDebounceTime; 13536 13537 this._delta += delta; 13538 this._lastMousePos = this._map.mouseEventToContainerPoint(e); 13539 13540 if (!this._startTime) { 13541 this._startTime = +new Date(); 13542 } 13543 13544 var left = Math.max(debounce - (+new Date() - this._startTime), 0); 13545 13546 clearTimeout(this._timer); 13547 this._timer = setTimeout(bind(this._performZoom, this), left); 13548 13549 stop(e); 13550 }, 13551 13552 _performZoom: function () { 13553 var map = this._map, 13554 zoom = map.getZoom(), 13555 snap = this._map.options.zoomSnap || 0; 13556 13557 map._stop(); // stop panning and fly animations if any 13558 13559 // map the delta with a sigmoid function to -4..4 range leaning on -1..1 13560 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4), 13561 d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2, 13562 d4 = snap ? Math.ceil(d3 / snap) * snap : d3, 13563 delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom; 13564 13565 this._delta = 0; 13566 this._startTime = null; 13567 13568 if (!delta) { return; } 13569 13570 if (map.options.scrollWheelZoom === 'center') { 13571 map.setZoom(zoom + delta); 13572 } else { 13573 map.setZoomAround(this._lastMousePos, zoom + delta); 13574 } 13575 } 13576}); 13577 13578// @section Handlers 13579// @property scrollWheelZoom: Handler 13580// Scroll wheel zoom handler. 13581Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom); 13582 13583/* 13584 * L.Map.Tap is used to enable mobile hacks like quick taps and long hold. 13585 */ 13586 13587// @namespace Map 13588// @section Interaction Options 13589Map.mergeOptions({ 13590 // @section Touch interaction options 13591 // @option tap: Boolean = true 13592 // Enables mobile hacks for supporting instant taps (fixing 200ms click 13593 // delay on iOS/Android) and touch holds (fired as `contextmenu` events). 13594 tap: true, 13595 13596 // @option tapTolerance: Number = 15 13597 // The max number of pixels a user can shift his finger during touch 13598 // for it to be considered a valid tap. 13599 tapTolerance: 15 13600}); 13601 13602var Tap = Handler.extend({ 13603 addHooks: function () { 13604 on(this._map._container, 'touchstart', this._onDown, this); 13605 }, 13606 13607 removeHooks: function () { 13608 off(this._map._container, 'touchstart', this._onDown, this); 13609 }, 13610 13611 _onDown: function (e) { 13612 if (!e.touches) { return; } 13613 13614 preventDefault(e); 13615 13616 this._fireClick = true; 13617 13618 // don't simulate click or track longpress if more than 1 touch 13619 if (e.touches.length > 1) { 13620 this._fireClick = false; 13621 clearTimeout(this._holdTimeout); 13622 return; 13623 } 13624 13625 var first = e.touches[0], 13626 el = first.target; 13627 13628 this._startPos = this._newPos = new Point(first.clientX, first.clientY); 13629 13630 // if touching a link, highlight it 13631 if (el.tagName && el.tagName.toLowerCase() === 'a') { 13632 addClass(el, 'leaflet-active'); 13633 } 13634 13635 // simulate long hold but setting a timeout 13636 this._holdTimeout = setTimeout(bind(function () { 13637 if (this._isTapValid()) { 13638 this._fireClick = false; 13639 this._onUp(); 13640 this._simulateEvent('contextmenu', first); 13641 } 13642 }, this), 1000); 13643 13644 this._simulateEvent('mousedown', first); 13645 13646 on(document, { 13647 touchmove: this._onMove, 13648 touchend: this._onUp 13649 }, this); 13650 }, 13651 13652 _onUp: function (e) { 13653 clearTimeout(this._holdTimeout); 13654 13655 off(document, { 13656 touchmove: this._onMove, 13657 touchend: this._onUp 13658 }, this); 13659 13660 if (this._fireClick && e && e.changedTouches) { 13661 13662 var first = e.changedTouches[0], 13663 el = first.target; 13664 13665 if (el && el.tagName && el.tagName.toLowerCase() === 'a') { 13666 removeClass(el, 'leaflet-active'); 13667 } 13668 13669 this._simulateEvent('mouseup', first); 13670 13671 // simulate click if the touch didn't move too much 13672 if (this._isTapValid()) { 13673 this._simulateEvent('click', first); 13674 } 13675 } 13676 }, 13677 13678 _isTapValid: function () { 13679 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance; 13680 }, 13681 13682 _onMove: function (e) { 13683 var first = e.touches[0]; 13684 this._newPos = new Point(first.clientX, first.clientY); 13685 this._simulateEvent('mousemove', first); 13686 }, 13687 13688 _simulateEvent: function (type, e) { 13689 var simulatedEvent = document.createEvent('MouseEvents'); 13690 13691 simulatedEvent._simulated = true; 13692 e.target._simulatedClick = true; 13693 13694 simulatedEvent.initMouseEvent( 13695 type, true, true, window, 1, 13696 e.screenX, e.screenY, 13697 e.clientX, e.clientY, 13698 false, false, false, false, 0, null); 13699 13700 e.target.dispatchEvent(simulatedEvent); 13701 } 13702}); 13703 13704// @section Handlers 13705// @property tap: Handler 13706// Mobile touch hacks (quick tap and touch hold) handler. 13707if (touch && !pointer) { 13708 Map.addInitHook('addHandler', 'tap', Tap); 13709} 13710 13711/* 13712 * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supp
vendor: 4,150 bytes, lines 13712-13840
13712orted mobile browsers. 13713 */ 13714 13715// @namespace Map 13716// @section Interaction Options 13717Map.mergeOptions({ 13718 // @section Touch interaction options 13719 // @option touchZoom: Boolean|String = * 13720 // Whether the map can be zoomed by touch-dragging with two fingers. If 13721 // passed `'center'`, it will zoom to the center of the view regardless of 13722 // where the touch events (fingers) were. Enabled for touch-capable web 13723 // browsers except for old Androids. 13724 touchZoom: touch && !android23, 13725 13726 // @option bounceAtZoomLimits: Boolean = true 13727 // Set it to false if you don't want the map to zoom beyond min/max zoom 13728 // and then bounce back when pinch-zooming. 13729 bounceAtZoomLimits: true 13730}); 13731 13732var TouchZoom = Handler.extend({ 13733 addHooks: function () { 13734 addClass(this._map._container, 'leaflet-touch-zoom'); 13735 on(this._map._container, 'touchstart', this._onTouchStart, this); 13736 }, 13737 13738 removeHooks: function () { 13739 removeClass(this._map._container, 'leaflet-touch-zoom'); 13740 off(this._map._container, 'touchstart', this._onTouchStart, this); 13741 }, 13742 13743 _onTouchStart: function (e) { 13744 var map = this._map; 13745 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; } 13746 13747 var p1 = map.mouseEventToContainerPoint(e.touches[0]), 13748 p2 = map.mouseEventToContainerPoint(e.touches[1]); 13749 13750 this._centerPoint = map.getSize()._divideBy(2); 13751 this._startLatLng = map.containerPointToLatLng(this._centerPoint); 13752 if (map.options.touchZoom !== 'center') { 13753 this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2)); 13754 } 13755 13756 this._startDist = p1.distanceTo(p2); 13757 this._startZoom = map.getZoom(); 13758 13759 this._moved = false; 13760 this._zooming = true; 13761 13762 map._stop(); 13763 13764 on(document, 'touchmove', this._onTouchMove, this); 13765 on(document, 'touchend', this._onTouchEnd, this); 13766 13767 preventDefault(e); 13768 }, 13769 13770 _onTouchMove: function (e) { 13771 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; } 13772 13773 var map = this._map, 13774 p1 = map.mouseEventToContainerPoint(e.touches[0]), 13775 p2 = map.mouseEventToContainerPoint(e.touches[1]), 13776 scale = p1.distanceTo(p2) / this._startDist; 13777 13778 this._zoom = map.getScaleZoom(scale, this._startZoom); 13779 13780 if (!map.options.bounceAtZoomLimits && ( 13781 (this._zoom < map.getMinZoom() && scale < 1) || 13782 (this._zoom > map.getMaxZoom() && scale > 1))) { 13783 this._zoom = map._limitZoom(this._zoom); 13784 } 13785 13786 if (map.options.touchZoom === 'center') { 13787 this._center = this._startLatLng; 13788 if (scale === 1) { return; } 13789 } else { 13790 // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng 13791 var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint); 13792 if (scale === 1 && delta.x === 0 && delta.y === 0) { return; } 13793 this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom); 13794 } 13795 13796 if (!this._moved) { 13797 map._moveStart(true, false); 13798 this._moved = true; 13799 } 13800 13801 cancelAnimFrame(this._animRequest); 13802 13803 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false}); 13804 this._animRequest = requestAnimFrame(moveFn, this, true); 13805 13806 preventDefault(e); 13807 }, 13808 13809 _onTouchEnd: function () { 13810 if (!this._moved || !this._zooming) { 13811 this._zooming = false; 13812 return; 13813 } 13814 13815 this._zooming = false; 13816 cancelAnimFrame(this._animRequest); 13817 13818 off(document, 'touchmove', this._onTouchMove); 13819 off(document, 'touchend', this._onTouchEnd); 13820 13821 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate. 13822 if (this._map.options.zoomAnimation) { 13823 this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap); 13824 } else { 13825 this._map._resetView(this._center, this._map._limitZoom(this._zoom)); 13826 } 13827 } 13828}); 13829 13830// @section Handlers 13831// @property touchZoom: Handler 13832// Touch zoom handler. 13833Map.addInitHook('addHandler', 'touchZoom', TouchZoom); 13834 13835Map.BoxZoom = BoxZoom; 13836Map.DoubleClickZoom = DoubleClickZoom; 13837Map.Drag = Drag; 13838Map.Keyboard = Keyboard; 13839Map.ScrollWheelZoom = ScrollWheelZoom; 13840Map.Tap = Tap;
13841Map.TouchZoom = TouchZoom; 13842 13843Object.freeze = freeze; 13844 13845exports.version = version; 13846exports.Control = Control; 13847exports.control = control; 13848exports.Browser = Browser; 13849exports.Evented = Evented; 13850exports.Mixin = Mixin; 13851exports.Util = Util; 13852exports.Class = Class; 13853exports.Handler = Handler; 13854exports.extend = extend; 13855exports.bind = bind; 13856exports.stamp = stamp; 13857exports.setOptions = setOptions; 13858exports.DomEvent = DomEvent; 13859exports.DomUtil = DomUtil; 13860exports.PosAnimation = PosAnimation; 13861exports.Draggable = Draggable; 13862exports.LineUtil = LineUtil; 13863exports.PolyUtil = PolyUtil; 13864exports.Point = Point; 13865exports.point = toPoint; 13866exports.Bounds = Bounds; 13867exports.bounds = toBounds; 13868exports.Transformation = Transformation; 13869exports.transformation = toTransformation; 13870exports.Projection = index; 13871exports.LatLng = LatLng; 13872exports.latLng = toLatLng; 13873exports.LatLngBounds = LatLngBounds; 13874exports.latLngBounds = toLatLngBounds; 13875exports.CRS = CRS; 13876exports.GeoJSON = GeoJSON; 13877exports.geoJSON = geoJSON; 13878exports.geoJson = geoJson; 13879exports.Layer = Layer; 13880exports.LayerGroup = LayerGroup; 13881exports.layerGroup = layerGroup; 13882exports.FeatureGroup = FeatureGroup; 13883exports.featureGroup = featureGroup; 13884exports.ImageOverlay = ImageOverlay; 13885exports.imageOverlay = imageOverlay; 13886exports.VideoOverlay = VideoOverlay; 13887exports.videoOverlay = videoOverlay; 13888exports.DivOverlay = DivOverlay; 13889exports.Popup = Popup; 13890exports.popup = popup; 13891exports.Tooltip = Tooltip; 13892exports.tooltip = tooltip; 13893exports.Icon = Icon; 13894exports.icon = icon; 13895exports.DivIcon = DivIcon; 13896exports.divIcon = divIcon; 13897exports.Marker = Marker; 13898exports.marker = marker; 13899exports.TileLayer = TileLayer; 13900exports.tileLayer = tileLayer; 13901exports.GridLayer = GridLayer; 13902exports.gridLayer = gridLayer; 13903exports.SVG = SVG; 13904exports.svg = svg$1; 13905exports.Renderer = Renderer; 13906exports.Canvas = Canvas; 13907exports.canvas = canvas$1; 13908exports.Path = Path; 13909exports.CircleMarker = CircleMarker; 13910exports.circleMarker = circleMarker; 13911exports.Circle = Circle; 13912exports.circle = circle; 13913exports.Polyline = Polyline; 13914exports.polyline = polyline; 13915exports.Polygon = Polygon; 13916exports.polygon = polygon; 13917exports.Rectangle = Rectangle; 13918exports.rectangle = rectangle; 13919exports.Map = Map; 13920exports.map = createMap; 13921 13922var oldL = window.L; 13923exports.noConflict = function() { 13924 window.L = oldL; 13925 return this; 13926} 13927 13928// Always export us to window global (see #2364) 13929window.L = exports; 13930 13931}))); 13932//# sourceMappingURL=leaflet-src.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.