vendor: 2,390 bytes, lines 1-73
1/* @preserve 2 * Leaflet 1.6.0, a JS library for interactive maps. http://leafletjs.com 3 * (c) 2010-2019 Vladimir Agafonkin, (c) 2010-2011 CloudMade 4 */ 5 6(function (global, factory) { 7 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 8 typeof define === 'function' && define.amd ? define(['exports'], factory) : 9 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.L = {})); 10})(this, (function (exports) { 'use strict'; 11 12 var version = "1.6.0"; 13 14 /* 15 * @namespace Util 16 * 17 * Various utility functions, used by Leaflet internally. 18 */ 19 20 // @function extend(dest: Object, src?: Object): Object 21 // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut. 22 function extend(dest) { 23 var i, j, len, src; 24 25 for (j = 1, len = arguments.length; j < len; j++) { 26 src = arguments[j]; 27 for (i in src) { 28 dest[i] = src[i]; 29 } 30 } 31 return dest; 32 } 33 34 // @function create(proto: Object, properties?: Object): Object 35 // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create) 36 var create$2 = Object.create || (function () { 37 function F() {} 38 return function (proto) { 39 F.prototype = proto; 40 return new F(); 41 }; 42 })(); 43 44 // @function bind(fn: Function, â¦): Function 45 // 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). 46 // Has a `L.bind()` shortcut. 47 function bind(fn, obj) { 48 var slice = Array.prototype.slice; 49 50 if (fn.bind) { 51 return fn.bind.apply(fn, slice.call(arguments, 1)); 52 } 53 54 var args = slice.call(arguments, 2); 55 56 return function () { 57 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments); 58 }; 59 } 60 61 // @property lastId: Number 62 // Last unique ID used by [`stamp()`](#util-stamp) 63 var lastId = 0; 64 65 // @function stamp(obj: Object): Number 66 // Returns the unique ID of an object, assigning it one if it doesn't have it. 67 function stamp(obj) { 68 /*eslint-disable */ 69 obj._leaflet_id = obj._leaflet_id || ++lastId; 70 return obj._leaflet_id; 71 /* eslint-enable */ 72 } 73
vendor: 11,050 bytes, lines 74-395
74 // @function throttle(fn: Function, time: Number, context: Object): Function 75 // Returns a function which executes function `fn` with the given scope `context` 76 // (so that the `this` keyword refers to `context` inside `fn`'s code). The function 77 // `fn` will be called no more than one time per given amount of `time`. The arguments 78 // received by the bound function will be any arguments passed when binding the 79 // function, followed by any arguments passed when invoking the bound function. 80 // Has an `L.throttle` shortcut. 81 function throttle(fn, time, context) { 82 var lock, args, wrapperFn, later; 83 84 later = function () { 85 // reset lock and call if queued 86 lock = false; 87 if (args) { 88 wrapperFn.apply(context, args); 89 args = false; 90 } 91 }; 92 93 wrapperFn = function () { 94 if (lock) { 95 // called too soon, queue to call later 96 args = arguments; 97 98 } else { 99 // call and lock until later 100 fn.apply(context, arguments); 101 setTimeout(later, time); 102 lock = true; 103 } 104 }; 105 106 return wrapperFn; 107 } 108 109 // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number 110 // Returns the number `num` modulo `range` in such a way so it lies within 111 // `range[0]` and `range[1]`. The returned value will be always smaller than 112 // `range[1]` unless `includeMax` is set to `true`. 113 function wrapNum(x, range, includeMax) { 114 var max = range[1], 115 min = range[0], 116 d = max - min; 117 return x === max && includeMax ? x : ((x - min) % d + d) % d + min; 118 } 119 120 // @function falseFn(): Function 121 // Returns a function which always returns `false`. 122 function falseFn() { return false; } 123 124 // @function formatNum(num: Number, digits?: Number): Number 125 // Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default. 126 function formatNum(num, digits) { 127 var pow = Math.pow(10, (digits === undefined ? 6 : digits)); 128 return Math.round(num * pow) / pow; 129 } 130 131 // @function trim(str: String): String 132 // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim) 133 function trim(str) { 134 return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); 135 } 136 137 // @function splitWords(str: String): String[] 138 // Trims and splits the string on whitespace and returns the array of parts. 139 function splitWords(str) { 140 return trim(str).split(/\s+/); 141 } 142 143 // @function setOptions(obj: Object, options: Object): Object 144 // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut. 145 function setOptions(obj, options) { 146 if (!Object.prototype.hasOwnProperty.call(obj, 'options')) { 147 obj.options = obj.options ? create$2(obj.options) : {}; 148 } 149 for (var i in options) { 150 obj.options[i] = options[i]; 151 } 152 return obj.options; 153 } 154 155 // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String 156 // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}` 157 // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will 158 // be appended at the end. If `uppercase` is `true`, the parameter names will 159 // be uppercased (e.g. `'?A=foo&B=bar'`) 160 function getParamString(obj, existingUrl, uppercase) { 161 var params = []; 162 for (var i in obj) { 163 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i])); 164 } 165 return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); 166 } 167 168 var templateRe = /\{ *([\w_-]+) *\}/g; 169 170 // @function template(str: String, data: Object): String 171 // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'` 172 // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string 173 // `('Hello foo, bar')`. You can also specify functions instead of strings for 174 // data values â they will be evaluated passing `data` as an argument. 175 function template(str, data) { 176 return str.replace(templateRe, function (str, key) { 177 var value = data[key]; 178 179 if (value === undefined) { 180 throw new Error('No value provided for variable ' + str); 181 182 } else if (typeof value === 'function') { 183 value = value(data); 184 } 185 return value; 186 }); 187 } 188 189 // @function isArray(obj): Boolean 190 // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) 191 var isArray = Array.isArray || function (obj) { 192 return (Object.prototype.toString.call(obj) === '[object Array]'); 193 }; 194 195 // @function indexOf(array: Array, el: Object): Number 196 // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf) 197 function indexOf(array, el) { 198 for (var i = 0; i < array.length; i++) { 199 if (array[i] === el) { return i; } 200 } 201 return -1; 202 } 203 204 // @property emptyImageUrl: String 205 // Data URI string containing a base64-encoded empty GIF image. 206 // Used as a hack to free memory from unused images on WebKit-powered 207 // mobile devices (by setting image `src` to this string). 208 var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; 209 210 // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 211 212 function getPrefixed(name) { 213 return window['webkit' + name] || window['moz' + name] || window['ms' + name]; 214 } 215 216 var lastTime = 0; 217 218 // fallback for IE 7-8 219 function timeoutDefer(fn) { 220 var time = +new Date(), 221 timeToCall = Math.max(0, 16 - (time - lastTime)); 222 223 lastTime = time + timeToCall; 224 return window.setTimeout(fn, timeToCall); 225 } 226 227 var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer; 228 var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') || 229 getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); }; 230 231 // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number 232 // Schedules `fn` to be executed when the browser repaints. `fn` is bound to 233 // `context` if given. When `immediate` is set, `fn` is called immediately if 234 // the browser doesn't have native support for 235 // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame), 236 // otherwise it's delayed. Returns a request ID that can be used to cancel the request. 237 function requestAnimFrame(fn, context, immediate) { 238 if (immediate && requestFn === timeoutDefer) { 239 fn.call(context); 240 } else { 241 return requestFn.call(window, bind(fn, context)); 242 } 243 } 244 245 // @function cancelAnimFrame(id: Number): undefined 246 // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame). 247 function cancelAnimFrame(id) { 248 if (id) { 249 cancelFn.call(window, id); 250 } 251 } 252 253 var Util = { 254 __proto__: null, 255 extend: extend, 256 create: create$2, 257 bind: bind, 258 get lastId () { return lastId; }, 259 stamp: stamp, 260 throttle: throttle, 261 wrapNum: wrapNum, 262 falseFn: falseFn, 263 formatNum: formatNum, 264 trim: trim, 265 splitWords: splitWords, 266 setOptions: setOptions, 267 getParamString: getParamString, 268 template: template, 269 isArray: isArray, 270 indexOf: indexOf, 271 emptyImageUrl: emptyImageUrl, 272 requestFn: requestFn, 273 cancelFn: cancelFn, 274 requestAnimFrame: requestAnimFrame, 275 cancelAnimFrame: cancelAnimFrame 276 }; 277 278 // @class Class 279 // @aka L.Class 280 281 // @section 282 // @uninheritable 283 284 // Thanks to John Resig and Dean Edwards for inspiration! 285 286 function Class() {} 287 288 Class.extend = function (props) { 289 290 // @function extend(props: Object): Function 291 // [Extends the current class](#class-inheritance) given the properties to be included. 292 // Returns a Javascript function that is a class constructor (to be called with `new`). 293 var NewClass = function () { 294 295 // call the constructor 296 if (this.initialize) { 297 this.initialize.apply(this, arguments); 298 } 299 300 // call all constructor hooks 301 this.callInitHooks(); 302 }; 303 304 var parentProto = NewClass.__super__ = this.prototype; 305 306 var proto = create$2(parentProto); 307 proto.constructor = NewClass; 308 309 NewClass.prototype = proto; 310 311 // inherit parent's statics 312 for (var i in this) { 313 if (Object.prototype.hasOwnProperty.call(this, i) && i !== 'prototype' && i !== '__super__') { 314 NewClass[i] = this[i]; 315 } 316 } 317 318 // mix static properties into the class 319 if (props.statics) { 320 extend(NewClass, props.statics); 321 delete props.statics; 322 } 323 324 // mix includes into the prototype 325 if (props.includes) { 326 checkDeprecatedMixinEvents(props.includes); 327 extend.apply(null, [proto].concat(props.includes)); 328 delete props.includes; 329 } 330 331 // merge options 332 if (proto.options) { 333 props.options = extend(create$2(proto.options), props.options); 334 } 335 336 // mix given properties into the prototype 337 extend(proto, props); 338 339 proto._initHooks = []; 340 341 // add method for calling all hooks 342 proto.callInitHooks = function () { 343 344 if (this._initHooksCalled) { return; } 345 346 if (parentProto.callInitHooks) { 347 parentProto.callInitHooks.call(this); 348 } 349 350 this._initHooksCalled = true; 351 352 for (var i = 0, len = proto._initHooks.length; i < len; i++) { 353 proto._initHooks[i].call(this); 354 } 355 }; 356 357 return NewClass; 358 }; 359 360 361 // @function include(properties: Object): this 362 // [Includes a mixin](#class-includes) into the current class. 363 Class.include = function (props) { 364 extend(this.prototype, props); 365 return this; 366 }; 367 368 // @function mergeOptions(options: Object): this 369 // [Merges `options`](#class-options) into the defaults of the class. 370 Class.mergeOptions = function (options) { 371 extend(this.prototype.options, options); 372 return this; 373 }; 374 375 // @function addInitHook(fn: Function): this 376 // Adds a [constructor hook](#class-constructor-hooks) to the class. 377 Class.addInitHook = function (fn) { // (Function) || (String, args...) 378 var args = Array.prototype.slice.call(arguments, 1); 379 380 var init = typeof fn === 'function' ? fn : function () { 381 this[fn].apply(this, args); 382 }; 383 384 this.prototype._initHooks = this.prototype._initHooks || []; 385 this.prototype._initHooks.push(init); 386 return this; 387 }; 388 389 function checkDeprecatedMixinEvents(includes) { 390 if (typeof L === 'undefined' || !L || !L.Mixin) { return; } 391 392 includes = isArray(includes) ? includes : [includes]; 393 394 for (var i = 0; i < includes.length; i++) { 395 if (includes[i] === L.Mixin.Events) {
396 console.warn('Deprecated include of L.Mixin.Events: ' + 397 'this property will be removed in future releases, ' + 398 'please inherit from L.Evented instead.', new Error().stack); 399 } 400 } 401 } 402 403 /* 404 * @class Evented 405 * @aka L.Evented 406 * @inherits Class 407 * 408 * 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). 409 * 410 * @example 411 * 412 * ```js 413 * map.on('click', function(e) { 414 * alert(e.latlng); 415 * } ); 416 * ``` 417 * 418 * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function: 419 * 420 * ```js 421 * function onClick(e) { ... } 422 * 423 * map.on('click', onClick); 424 * map.off('click', onClick); 425 * ``` 426 */ 427 428 var Events = { 429 /* @method on(type: String, fn: Function, context?: Object): this 430 * 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'`). 431 * 432 * @alternative 433 * @method on(eventMap: Object): this 434 * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` 435 */ 436 on: function (types, fn, context) { 437 438 // types can be a map of types/handlers 439 if (typeof types === 'object') { 440 for (var type in types) { 441 // we don't process space-separated events here for performance; 442 // it's a hot path since Layer uses the on(obj) syntax 443 this._on(type, types[type], fn); 444 } 445 446 } else { 447 // types can be a string of space-separated words 448 types = splitWords(types); 449 450 for (var i = 0, len = types.length; i < len; i++) { 451 this._on(types[i], fn, context); 452 } 453 } 454 455 return this; 456 }, 457 458 /* @method off(type: String, fn?: Function, context?: Object): this 459 * 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. 460 * 461 * @alternative 462 * @method off(eventMap: Object): this 463 * Removes a set of type/listener pairs. 464 * 465 * @alternative 466 * @method off: this 467 * Removes all listeners to all events on the object. This includes implicitly attached events. 468 */ 469 off: function (types, fn, context) { 470 471 if (!types) { 472 // clear all listeners if called without arguments 473 delete this._events; 474 475 } else if (typeof types === 'object') { 476 for (var type in types) { 477 this._off(type, types[type], fn); 478 } 479 480 } else { 481 types = splitWords(types); 482 483 for (var i = 0, len = types.length; i < len; i++) { 484 this._off(types[i], fn, context); 485 } 486 } 487 488 return this; 489 }, 490 491 // attach listener (without syntactic sugar now) 492 _on: function (type, fn, context) { 493 this._events = this._events || {}; 494 495 /* get/init listeners for type */ 496 var typeListeners = this._events[type]; 497 if (!typeListeners) { 498 typeListeners = []; 499 this._events[type] = typeListeners; 500 } 501 502 if (context === this) { 503 // Less memory footprint. 504 context = undefined; 505 } 506 var newListener = {fn: fn, ctx: context}, 507 listeners = typeListeners; 508 509 // check if fn already there 510 for (var i = 0, len = listeners.length; i < len; i++) { 511 if (listeners[i].fn === fn && listeners[i].ctx === context) { 512 return; 513 } 514 } 515 516 listeners.push(newListener); 517 }, 518 519 _off: function (type, fn, context) { 520 var listeners, 521 i, 522 len; 523 524 if (!this._events) { return; } 525 526 listeners = this._events[type]; 527 528 if (!listeners) { 529 return; 530 } 531 532 if (!fn) { 533 // Set all removed listeners to noop so they are not called if remove happens in fire 534 for (i = 0, len = listeners.length; i < len; i++) { 535 listeners[i].fn = falseFn; 536 } 537 // clear all listeners for a type if function isn't specified 538 delete this._events[type]; 539 return; 540 } 541 542 if (context === this) { 543 context = undefined; 544 } 545 546 if (listeners) { 547 548 // find fn and remove it 549 for (i = 0, len = listeners.length; i < len; i++) { 550 var l = listeners[i];
551 if (l.ctx !== context) { continue; } 552 if (l.fn === fn) { 553 554 // set the removed listener to noop so that's not called if remove happens in fire 555 l.fn = falseFn; 556 557 if (this._firingCount) { 558 /* copy array in case events are being fired */ 559 this._events[type] = listeners = listeners.slice(); 560 } 561 listeners.splice(i, 1); 562 563 return; 564 } 565 } 566 } 567 }, 568 569 // @method fire(type: String, data?: Object, propagate?: Boolean): this 570 // Fires an event of the specified type. You can optionally provide an data 571 // object â the first argument of the listener function will contain its 572 // properties. The event can optionally be propagated to event parents. 573 fire: function (type, data, propagate) { 574 if (!this.listens(type, propagate)) { return this; } 575 576 var event = extend({}, data, { 577 type: type, 578 target: this, 579 sourceTarget: data && data.sourceTarget || this 580 }); 581 582 if (this._events) { 583 var listeners = this._events[type]; 584 585 if (listeners) { 586 this._firingCount = (this._firingCount + 1) || 1; 587 for (var i = 0, len = listeners.length; i < len; i++) { 588 var l = listeners[i]; 589 l.fn.call(l.ctx || this, event); 590 } 591 592 this._firingCount--; 593 } 594 } 595 596 if (propagate) { 597 // propagate the event to parents (set with addEventParent) 598 this._propagateEvent(event); 599 } 600 601 return this; 602 }, 603 604 // @method listens(type: String): Boolean 605 // Returns `true` if a particular event type has any listeners attached to it. 606 listens: function (type, propagate) { 607 var listeners = this._events && this._events[type]; 608 if (listeners && listeners.length) { return true; } 609 610 if (propagate) { 611 // also check parents for listeners if event propagates 612 for (var id in this._eventParents) { 613 if (this._eventParents[id].listens(type, propagate)) { return true; } 614 } 615 } 616 return false; 617 }, 618 619 // @method once(â¦): this 620 // Behaves as [`on(â¦)`](#evented-on), except the listener will only get fired once and then removed. 621 once: function (types, fn, context) { 622 623 if (typeof types === 'object') { 624 for (var type in types) { 625 this.once(type, types[type], fn); 626 } 627 return this; 628 } 629 630 var handler = bind(function () { 631 this 632 .off(types, fn, context) 633 .off(types, handler, context); 634 }, this); 635 636 // add a listener that's executed once and removed after that 637 return this 638 .on(types, fn, context) 639 .on(types, handler, context)
vendor: 9,158 bytes, lines 639-954
639; 640 }, 641 642 // @method addEventParent(obj: Evented): this 643 // Adds an event parent - an `Evented` that will receive propagated events 644 addEventParent: function (obj) { 645 this._eventParents = this._eventParents || {}; 646 this._eventParents[stamp(obj)] = obj; 647 return this; 648 }, 649 650 // @method removeEventParent(obj: Evented): this 651 // Removes an event parent, so it will stop receiving propagated events 652 removeEventParent: function (obj) { 653 if (this._eventParents) { 654 delete this._eventParents[stamp(obj)]; 655 } 656 return this; 657 }, 658 659 _propagateEvent: function (e) { 660 for (var id in this._eventParents) { 661 this._eventParents[id].fire(e.type, extend({ 662 layer: e.target, 663 propagatedFrom: e.target 664 }, e), true); 665 } 666 } 667 }; 668 669 // aliases; we should ditch those eventually 670 671 // @method addEventListener(â¦): this 672 // Alias to [`on(â¦)`](#evented-on) 673 Events.addEventListener = Events.on; 674 675 // @method removeEventListener(â¦): this 676 // Alias to [`off(â¦)`](#evented-off) 677 678 // @method clearAllEventListeners(â¦): this 679 // Alias to [`off()`](#evented-off) 680 Events.removeEventListener = Events.clearAllEventListeners = Events.off; 681 682 // @method addOneTimeEventListener(â¦): this 683 // Alias to [`once(â¦)`](#evented-once) 684 Events.addOneTimeEventListener = Events.once; 685 686 // @method fireEvent(â¦): this 687 // Alias to [`fire(â¦)`](#evented-fire) 688 Events.fireEvent = Events.fire; 689 690 // @method hasEventListeners(â¦): Boolean 691 // Alias to [`listens(â¦)`](#evented-listens) 692 Events.hasEventListeners = Events.listens; 693 694 var Evented = Class.extend(Events); 695 696 /* 697 * @class Point 698 * @aka L.Point 699 * 700 * Represents a point with `x` and `y` coordinates in pixels. 701 * 702 * @example 703 * 704 * ```js 705 * var point = L.point(200, 300); 706 * ``` 707 * 708 * 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: 709 * 710 * ```js 711 * map.panBy([200, 300]); 712 * map.panBy(L.point(200, 300)); 713 * ``` 714 * 715 * Note that `Point` does not inherit from Leaflet's `Class` object, 716 * which means new classes can't inherit from it, and new methods 717 * can't be added to it with the `include` function. 718 */ 719 720 function Point(x, y, round) { 721 // @property x: Number; The `x` coordinate of the point 722 this.x = (round ? Math.round(x) : x); 723 // @property y: Number; The `y` coordinate of the point 724 this.y = (round ? Math.round(y) : y); 725 } 726 727 var trunc = Math.trunc || function (v) { 728 return v > 0 ? Math.floor(v) : Math.ceil(v); 729 }; 730 731 Point.prototype = { 732 733 // @method clone(): Point 734 // Returns a copy of the current point. 735 clone: function () { 736 return new Point(this.x, this.y); 737 }, 738 739 // @method add(otherPoint: Point): Point 740 // Returns the result of addition of the current and the given points. 741 add: function (point) { 742 // non-destructive, returns a new point 743 return this.clone()._add(toPoint(point)); 744 }, 745 746 _add: function (point) { 747 // destructive, used directly for performance in situations where it's safe to modify existing point 748 this.x += point.x; 749 this.y += point.y; 750 return this; 751 }, 752 753 // @method subtract(otherPoint: Point): Point 754 // Returns the result of subtraction of the given point from the current. 755 subtract: function (point) { 756 return this.clone()._subtract(toPoint(point)); 757 }, 758 759 _subtract: function (point) { 760 this.x -= point.x; 761 this.y -= point.y; 762 return this; 763 }, 764 765 // @method divideBy(num: Number): Point 766 // Returns the result of division of the current point by the given number. 767 divideBy: function (num) { 768 return this.clone()._divideBy(num); 769 }, 770 771 _divideBy: function (num) { 772 this.x /= num; 773 this.y /= num; 774 return this; 775 }, 776 777 // @method multiplyBy(num: Number): Point 778 // Returns the result of multiplication of the current point by the given number. 779 multiplyBy: function (num) { 780 return this.clone()._multiplyBy(num); 781 }, 782 783 _multiplyBy: function (num) { 784 this.x *= num; 785 this.y *= num; 786 return this; 787 }, 788 789 // @method scaleBy(scale: Point): Point 790 // Multiply each coordinate of the current point by each coordinate of 791 // `scale`. In linear algebra terms, multiply the point by the 792 // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation) 793 // defined by `scale`. 794 scaleBy: function (point) { 795 return new Point(this.x * point.x, this.y * point.y); 796 }, 797 798 // @method unscaleBy(scale: Point): Point 799 // Inverse of `scaleBy`. Divide each coordinate of the current point by 800 // each coordinate of `scale`. 801 unscaleBy: function (point) { 802 return new Point(this.x / point.x, this.y / point.y); 803 }, 804 805 // @method round(): Point 806 // Returns a copy of the current point with rounded coordinates. 807 round: function () { 808 return this.clone()._round(); 809 }, 810 811 _round: function () { 812 this.x = Math.round(this.x); 813 this.y = Math.round(this.y); 814 return this; 815 }, 816 817 // @method floor(): Point 818 // Returns a copy of the current point with floored coordinates (rounded down). 819 floor: function () { 820 return this.clone()._floor(); 821 }, 822 823 _floor: function () { 824 this.x = Math.floor(this.x); 825 this.y = Math.floor(this.y); 826 return this; 827 }, 828 829 // @method ceil(): Point 830 // Returns a copy of the current point with ceiled coordinates (rounded up). 831 ceil: function () { 832 return this.clone()._ceil(); 833 }, 834 835 _ceil: function () { 836 this.x = Math.ceil(this.x); 837 this.y = Math.ceil(this.y); 838 return this; 839 }, 840 841 // @method trunc(): Point 842 // Returns a copy of the current point with truncated coordinates (rounded towards zero). 843 trunc: function () { 844 return this.clone()._trunc(); 845 }, 846 847 _trunc: function () { 848 this.x = trunc(this.x); 849 this.y = trunc(this.y); 850 return this; 851 }, 852 853 // @method distanceTo(otherPoint: Point): Number 854 // Returns the cartesian distance between the current and the given points. 855 distanceTo: function (point) { 856 point = toPoint(point); 857 858 var x = point.x - this.x, 859 y = point.y - this.y; 860 861 return Math.sqrt(x * x + y * y); 862 }, 863 864 // @method equals(otherPoint: Point): Boolean 865 // Returns `true` if the given point has the same coordinates. 866 equals: function (point) { 867 point = toPoint(point); 868 869 return point.x === this.x && 870 point.y === this.y; 871 }, 872 873 // @method contains(otherPoint: Point): Boolean 874 // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values). 875 contains: function (point) { 876 point = toPoint(point); 877 878 return Math.abs(point.x) <= Math.abs(this.x) && 879 Math.abs(point.y) <= Math.abs(this.y); 880 }, 881 882 // @method toString(): String 883 // Returns a string representation of the point for debugging purposes. 884 toString: function () { 885 return 'Point(' + 886 formatNum(this.x) + ', ' + 887 formatNum(this.y) + ')'; 888 } 889 }; 890 891 // @factory L.point(x: Number, y: Number, round?: Boolean) 892 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values. 893 894 // @alternative 895 // @factory L.point(coords: Number[]) 896 // Expects an array of the form `[x, y]` instead. 897 898 // @alternative 899 // @factory L.point(coords: Object) 900 // Expects a plain object of the form `{x: Number, y: Number}` instead. 901 function toPoint(x, y, round) { 902 if (x instanceof Point) { 903 return x; 904 } 905 if (isArray(x)) { 906 return new Point(x[0], x[1]); 907 } 908 if (x === undefined || x === null) { 909 return x; 910 } 911 if (typeof x === 'object' && 'x' in x && 'y' in x) { 912 return new Point(x.x, x.y); 913 } 914 return new Point(x, y, round); 915 } 916 917 /* 918 * @class Bounds 919 * @aka L.Bounds 920 * 921 * Represents a rectangular area in pixel coordinates. 922 * 923 * @example 924 * 925 * ```js 926 * var p1 = L.point(10, 10), 927 * p2 = L.point(40, 60), 928 * bounds = L.bounds(p1, p2); 929 * ``` 930 * 931 * 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: 932 * 933 * ```js 934 * otherBounds.intersects([[10, 10], [40, 60]]); 935 * ``` 936 * 937 * Note that `Bounds` does not inherit from Leaflet's `Class` object, 938 * which means new classes can't inherit from it, and new methods 939 * can't be added to it with the `include` function. 940 */ 941 942 function Bounds(a, b) { 943 if (!a) { return; } 944 945 var points = b ? [a, b] : a; 946 947 for (var i = 0, len = points.length; i < len; i++) { 948 this.extend(points[i]); 949 } 950 } 951 952 Bounds.prototype = { 953 // @method extend(point: Point): this 954 // Extends the bounds to contain the given point.
vendor: 3,646 bytes, lines 955-1073
955 extend: function (point) { // (Point) 956 point = toPoint(point); 957 958 // @property min: Point 959 // The top left corner of the rectangle. 960 // @property max: Point 961 // The bottom right corner of the rectangle. 962 if (!this.min && !this.max) { 963 this.min = point.clone(); 964 this.max = point.clone(); 965 } else { 966 this.min.x = Math.min(point.x, this.min.x); 967 this.max.x = Math.max(point.x, this.max.x); 968 this.min.y = Math.min(point.y, this.min.y); 969 this.max.y = Math.max(point.y, this.max.y); 970 } 971 return this; 972 }, 973 974 // @method getCenter(round?: Boolean): Point 975 // Returns the center point of the bounds. 976 getCenter: function (round) { 977 return new Point( 978 (this.min.x + this.max.x) / 2, 979 (this.min.y + this.max.y) / 2, round); 980 }, 981 982 // @method getBottomLeft(): Point 983 // Returns the bottom-left point of the bounds. 984 getBottomLeft: function () { 985 return new Point(this.min.x, this.max.y); 986 }, 987 988 // @method getTopRight(): Point 989 // Returns the top-right point of the bounds. 990 getTopRight: function () { // -> Point 991 return new Point(this.max.x, this.min.y); 992 }, 993 994 // @method getTopLeft(): Point 995 // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)). 996 getTopLeft: function () { 997 return this.min; // left, top 998 }, 999 1000 // @method getBottomRight(): Point 1001 // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)). 1002 getBottomRight: function () { 1003 return this.max; // right, bottom 1004 }, 1005 1006 // @method getSize(): Point 1007 // Returns the size of the given bounds 1008 getSize: function () { 1009 return this.max.subtract(this.min); 1010 }, 1011 1012 // @method contains(otherBounds: Bounds): Boolean 1013 // Returns `true` if the rectangle contains the given one. 1014 // @alternative 1015 // @method contains(point: Point): Boolean 1016 // Returns `true` if the rectangle contains the given point. 1017 contains: function (obj) { 1018 var min, max; 1019 1020 if (typeof obj[0] === 'number' || obj instanceof Point) { 1021 obj = toPoint(obj); 1022 } else { 1023 obj = toBounds(obj); 1024 } 1025 1026 if (obj instanceof Bounds) { 1027 min = obj.min; 1028 max = obj.max; 1029 } else { 1030 min = max = obj; 1031 } 1032 1033 return (min.x >= this.min.x) && 1034 (max.x <= this.max.x) && 1035 (min.y >= this.min.y) && 1036 (max.y <= this.max.y); 1037 }, 1038 1039 // @method intersects(otherBounds: Bounds): Boolean 1040 // Returns `true` if the rectangle intersects the given bounds. Two bounds 1041 // intersect if they have at least one point in common. 1042 intersects: function (bounds) { // (Bounds) -> Boolean 1043 bounds = toBounds(bounds); 1044 1045 var min = this.min, 1046 max = this.max, 1047 min2 = bounds.min, 1048 max2 = bounds.max, 1049 xIntersects = (max2.x >= min.x) && (min2.x <= max.x), 1050 yIntersects = (max2.y >= min.y) && (min2.y <= max.y); 1051 1052 return xIntersects && yIntersects; 1053 }, 1054 1055 // @method overlaps(otherBounds: Bounds): Boolean 1056 // Returns `true` if the rectangle overlaps the given bounds. Two bounds 1057 // overlap if their intersection is an area. 1058 overlaps: function (bounds) { // (Bounds) -> Boolean 1059 bounds = toBounds(bounds); 1060 1061 var min = this.min, 1062 max = this.max, 1063 min2 = bounds.min, 1064 max2 = bounds.max, 1065 xOverlaps = (max2.x > min.x) && (min2.x < max.x), 1066 yOverlaps = (max2.y > min.y) && (min2.y < max.y); 1067 1068 return xOverlaps && yOverlaps; 1069 }, 1070 1071 isValid: function () { 1072 return !!(this.min && this.max); 1073 }
vendor: 13,363 bytes, lines 1073-1479
1073 1074 }; 1075 1076 1077 // @factory L.bounds(corner1: Point, corner2: Point) 1078 // Creates a Bounds object from two corners coordinate pairs. 1079 // @alternative 1080 // @factory L.bounds(points: Point[]) 1081 // Creates a Bounds object from the given array of points. 1082 function toBounds(a, b) { 1083 if (!a || a instanceof Bounds) { 1084 return a; 1085 } 1086 return new Bounds(a, b); 1087 } 1088 1089 /* 1090 * @class LatLngBounds 1091 * @aka L.LatLngBounds 1092 * 1093 * Represents a rectangular geographical area on a map. 1094 * 1095 * @example 1096 * 1097 * ```js 1098 * var corner1 = L.latLng(40.712, -74.227), 1099 * corner2 = L.latLng(40.774, -74.125), 1100 * bounds = L.latLngBounds(corner1, corner2); 1101 * ``` 1102 * 1103 * 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: 1104 * 1105 * ```js 1106 * map.fitBounds([ 1107 * [40.712, -74.227], 1108 * [40.774, -74.125] 1109 * ]); 1110 * ``` 1111 * 1112 * 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. 1113 * 1114 * Note that `LatLngBounds` does not inherit from Leaflet's `Class` object, 1115 * which means new classes can't inherit from it, and new methods 1116 * can't be added to it with the `include` function. 1117 */ 1118 1119 function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[]) 1120 if (!corner1) { return; } 1121 1122 var latlngs = corner2 ? [corner1, corner2] : corner1; 1123 1124 for (var i = 0, len = latlngs.length; i < len; i++) { 1125 this.extend(latlngs[i]); 1126 } 1127 } 1128 1129 LatLngBounds.prototype = { 1130 1131 // @method extend(latlng: LatLng): this 1132 // Extend the bounds to contain the given point 1133 1134 // @alternative 1135 // @method extend(otherBounds: LatLngBounds): this 1136 // Extend the bounds to contain the given bounds 1137 extend: function (obj) { 1138 var sw = this._southWest, 1139 ne = this._northEast, 1140 sw2, ne2; 1141 1142 if (obj instanceof LatLng) { 1143 sw2 = obj; 1144 ne2 = obj; 1145 1146 } else if (obj instanceof LatLngBounds) { 1147 sw2 = obj._southWest; 1148 ne2 = obj._northEast; 1149 1150 if (!sw2 || !ne2) { return this; } 1151 1152 } else { 1153 return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this; 1154 } 1155 1156 if (!sw && !ne) { 1157 this._southWest = new LatLng(sw2.lat, sw2.lng); 1158 this._northEast = new LatLng(ne2.lat, ne2.lng); 1159 } else { 1160 sw.lat = Math.min(sw2.lat, sw.lat); 1161 sw.lng = Math.min(sw2.lng, sw.lng); 1162 ne.lat = Math.max(ne2.lat, ne.lat); 1163 ne.lng = Math.max(ne2.lng, ne.lng); 1164 } 1165 1166 return this; 1167 }, 1168 1169 // @method pad(bufferRatio: Number): LatLngBounds 1170 // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction. 1171 // For example, a ratio of 0.5 extends the bounds by 50% in each direction. 1172 // Negative values will retract the bounds. 1173 pad: function (bufferRatio) { 1174 var sw = this._southWest, 1175 ne = this._northEast, 1176 heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, 1177 widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; 1178 1179 return new LatLngBounds( 1180 new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), 1181 new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); 1182 }, 1183 1184 // @method getCenter(): LatLng 1185 // Returns the center point of the bounds. 1186 getCenter: function () { 1187 return new LatLng( 1188 (this._southWest.lat + this._northEast.lat) / 2, 1189 (this._southWest.lng + this._northEast.lng) / 2); 1190 }, 1191 1192 // @method getSouthWest(): LatLng 1193 // Returns the south-west point of the bounds. 1194 getSouthWest: function () { 1195 return this._southWest; 1196 }, 1197 1198 // @method getNorthEast(): LatLng 1199 // Returns the north-east point of the bounds. 1200 getNorthEast: function () { 1201 return this._northEast; 1202 }, 1203 1204 // @method getNorthWest(): LatLng 1205 // Returns the north-west point of the bounds. 1206 getNorthWest: function () { 1207 return new LatLng(this.getNorth(), this.getWest()); 1208 }, 1209 1210 // @method getSouthEast(): LatLng 1211 // Returns the south-east point of the bounds. 1212 getSouthEast: function () { 1213 return new LatLng(this.getSouth(), this.getEast()); 1214 }, 1215 1216 // @method getWest(): Number 1217 // Returns the west longitude of the bounds 1218 getWest: function () { 1219 return this._southWest.lng; 1220 }, 1221 1222 // @method getSouth(): Number 1223 // Returns the south latitude of the bounds 1224 getSouth: function () { 1225 return this._southWest.lat; 1226 }, 1227 1228 // @method getEast(): Number 1229 // Returns the east longitude of the bounds 1230 getEast: function () { 1231 return this._northEast.lng; 1232 }, 1233 1234 // @method getNorth(): Number 1235 // Returns the north latitude of the bounds 1236 getNorth: function () { 1237 return this._northEast.lat; 1238 }, 1239 1240 // @method contains(otherBounds: LatLngBounds): Boolean 1241 // Returns `true` if the rectangle contains the given one. 1242 1243 // @alternative 1244 // @method contains (latlng: LatLng): Boolean 1245 // Returns `true` if the rectangle contains the given point. 1246 contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean 1247 if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) { 1248 obj = toLatLng(obj); 1249 } else { 1250 obj = toLatLngBounds(obj); 1251 } 1252 1253 var sw = this._southWest, 1254 ne = this._northEast, 1255 sw2, ne2; 1256 1257 if (obj instanceof LatLngBounds) { 1258 sw2 = obj.getSouthWest(); 1259 ne2 = obj.getNorthEast(); 1260 } else { 1261 sw2 = ne2 = obj; 1262 } 1263 1264 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && 1265 (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); 1266 }, 1267 1268 // @method intersects(otherBounds: LatLngBounds): Boolean 1269 // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common. 1270 intersects: function (bounds) { 1271 bounds = toLatLngBounds(bounds); 1272 1273 var sw = this._southWest, 1274 ne = this._northEast, 1275 sw2 = bounds.getSouthWest(), 1276 ne2 = bounds.getNorthEast(), 1277 1278 latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), 1279 lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); 1280 1281 return latIntersects && lngIntersects; 1282 }, 1283 1284 // @method overlaps(otherBounds: LatLngBounds): Boolean 1285 // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area. 1286 overlaps: function (bounds) { 1287 bounds = toLatLngBounds(bounds); 1288 1289 var sw = this._southWest, 1290 ne = this._northEast, 1291 sw2 = bounds.getSouthWest(), 1292 ne2 = bounds.getNorthEast(), 1293 1294 latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat), 1295 lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng); 1296 1297 return latOverlaps && lngOverlaps; 1298 }, 1299 1300 // @method toBBoxString(): String 1301 // 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. 1302 toBBoxString: function () { 1303 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(','); 1304 }, 1305 1306 // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean 1307 // 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. 1308 equals: function (bounds, maxMargin) { 1309 if (!bounds) { return false; } 1310 1311 bounds = toLatLngBounds(bounds); 1312 1313 return this._southWest.equals(bounds.getSouthWest(), maxMargin) && 1314 this._northEast.equals(bounds.getNorthEast(), maxMargin); 1315 }, 1316 1317 // @method isValid(): Boolean 1318 // Returns `true` if the bounds are properly initialized. 1319 isValid: function () { 1320 return !!(this._southWest && this._northEast); 1321 } 1322 }; 1323 1324 // TODO International date line? 1325 1326 // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng) 1327 // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle. 1328 1329 // @alternative 1330 // @factory L.latLngBounds(latlngs: LatLng[]) 1331 // 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). 1332 function toLatLngBounds(a, b) { 1333 if (a instanceof LatLngBounds) { 1334 return a; 1335 } 1336 return new LatLngBounds(a, b); 1337 } 1338 1339 /* @class LatLng 1340 * @aka L.LatLng 1341 * 1342 * Represents a geographical point with a certain latitude and longitude. 1343 * 1344 * @example 1345 * 1346 * ``` 1347 * var latlng = L.latLng(50.5, 30.5); 1348 * ``` 1349 * 1350 * 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: 1351 * 1352 * ``` 1353 * map.panTo([50, 30]); 1354 * map.panTo({lon: 30, lat: 50}); 1355 * map.panTo({lat: 50, lng: 30}); 1356 * map.panTo(L.latLng(50, 30)); 1357 * ``` 1358 * 1359 * Note that `LatLng` does not inherit from Leaflet's `Class` object, 1360 * which means new classes can't inherit from it, and new methods 1361 * can't be added to it with the `include` function. 1362 */ 1363 1364 function LatLng(lat, lng, alt) { 1365 if (isNaN(lat) || isNaN(lng)) { 1366 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')'); 1367 } 1368 1369 // @property lat: Number 1370 // Latitude in degrees 1371 this.lat = +lat; 1372 1373 // @property lng: Number 1374 // Longitude in degrees 1375 this.lng = +lng; 1376 1377 // @property alt: Number 1378 // Altitude in meters (optional) 1379 if (alt !== undefined) { 1380 this.alt = +alt; 1381 } 1382 } 1383 1384 LatLng.prototype = { 1385 // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean 1386 // 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. 1387 equals: function (obj, maxMargin) { 1388 if (!obj) { return false; } 1389 1390 obj = toLatLng(obj); 1391 1392 var margin = Math.max( 1393 Math.abs(this.lat - obj.lat), 1394 Math.abs(this.lng - obj.lng)); 1395 1396 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin); 1397 }, 1398 1399 // @method toString(): String 1400 // Returns a string representation of the point (for debugging purposes). 1401 toString: function (precision) { 1402 return 'LatLng(' + 1403 formatNum(this.lat, precision) + ', ' + 1404 formatNum(this.lng, precision) + ')'; 1405 }, 1406 1407 // @method distanceTo(otherLatLng: LatLng): Number 1408 // 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). 1409 distanceTo: function (other) { 1410 return Earth.distance(this, toLatLng(other)); 1411 }, 1412 1413 // @method wrap(): LatLng 1414 // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees. 1415 wrap: function () { 1416 return Earth.wrapLatLng(this); 1417 }, 1418 1419 // @method toBounds(sizeInMeters: Number): LatLngBounds 1420 // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`. 1421 toBounds: function (sizeInMeters) { 1422 var latAccuracy = 180 * sizeInMeters / 40075017, 1423 lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat); 1424 1425 return toLatLngBounds( 1426 [this.lat - latAccuracy, this.lng - lngAccuracy], 1427 [this.lat + latAccuracy, this.lng + lngAccuracy]); 1428 }, 1429 1430 clone: function () { 1431 return new LatLng(this.lat, this.lng, this.alt); 1432 } 1433 }; 1434 1435 1436 1437 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng 1438 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude). 1439 1440 // @alternative 1441 // @factory L.latLng(coords: Array): LatLng 1442 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead. 1443 1444 // @alternative 1445 // @factory L.latLng(coords: Object): LatLng 1446 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead. 1447 1448 function toLatLng(a, b, c) { 1449 if (a instanceof LatLng) { 1450 return a; 1451 } 1452 if (isArray(a) && typeof a[0] !== 'object') { 1453 if (a.length === 3) { 1454 return new LatLng(a[0], a[1], a[2]); 1455 } 1456 if (a.length === 2) { 1457 return new LatLng(a[0], a[1]); 1458 } 1459 return null; 1460 } 1461 if (a === undefined || a === null) { 1462 return a; 1463 } 1464 if (typeof a === 'object' && 'lat' in a) { 1465 return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt); 1466 } 1467 if (b === undefined) { 1468 return null; 1469 } 1470 return new LatLng(a, b, c); 1471 } 1472 1473 /* 1474 * @namespace CRS 1475 * @crs L.CRS.Base 1476 * Object that defines coordinate reference systems for projecting 1477 * geographical points into pixel (screen) coordinates and back (and to 1478 * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See 1479 * [spatial reference system](http
1479://en.wikipedia.org/wiki/Coordinate_reference_system). 1480 * 1481 * Leaflet defines the most usual CRSs by default. If you want to use a 1482 * CRS not defined by default, take a look at the 1483 * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin. 1484 * 1485 * Note that the CRS instances do not inherit from Leaflet's `Class` object, 1486 * and can't be instantiated. Also, new classes can't inherit from them, 1487 * and methods can't be added to them with the `include` function. 1488 */ 1489 1490 var CRS = { 1491 // @method latLngToPoint(latlng: LatLng, zoom: Number): Point 1492 // Projects geographical coordinates into pixel coordinates for a given zoom. 1493 latLngToPoint: function (latlng, zoom) { 1494 var projectedPoint = this.projection.project(latlng), 1495 scale = this.scale(zoom); 1496 1497 return this.transformation._transform(projectedPoint, scale); 1498 }, 1499 1500 // @method pointToLatLng(point: Point, zoom: Number): LatLng 1501 // The inverse of `latLngToPoint`. Projects pixel coordinates on a given 1502 // zoom into geographical coordinates. 1503 pointToLatLng: function (point, zoom) { 1504 var scale = this.scale(zoom), 1505 untransformedPoint = this.transformation.untransform(point, scale); 1506 1507 return this.projection.unproject(untransformedPoint); 1508 }, 1509 1510 // @method project(latlng: LatLng): Point 1511 // Projects geographical coordinates into coordinates in units accepted for 1512 // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services). 1513 project: function (latlng) { 1514 return this.projection.project(latlng); 1515 }, 1516 1517 // @method unproject(point: Point): LatLng 1518 // Given a projected coordinate returns the corresponding LatLng. 1519 // The inverse of `project`. 1520 unproject: function (point) { 1521 return this.projection.unproject(point); 1522 }, 1523 1524 // @method scale(zoom: Number): Number 1525 // Returns the scale used when transforming projected coordinates into 1526 // pixel coordinates for a particular zoom. For example, it returns 1527 // `256 * 2^zoom` for Mercator-based CRS. 1528 scale: function (zoom) { 1529 return 256 * Math.pow(2, zoom); 1530 }, 1531 1532 // @method zoom(scale: Number): Number 1533 // Inverse of `scale()`, returns the zoom level corresponding to a scale 1534 // factor of `scale`. 1535 zoom: function (scale) { 1536 return Math.log(scale / 256) / Math.LN2; 1537 }, 1538 1539 // @method getProjectedBounds(zoom: Number): Bounds 1540 // Returns the projection's bounds scaled and transformed for the provided `zoom`. 1541 getProjectedBounds: function (zoom) { 1542 if (this.infinite) { return null; } 1543 1544 var b = this.projection.bounds, 1545 s = this.scale(zoom), 1546 min = this.transformation.transform(b.min, s), 1547 max = this.transformation.transform(b.max, s); 1548 1549 return new Bounds(min, max); 1550 }, 1551 1552 // @method distance(latlng1: LatLng, latlng2: LatLng): Number 1553 // Returns the distance between two geographical coordinates. 1554 1555 // @property code: String 1556 // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`) 1557 // 1558 // @property wrapLng: Number[] 1559 // An array of two numbers defining whether the longitude (horizontal) coordinate 1560 // axis wraps around a given range and how. Defaults to `[-180, 180]` in most 1561 // geographical CRSs. If `undefined`, the longitude axis does not wrap around. 1562 // 1563 // @property wrapLat: Number[] 1564 // Like `wrapLng`, but for the latitude (vertical) axis. 1565 1566 // wrapLng: [min, max], 1567 // wrapLat: [min, max], 1568 1569 // @property infinite: Boolean 1570 // If true, the coordinate space will be unbounded (infinite in both axes) 1571 infinite: false, 1572 1573 // @method wrapLatLng(latlng: LatLng): LatLng 1574 // Returns a `LatLng` where lat and lng has been wrapped according to the 1575 // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds. 1576 wrapLatLng: function (latlng) { 1577 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng, 1578 lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat, 1579 alt = latlng.alt; 1580
vendor: 14,570 bytes, lines 1581-1998
1581 return new LatLng(lat, lng, alt); 1582 }, 1583 1584 // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds 1585 // Returns a `LatLngBounds` with the same size as the given one, ensuring 1586 // that its center is within the CRS's bounds. 1587 // Only accepts actual `L.LatLngBounds` instances, not arrays. 1588 wrapLatLngBounds: function (bounds) { 1589 var center = bounds.getCenter(), 1590 newCenter = this.wrapLatLng(center), 1591 latShift = center.lat - newCenter.lat, 1592 lngShift = center.lng - newCenter.lng; 1593 1594 if (latShift === 0 && lngShift === 0) { 1595 return bounds; 1596 } 1597 1598 var sw = bounds.getSouthWest(), 1599 ne = bounds.getNorthEast(), 1600 newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift), 1601 newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift); 1602 1603 return new LatLngBounds(newSw, newNe); 1604 } 1605 }; 1606 1607 /* 1608 * @namespace CRS 1609 * @crs L.CRS.Earth 1610 * 1611 * Serves as the base for CRS that are global such that they cover the earth. 1612 * Can only be used as the base for other CRS and cannot be used directly, 1613 * since it does not have a `code`, `projection` or `transformation`. `distance()` returns 1614 * meters. 1615 */ 1616 1617 var Earth = extend({}, CRS, { 1618 wrapLng: [-180, 180], 1619 1620 // Mean Earth Radius, as recommended for use by 1621 // the International Union of Geodesy and Geophysics, 1622 // see http://rosettacode.org/wiki/Haversine_formula 1623 R: 6371000, 1624 1625 // distance between two geographical points using spherical law of cosines approximation 1626 distance: function (latlng1, latlng2) { 1627 var rad = Math.PI / 180, 1628 lat1 = latlng1.lat * rad, 1629 lat2 = latlng2.lat * rad, 1630 sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2), 1631 sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2), 1632 a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon, 1633 c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 1634 return this.R * c; 1635 } 1636 }); 1637 1638 /* 1639 * @namespace Projection 1640 * @projection L.Projection.SphericalMercator 1641 * 1642 * Spherical Mercator projection â the most common projection for online maps, 1643 * used by almost all free and commercial tile providers. Assumes that Earth is 1644 * a sphere. Used by the `EPSG:3857` CRS. 1645 */ 1646 1647 var earthRadius = 6378137; 1648 1649 var SphericalMercator = { 1650 1651 R: earthRadius, 1652 MAX_LATITUDE: 85.0511287798, 1653 1654 project: function (latlng) { 1655 var d = Math.PI / 180, 1656 max = this.MAX_LATITUDE, 1657 lat = Math.max(Math.min(max, latlng.lat), -max), 1658 sin = Math.sin(lat * d); 1659 1660 return new Point( 1661 this.R * latlng.lng * d, 1662 this.R * Math.log((1 + sin) / (1 - sin)) / 2); 1663 }, 1664 1665 unproject: function (point) { 1666 var d = 180 / Math.PI; 1667 1668 return new LatLng( 1669 (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d, 1670 point.x * d / this.R); 1671 }, 1672 1673 bounds: (function () { 1674 var d = earthRadius * Math.PI; 1675 return new Bounds([-d, -d], [d, d]); 1676 })() 1677 }; 1678 1679 /* 1680 * @class Transformation 1681 * @aka L.Transformation 1682 * 1683 * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d` 1684 * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing 1685 * the reverse. Used by Leaflet in its projections code. 1686 * 1687 * @example 1688 * 1689 * ```js 1690 * var transformation = L.transformation(2, 5, -1, 10), 1691 * p = L.point(1, 2), 1692 * p2 = transformation.transform(p), // L.point(7, 8) 1693 * p3 = transformation.untransform(p2); // L.point(1, 2) 1694 * ``` 1695 */ 1696 1697 1698 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number) 1699 // Creates a `Transformation` object with the given coefficients. 1700 function Transformation(a, b, c, d) { 1701 if (isArray(a)) { 1702 // use array properties 1703 this._a = a[0]; 1704 this._b = a[1]; 1705 this._c = a[2]; 1706 this._d = a[3]; 1707 return; 1708 } 1709 this._a = a; 1710 this._b = b; 1711 this._c = c; 1712 this._d = d; 1713 } 1714 1715 Transformation.prototype = { 1716 // @method transform(point: Point, scale?: Number): Point 1717 // Returns a transformed point, optionally multiplied by the given scale. 1718 // Only accepts actual `L.Point` instances, not arrays. 1719 transform: function (point, scale) { // (Point, Number) -> Point 1720 return this._transform(point.clone(), scale); 1721 }, 1722 1723 // destructive transform (faster) 1724 _transform: function (point, scale) { 1725 scale = scale || 1; 1726 point.x = scale * (this._a * point.x + this._b); 1727 point.y = scale * (this._c * point.y + this._d); 1728 return point; 1729 }, 1730 1731 // @method untransform(point: Point, scale?: Number): Point 1732 // Returns the reverse transformation of the given point, optionally divided 1733 // by the given scale. Only accepts actual `L.Point` instances, not arrays. 1734 untransform: function (point, scale) { 1735 scale = scale || 1; 1736 return new Point( 1737 (point.x / scale - this._b) / this._a, 1738 (point.y / scale - this._d) / this._c); 1739 } 1740 }; 1741 1742 // factory L.transformation(a: Number, b: Number, c: Number, d: Number) 1743 1744 // @factory L.transformation(a: Number, b: Number, c: Number, d: Number) 1745 // Instantiates a Transformation object with the given coefficients. 1746 1747 // @alternative 1748 // @factory L.transformation(coefficients: Array): Transformation 1749 // Expects an coefficients array of the form 1750 // `[a: Number, b: Number, c: Number, d: Number]`. 1751 1752 function toTransformation(a, b, c, d) { 1753 return new Transformation(a, b, c, d); 1754 } 1755 1756 /* 1757 * @namespace CRS 1758 * @crs L.CRS.EPSG3857 1759 * 1760 * The most common CRS for online maps, used by almost all free and commercial 1761 * tile providers. Uses Spherical Mercator projection. Set in by default in 1762 * Map's `crs` option. 1763 */ 1764 1765 var EPSG3857 = extend({}, Earth, { 1766 code: 'EPSG:3857', 1767 projection: SphericalMercator, 1768 1769 transformation: (function () { 1770 var scale = 0.5 / (Math.PI * SphericalMercator.R); 1771 return toTransformation(scale, 0.5, -scale, 0.5); 1772 }()) 1773 }); 1774 1775 var EPSG900913 = extend({}, EPSG3857, { 1776 code: 'EPSG:900913' 1777 }); 1778 1779 // @namespace SVG; @section 1780 // There are several static functions which can be called without instantiating L.SVG: 1781 1782 // @function create(name: String): SVGElement 1783 // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement), 1784 // corresponding to the class name passed. For example, using 'line' will return 1785 // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement). 1786 function svgCreate(name) { 1787 return document.createElementNS('http://www.w3.org/2000/svg', name); 1788 } 1789 1790 // @function pointsToPath(rings: Point[], closed: Boolean): String 1791 // Generates a SVG path string for multiple rings, with each ring turning 1792 // into "M..L..L.." instructions 1793 function pointsToPath(rings, closed) { 1794 var str = '', 1795 i, j, len, len2, points, p; 1796 1797 for (i = 0, len = rings.length; i < len; i++) { 1798 points = rings[i]; 1799 1800 for (j = 0, len2 = points.length; j < len2; j++) { 1801 p = points[j]; 1802 str += (j ? 'L' : 'M') + p.x + ' ' + p.y; 1803 } 1804 1805 // closes the ring for polygons; "x" is VML syntax 1806 str += closed ? (svg$1 ? 'z' : 'x') : ''; 1807 } 1808 1809 // SVG complains about empty path strings 1810 return str || 'M0 0'; 1811 } 1812 1813 /* 1814 * @namespace Browser 1815 * @aka L.Browser 1816 * 1817 * A namespace with static properties for browser/feature detection used by Leaflet internally. 1818 * 1819 * @example 1820 * 1821 * ```js 1822 * if (L.Browser.ielt9) { 1823 * alert('Upgrade your browser, dude!'); 1824 * } 1825 * ``` 1826 */ 1827 1828 var style = document.documentElement.style; 1829 1830 // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge). 1831 var ie = 'ActiveXObject' in window; 1832 1833 // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9. 1834 var ielt9 = ie && !document.addEventListener; 1835 1836 // @property edge: Boolean; `true` for the Edge web browser. 1837 var edge = 'msLaunchUri' in navigator && !('documentMode' in document); 1838 1839 // @property webkit: Boolean; 1840 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions). 1841 var webkit = userAgentContains('webkit'); 1842 1843 // @property android: Boolean 1844 // `true` for any browser running on an Android platform. 1845 var android = userAgentContains('android'); 1846 1847 // @property android23: Boolean; `true` for browsers running on Android 2 or Android 3. 1848 var android23 = userAgentContains('android 2') || userAgentContains('android 3'); 1849 1850 /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */ 1851 var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit 1852 // @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome) 1853 var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window); 1854 1855 // @property opera: Boolean; `true` for the Opera browser 1856 var opera = !!window.opera; 1857 1858 // @property chrome: Boolean; `true` for the Chrome browser. 1859 var chrome = !edge && userAgentContains('chrome'); 1860 1861 // @property gecko: Boolean; `true` for gecko-based browsers like Firefox. 1862 var gecko = userAgentContains('gecko') && !webkit && !opera && !ie; 1863 1864 // @property safari: Boolean; `true` for the Safari browser. 1865 var safari = !chrome && userAgentContains('safari'); 1866 1867 var phantom = userAgentContains('phantom'); 1868 1869 // @property opera12: Boolean 1870 // `true` for the Opera browser supporting CSS transforms (version 12 or later). 1871 var opera12 = 'OTransition' in style; 1872 1873 // @property win: Boolean; `true` when the browser is running in a Windows platform 1874 var win = navigator.platform.indexOf('Win') === 0; 1875 1876 // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms. 1877 var ie3d = ie && ('transition' in style); 1878 1879 // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms. 1880 var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23; 1881 1882 // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms. 1883 var gecko3d = 'MozPerspective' in style; 1884 1885 // @property any3d: Boolean 1886 // `true` for all browsers supporting CSS transforms. 1887 var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom; 1888 1889 // @property mobile: Boolean; `true` for all browsers running in a mobile device. 1890 var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile'); 1891 1892 // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device. 1893 var mobileWebkit = mobile && webkit; 1894 1895 // @property mobileWebkit3d: Boolean 1896 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms. 1897 var mobileWebkit3d = mobile && webkit3d; 1898 1899 // @property msPointer: Boolean 1900 // `true` for browsers implementing the Microsoft touch events model (notably IE10). 1901 var msPointer = !window.PointerEvent && window.MSPointerEvent; 1902 1903 // @property pointer: Boolean 1904 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx). 1905 var pointer = !!(window.PointerEvent || msPointer); 1906 1907 // @property touch: Boolean 1908 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events). 1909 // This does not necessarily mean that the browser is running in a computer with 1910 // a touchscreen, it only means that the browser is capable of understanding 1911 // touch events. 1912 var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window || 1913 (window.DocumentTouch && document instanceof window.DocumentTouch)); 1914 1915 // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device. 1916 var mobileOpera = mobile && opera; 1917 1918 // @property mobileGecko: Boolean 1919 // `true` for gecko-based browsers running in a mobile device. 1920 var mobileGecko = mobile && gecko; 1921 1922 // @property retina: Boolean 1923 // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%. 1924 var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1; 1925 1926 // @property passiveEvents: Boolean 1927 // `true` for browsers that support passive events. 1928 var passiveEvents = (function () { 1929 var supportsPassiveOption = false; 1930 try { 1931 var opts = Object.defineProperty({}, 'passive', { 1932 get: function () { // eslint-disable-line getter-return 1933 supportsPassiveOption = true; 1934 } 1935 }); 1936 window.addEventListener('testPassiveEventSupport', falseFn, opts); 1937 window.removeEventListener('testPassiveEventSupport', falseFn, opts); 1938 } catch (e) { 1939 // Errors can safely be ignored since this is only a browser support test. 1940 } 1941 return supportsPassiveOption; 1942 }()); 1943 1944 // @property canvas: Boolean 1945 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API). 1946 var canvas$1 = (function () { 1947 return !!document.createElement('canvas').getContext; 1948 }()); 1949 1950 // @property svg: Boolean 1951 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG). 1952 var svg$1 = !!(document.createElementNS && svgCreate('svg').createSVGRect); 1953 1954 // @property vml: Boolean 1955 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language). 1956 var vml = !svg$1 && (function () { 1957 try { 1958 var div = document.createElement('div'); 1959 div.innerHTML = '<v:shape adj="1"/>'; 1960 1961 var shape = div.firstChild; 1962 shape.style.behavior = 'url(#default#VML)'; 1963 1964 return shape && (typeof shape.adj === 'object'); 1965 1966 } catch (e) { 1967 return false; 1968 } 1969 }()); 1970 1971 1972 function userAgentContains(str) { 1973 return navigator.userAgent.toLowerCase().indexOf(str) >= 0; 1974 } 1975 1976 var Browser = { 1977 __proto__: null, 1978 ie: ie, 1979 ielt9: ielt9, 1980 edge: edge, 1981 webkit: webkit, 1982 android: android, 1983 android23: android23, 1984 androidStock: androidStock, 1985 opera: opera, 1986 chrome: chrome, 1987 gecko: gecko, 1988 safari: safari, 1989 phantom: phantom, 1990 opera12: opera12, 1991 win: win, 1992 ie3d: ie3d, 1993 webkit3d: webkit3d, 1994 gecko3d: gecko3d, 1995 any3d: any3d, 1996 mobile: mobile, 1997 mobileWebkit: mobileWebkit, 1998 mobileWebkit3d: mobileWebkit3d,
1999 msPointer: msPointer, 2000 pointer: pointer, 2001 touch: touch, 2002 mobileOpera: mobileOpera, 2003 mobileGecko: mobileGecko, 2004 retina: retina, 2005 passiveEvents: passiveEvents, 2006 canvas: canvas$1, 2007 svg: svg$1, 2008 vml: vml 2009 }; 2010 2011 /* 2012 * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. 2013 */ 2014 2015 2016 var POINTER_DOWN = msPointer ? 'MSPointerDown' : 'pointerdown'; 2017 var POINTER_MOVE = msPointer ? 'MSPointerMove' : 'pointermove'; 2018 var POINTER_UP = msPointer ? 'MSPointerUp' : 'pointerup'; 2019 var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel'; 2020 2021 var _pointers = {}; 2022 var _pointerDocListener = false; 2023 2024 // Provides a touch events wrapper for (ms)pointer events. 2025 // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 2026 2027 function addPointerListener(obj, type, handler, id) { 2028 if (type === 'touchstart') { 2029 _addPointerStart(obj, handler, id); 2030 2031 } else if (type === 'touchmove') { 2032 _addPointerMove(obj, handler, id); 2033 2034 } else if (type === 'touchend') { 2035 _addPointerEnd(obj, handler, id); 2036 } 2037 2038 return this; 2039 } 2040 2041 function removePointerListener(obj, type, id) { 2042 var handler = obj['_leaflet_' + type + id]; 2043 2044 if (type === 'touchstart') { 2045 obj.removeEventListener(POINTER_DOWN, handler, false); 2046 2047 } else if (type === 'touchmove') { 2048 obj.removeEventListener(POINTER_MOVE, handler, false); 2049 2050 } else if (type === 'touchend') { 2051 obj.removeEventListener(POINTER_UP, handler, false); 2052 obj.removeEventListener(POINTER_CANCEL, handler, false); 2053 } 2054 2055 return this; 2056 } 2057 2058 function _addPointerStart(obj, handler, id) { 2059 var onDown = bind(function (e) { 2060 // IE10 specific: MsTouch needs preventDefault. See #2000 2061 if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) { 2062 preventDefault(e); 2063 } 2064 2065 _handlePointer(e, handler); 2066 }); 2067 2068 obj['_leaflet_touchstart' + id] = onDown; 2069 obj.addEventListener(POINTER_DOWN, onDown, false); 2070 2071 // need to keep track of what pointers and how many are active to provide e.touches emulation 2072 if (!_pointerDocListener) { 2073 // we listen document as any drags that end by moving the touch off the screen get fired there 2074 document.addEventListener(POINTER_DOWN, _globalPointerDown, true); 2075 document.addEventListener(POINTER_MOVE, _globalPointerMove, true); 2076 document.addEventListener(POINTER_UP, _globalPointerUp, true); 2077 document.addEventListener(POINTER_CANCEL, _globalPointerUp, true); 2078 2079 _pointerDocListener = true; 2080 } 2081 } 2082 2083 function _globalPointerDown(e) { 2084 _pointers[e.pointerId] = e; 2085 } 2086 2087 function _globalPointerMove(e) { 2088 if (_pointers[e.pointerId]) { 2089 _pointers[e.pointerId] = e; 2090 } 2091 } 2092 2093 function _globalPointerUp(e) { 2094 delete _pointers[e.pointerId]; 2095 } 2096 2097 function _handlePointer(e, handler) { 2098 e.touches = []; 2099 for (var i in _pointers) { 2100 e.touches.push(_pointers[i]); 2101 } 2102 e.changedTouches = [e]; 2103 2104 handler(e); 2105 } 2106 2107 function _addPointerMove(obj, handler, id) { 2108 var onMove = function (e) { 2109 // don't fire touch moves when mouse isn't down 2110 if ((e.pointerType === (e.MSPOINTER_TYPE_MOUSE || 'mouse')) && e.buttons === 0) { 2111 return; 2112 } 2113 2114 _handlePointer(e, handler); 2115 }; 2116 2117 obj['_leaflet_touchmove' + id] = onMove; 2118 obj.addEventListener(POINTER_MOVE, onMove, false); 2119 } 2120 2121 function _addPointerEnd(obj, handler, id) { 2122 var onUp = function (e) { 2123 _handlePointer(e, handler); 2124 }; 2125 2126 obj['_leaflet_touchend' + id] = onUp; 2127 obj.addEventListener(POINTER_UP, onUp, false); 2128 obj.addEventListener(POINTER_CANCEL, onUp, false); 2129 } 2130 2131 /* 2132 * Extends the event handling code with double tap support for mobile browsers. 2133 */ 2134 2135 var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart'; 2136 var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend'; 2137 var _pre = '_leaflet_'; 2138 2139 // inspired by Zepto touch code by Thomas Fuchs 2140 function addDoubleTapListener(obj, handler, id) { 2141 var last, touch, 2142 doubleTap = false,
2143 delay = 250; 2144 2145 function onTouchStart(e) { 2146 2147 if (pointer) { 2148 if (!e.isPrimary) { return; } 2149 if (e.pointerType === 'mouse') { return; } // mouse fires native dblclick 2150 } else if (e.touches.length > 1) { 2151 return; 2152 } 2153 2154 var now = Date.now(), 2155 delta = now - (last || now); 2156 2157 touch = e.touches ? e.touches[0] : e; 2158 doubleTap = (delta > 0 && delta <= delay); 2159 last = now; 2160 } 2161 2162 function onTouchEnd(e) { 2163 if (doubleTap && !touch.cancelBubble) { 2164 if (pointer) { 2165 if (e.pointerType === 'mouse') { return; } 2166 // work around .type being readonly with MSPointer* events 2167 var newTouch = {}, 2168 prop, i; 2169 2170 for (i in touch) { 2171 prop = touch[i]; 2172 newTouch[i] = prop && prop.bind ? prop.bind(touch) : prop; 2173 } 2174 touch = newTouch; 2175 } 2176 touch.type = 'dblclick'; 2177 touch.button = 0; 2178 handler(touch); 2179 last = null; 2180 } 2181 } 2182 2183 obj[_pre + _touchstart + id] = onTouchStart; 2184 obj[_pre + _touchend + id] = onTouchEnd; 2185 obj[_pre + 'dblclick' + id] = handler; 2186 2187 obj.addEventListener(_touchstart, onTouchStart, passiveEvents ? {passive: false} : false); 2188 obj.addEventListener(_touchend, onTouchEnd, passiveEvents ? {passive: false} : false); 2189 2190 // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse), 2191 // the browser doesn't fire touchend/pointerup events but does fire 2192 // native dblclicks. See #4127. 2193 // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180. 2194 obj.addEventListener('dblclick', handler, false); 2195 2196 return this; 2197 } 2198 2199 function removeDoubleTapListener(obj, id) { 2200 var touchstart = obj[_pre + _touchstart + id], 2201 touchend = obj[_pre + _touchend + id], 2202 dblclick = obj[_pre + 'dblclick' + id]; 2203 2204 obj.removeEventListener(_touchstart, touchstart, passiveEvents ? {passive: false} : false); 2205 obj.removeEventListener(_touchend, touchend, passiveEvents ? {passive: false} : false); 2206 obj.removeEventListener('dblclick', dblclick, false); 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). 2225 var 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. 2233 var TRANSITION = testProp( 2234 ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); 2235 2236 // @property TRANSITION_END: String 2237 // Vendor-prefixed transitionend event name. 2238 var 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. 2245 function 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. 2252 function 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. 2264 function 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 2276 function 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` 2285 function 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. 2293 function 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. 2302 function 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`. 2311 function 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. 2321 function 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. 2335 function 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. 2345 function 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. 2356 function 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`. 2368 function 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 2376 function _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`. 2403 function 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. 2418 function 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`, 2430 // using CSS translate or top/left positioning depending on the browser
vendor: 8,125 bytes, lines 2431-2671
2431 // (used by Leaflet internally to position its layers). 2432 function 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. 2448 function 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). 2463 var disableTextSelection; 2464 var enableTextSelection; 2465 var _userSelect; 2466 if ('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. 2495 function disableImageDrag() { 2496 on(window, 'dragstart', preventDefault); 2497 } 2498 2499 // @function enableImageDrag() 2500 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection). 2501 function enableImageDrag() { 2502 off(window, 'dragstart', preventDefault); 2503 } 2504 2505 var _outlineElement, _outlineStyle; 2506 // @function preventOutline(el: HTMLElement) 2507 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline) 2508 // of the element `el` invisible. Used internally by Leaflet to prevent 2509 // focusable elements from displaying an outline when the user performs a 2510 // drag interaction on them. 2511 function preventOutline(element) { 2512 while (element.tabIndex === -1) { 2513 element = element.parentNode; 2514 } 2515 if (!element.style) { return; } 2516 restoreOutline(); 2517 _outlineElement = element; 2518 _outlineStyle = element.style.outline; 2519 element.style.outline = 'none'; 2520 on(window, 'keydown', restoreOutline); 2521 } 2522 2523 // @function restoreOutline() 2524 // Cancels the effects of a previous [`L.DomUtil.preventOutline`](). 2525 function restoreOutline() { 2526 if (!_outlineElement) { return; } 2527 _outlineElement.style.outline = _outlineStyle; 2528 _outlineElement = undefined; 2529 _outlineStyle = undefined; 2530 off(window, 'keydown', restoreOutline); 2531 } 2532 2533 // @function getSizedParentNode(el: HTMLElement): HTMLElement 2534 // Finds the closest parent node which size (width and height) is not null. 2535 function getSizedParentNode(element) { 2536 do { 2537 element = element.parentNode; 2538 } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body); 2539 return element; 2540 } 2541 2542 // @function getScale(el: HTMLElement): Object 2543 // Computes the CSS scale currently applied on the element. 2544 // Returns an object with `x` and `y` members as horizontal and vertical scales respectively, 2545 // and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect). 2546 function getScale(element) { 2547 var rect = element.getBoundingClientRect(); // Read-only in old browsers. 2548 2549 return { 2550 x: rect.width / element.offsetWidth || 1, 2551 y: rect.height / element.offsetHeight || 1, 2552 boundingClientRect: rect 2553 }; 2554 } 2555 2556 var DomUtil = { 2557 __proto__: null, 2558 TRANSFORM: TRANSFORM, 2559 TRANSITION: TRANSITION, 2560 TRANSITION_END: TRANSITION_END, 2561 get: get, 2562 getStyle: getStyle, 2563 create: create$1, 2564 remove: remove, 2565 empty: empty, 2566 toFront: toFront, 2567 toBack: toBack, 2568 hasClass: hasClass, 2569 addClass: addClass, 2570 removeClass: removeClass, 2571 setClass: setClass, 2572 getClass: getClass, 2573 setOpacity: setOpacity, 2574 testProp: testProp, 2575 setTransform: setTransform, 2576 setPosition: setPosition, 2577 getPosition: getPosition, 2578 get disableTextSelection () { return disableTextSelection; }, 2579 get enableTextSelection () { return enableTextSelection; }, 2580 disableImageDrag: disableImageDrag, 2581 enableImageDrag: enableImageDrag, 2582 preventOutline: preventOutline, 2583 restoreOutline: restoreOutline, 2584 getSizedParentNode: getSizedParentNode, 2585 getScale: getScale 2586 }; 2587 2588 /* 2589 * @namespace DomEvent 2590 * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally. 2591 */ 2592 2593 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations. 2594 2595 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this 2596 // Adds a listener function (`fn`) to a particular DOM event type of the 2597 // element `el`. You can optionally specify the context of the listener 2598 // (object the `this` keyword will point to). You can also pass several 2599 // space-separated types (e.g. `'click dblclick'`). 2600 2601 // @alternative 2602 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this 2603 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` 2604 function on(obj, types, fn, context) { 2605 2606 if (typeof types === 'object') { 2607 for (var type in types) { 2608 addOne(obj, type, types[type], fn); 2609 } 2610 } else { 2611 types = splitWords(types); 2612 2613 for (var i = 0, len = types.length; i < len; i++) { 2614 addOne(obj, types[i], fn, context); 2615 } 2616 } 2617 2618 return this; 2619 } 2620 2621 var eventsKey = '_leaflet_events'; 2622 2623 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this 2624 // Removes a previously added listener function. 2625 // Note that if you passed a custom context to on, you must pass the same 2626 // context to `off` in order to remove the listener. 2627 2628 // @alternative 2629 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this 2630 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}` 2631 function off(obj, types, fn, context) { 2632 2633 if (typeof types === 'object') { 2634 for (var type in types) { 2635 removeOne(obj, type, types[type], fn); 2636 } 2637 } else if (types) { 2638 types = splitWords(types); 2639 2640 for (var i = 0, len = types.length; i < len; i++) { 2641 removeOne(obj, types[i], fn, context); 2642 } 2643 } else { 2644 for (var j in obj[eventsKey]) { 2645 removeOne(obj, j, obj[eventsKey][j]); 2646 } 2647 delete obj[eventsKey]; 2648 } 2649 2650 return this; 2651 } 2652 2653 function browserFiresNativeDblClick() { 2654 // See https://github.com/w3c/pointerevents/issues/171 2655 if (pointer) { 2656 return !(edge || safari); 2657 } 2658 } 2659 2660 var mouseSubst = { 2661 mouseenter: 'mouseover', 2662 mouseleave: 'mouseout', 2663 wheel: !('onwheel' in window) && 'mousewheel' 2664 }; 2665 2666 function addOne(obj, type, fn, context) { 2667 var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''); 2668 2669 if (obj[eventsKey] && obj[eventsKey][id]) { return this; } 2670 2671 var handler = function (e) {
2672 return fn.call(context || obj, e || window.event); 2673 }; 2674 2675 var originalHandler = handler; 2676 2677 if (pointer && type.indexOf('touch') === 0) { 2678 // Needs DomEvent.Pointer.js 2679 addPointerListener(obj, type, handler, id); 2680 2681 } else if (touch && (type === 'dblclick') && !browserFiresNativeDblClick()) { 2682 addDoubleTapListener(obj, handler, id); 2683 2684 } else if ('addEventListener' in obj) { 2685 2686 if (type === 'touchstart' || type === 'touchmove' || type === 'wheel' || type === 'mousewheel') { 2687 obj.addEventListener(mouseSubst[type] || type, handler, passiveEvents ? {passive: false} : false); 2688 2689 } else if (type === 'mouseenter' || type === 'mouseleave') { 2690 handler = function (e) { 2691 e = e || window.event; 2692 if (isExternalTarget(obj, e)) { 2693 originalHandler(e); 2694 } 2695 }; 2696 obj.addEventListener(mouseSubst[type], handler, false); 2697 2698 } else { 2699 obj.addEventListener(type, originalHandler, false); 2700 } 2701 2702 } else if ('attachEvent' in obj) { 2703 obj.attachEvent('on' + type, handler); 2704 } 2705 2706 obj[eventsKey] = obj[eventsKey] || {}; 2707 obj[eventsKey][id] = handler; 2708 } 2709 2710 function removeOne(obj, type, fn, context) { 2711 2712 var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''), 2713 handler = obj[eventsKey] && obj[eventsKey][id]; 2714 2715 if (!handler) { return this; } 2716 2717 if (pointer && type.indexOf('touch') === 0) { 2718 removePointerListener(obj, type, id); 2719 2720 } else if (touch && (type === 'dblclick') && !browserFiresNativeDblClick()) { 2721 removeDoubleTapListener(obj, id); 2722 2723 } else if ('removeEventListener' in obj) { 2724 2725 obj.removeEventListener(mouseSubst[type] || type, handler, false); 2726 2727 } else if ('detachEvent' in obj) { 2728 obj.detachEvent('on' + type, handler); 2729 } 2730 2731 obj[eventsKey][id] = null; 2732 } 2733 2734 // @function stopPropagation(ev: DOMEvent): this 2735 // Stop the given event from propagation to parent elements. Used inside the listener functions: 2736 // ```js 2737 // L.DomEvent.on(div, 'click', function (ev) { 2738 // L.DomEvent.stopPropagation(ev); 2739 // }); 2740 // ``` 2741 function stopPropagation(e) { 2742 2743 if (e.stopPropagation) { 2744 e.stopPropagation(); 2745 } else if (e.originalEvent) { // In case of Leaflet event. 2746 e.originalEvent._stopped = true; 2747 } else { 2748 e.cancelBubble = true; 2749 } 2750 skipped(e); 2751 2752 return this; 2753 } 2754 2755 // @function disableScrollPropagation(el: HTMLElement): this 2756 // Adds `stopPropagation` to the element's `'wheel'` events (plus browser variants). 2757 function disableScrollPropagation(el) { 2758 addOne(el, 'wheel', stopPropagation); 2759 return this; 2760 } 2761 2762 // @function disableClickPropagation(el: HTMLElement): this 2763 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`, 2764 // `'mousedown'` and `'touchstart'` events (plus browser variants). 2765 function disableClickPropagation(el) { 2766 on(el, 'mousedown touchstart dblclick', stopPropagation); 2767 addOne(el, 'click', fakeStop); 2768 return this; 2769 } 2770 2771 // @function preventDefault(ev: DOMEvent): this 2772 // Prevents the default action of the DOM Event `ev` from happening (such as 2773 // following a link in the href of the a element, or doing a POST request 2774 // with page reload when a `<form>` is submitted). 2775 // Use it inside listener functions. 2776 function preventDefault(e) { 2777 if (e.preventDefault) { 2778 e.preventDefault(); 2779 } else { 2780 e.returnValue = false; 2781 } 2782 return this; 2783 } 2784 2785 // @function stop(ev: DOMEvent): this 2786 // Does `stopPropagation` and `preventDefault` at the same time. 2787 function stop(e) { 2788 preventDefault(e); 2789 stopPropagation(e); 2790 return this; 2791 } 2792 2793 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point 2794 // Gets normalized mouse position from a DOM event relative to the 2795 // `container` (border excluded) or to the whole page if not specified. 2796 function getMousePosition(e, container) { 2797 if (!container) { 2798 return new Point(e.clientX, e.clientY); 2799 } 2800 2801 var scale = getScale(container), 2802 offset = scale.boundingClientRect; // left and top values are in page scale (like the event clientX/Y) 2803 2804 return new Point( 2805 // offset.left/top values are in page scale (like clientX/Y), 2806 // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies). 2807 (e.clientX - offset.left) / scale.x - container.clientLeft, 2808 (e.clientY - offset.top) / scale.y - container.clientTop 2809 ); 2810 } 2811 2812 // Chrome on Win scrolls double the pixels as in other platforms (see #4538), 2813 // and Firefox scrolls device pixels, not CSS pixels 2814 var wheelPxFactor = 2815 (win && chrome) ? 2 * window.devicePixelRatio : 2816 gecko ? window.devicePixelRatio : 1; 2817 2818 // @function getWheelDelta(ev: DOMEvent): Number 2819 // Gets normalized wheel delta from a wheel DOM event, in vertical 2820 // pixels scrolled (negative if scrolling down). 2821 // Events from pointing devices without precise scrolling are mapped to 2822 // a best guess of 60 pixels. 2823 function getWheelDelta(e) { 2824 return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta 2825 (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels 2826 (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines 2827 (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages 2828 (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events
vendor: 18,424 bytes, lines 2829-3382
2829 e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels 2830 (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines 2831 e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages 2832 0; 2833 } 2834 2835 var skipEvents = {}; 2836 2837 function fakeStop(e) { 2838 // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e) 2839 skipEvents[e.type] = true; 2840 } 2841 2842 function skipped(e) { 2843 var events = skipEvents[e.type]; 2844 // reset when checking, as it's only used in map container and propagates outside of the map 2845 skipEvents[e.type] = false; 2846 return events; 2847 } 2848 2849 // check if element really left/entered the event target (for mouseenter/mouseleave) 2850 function isExternalTarget(el, e) { 2851 2852 var related = e.relatedTarget; 2853 2854 if (!related) { return true; } 2855 2856 try { 2857 while (related && (related !== el)) { 2858 related = related.parentNode; 2859 } 2860 } catch (err) { 2861 return false; 2862 } 2863 return (related !== el); 2864 } 2865 2866 var DomEvent = { 2867 __proto__: null, 2868 on: on, 2869 off: off, 2870 stopPropagation: stopPropagation, 2871 disableScrollPropagation: disableScrollPropagation, 2872 disableClickPropagation: disableClickPropagation, 2873 preventDefault: preventDefault, 2874 stop: stop, 2875 getMousePosition: getMousePosition, 2876 getWheelDelta: getWheelDelta, 2877 fakeStop: fakeStop, 2878 skipped: skipped, 2879 isExternalTarget: isExternalTarget, 2880 addListener: on, 2881 removeListener: off 2882 }; 2883 2884 /* 2885 * @class PosAnimation 2886 * @aka L.PosAnimation 2887 * @inherits Evented 2888 * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9. 2889 * 2890 * @example 2891 * ```js 2892 * var fx = new L.PosAnimation(); 2893 * fx.run(el, [300, 500], 0.5); 2894 * ``` 2895 * 2896 * @constructor L.PosAnimation() 2897 * Creates a `PosAnimation` object. 2898 * 2899 */ 2900 2901 var PosAnimation = Evented.extend({ 2902 2903 // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number) 2904 // Run an animation of a given element to a new position, optionally setting 2905 // duration in seconds (`0.25` by default) and easing linearity factor (3rd 2906 // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1), 2907 // `0.5` by default). 2908 run: function (el, newPos, duration, easeLinearity) { 2909 this.stop(); 2910 2911 this._el = el; 2912 this._inProgress = true; 2913 this._duration = duration || 0.25; 2914 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2); 2915 2916 this._startPos = getPosition(el); 2917 this._offset = newPos.subtract(this._startPos); 2918 this._startTime = +new Date(); 2919 2920 // @event start: Event 2921 // Fired when the animation starts 2922 this.fire('start'); 2923 2924 this._animate(); 2925 }, 2926 2927 // @method stop() 2928 // Stops the animation (if currently running). 2929 stop: function () { 2930 if (!this._inProgress) { return; } 2931 2932 this._step(true); 2933 this._complete(); 2934 }, 2935 2936 _animate: function () { 2937 // animation loop 2938 this._animId = requestAnimFrame(this._animate, this); 2939 this._step(); 2940 }, 2941 2942 _step: function (round) { 2943 var elapsed = (+new Date()) - this._startTime, 2944 duration = this._duration * 1000; 2945 2946 if (elapsed < duration) { 2947 this._runFrame(this._easeOut(elapsed / duration), round); 2948 } else { 2949 this._runFrame(1); 2950 this._complete(); 2951 } 2952 }, 2953 2954 _runFrame: function (progress, round) { 2955 var pos = this._startPos.add(this._offset.multiplyBy(progress)); 2956 if (round) { 2957 pos._round(); 2958 } 2959 setPosition(this._el, pos); 2960 2961 // @event step: Event 2962 // Fired continuously during the animation. 2963 this.fire('step'); 2964 }, 2965 2966 _complete: function () { 2967 cancelAnimFrame(this._animId); 2968 2969 this._inProgress = false; 2970 // @event end: Event 2971 // Fired when the animation ends. 2972 this.fire('end'); 2973 }, 2974 2975 _easeOut: function (t) { 2976 return 1 - Math.pow(1 - t, this._easeOutPower); 2977 } 2978 }); 2979 2980 /* 2981 * @class Map 2982 * @aka L.Map 2983 * @inherits Evented 2984 * 2985 * The central class of the API â it is used to create a map on a page and manipulate it. 2986 * 2987 * @example 2988 * 2989 * ```js 2990 * // initialize the map on the "map" div with a given center and zoom 2991 * var map = L.map('map', { 2992 * center: [51.505, -0.09], 2993 * zoom: 13 2994 * }); 2995 * ``` 2996 * 2997 */ 2998 2999 var Map = Evented.extend({ 3000 3001 options: { 3002 // @section Map State Options 3003 // @option crs: CRS = L.CRS.EPSG3857 3004 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not 3005 // sure what it means. 3006 crs: EPSG3857, 3007 3008 // @option center: LatLng = undefined 3009 // Initial geographic center of the map 3010 center: undefined, 3011 3012 // @option zoom: Number = undefined 3013 // Initial map zoom level 3014 zoom: undefined, 3015 3016 // @option minZoom: Number = * 3017 // Minimum zoom level of the map. 3018 // If not specified and at least one `GridLayer` or `TileLayer` is in the map, 3019 // the lowest of their `minZoom` options will be used instead. 3020 minZoom: undefined, 3021 3022 // @option maxZoom: Number = * 3023 // Maximum zoom level of the map. 3024 // If not specified and at least one `GridLayer` or `TileLayer` is in the map, 3025 // the highest of their `maxZoom` options will be used instead. 3026 maxZoom: undefined, 3027 3028 // @option layers: Layer[] = [] 3029 // Array of layers that will be added to the map initially 3030 layers: [], 3031 3032 // @option maxBounds: LatLngBounds = null 3033 // When this option is set, the map restricts the view to the given 3034 // geographical bounds, bouncing the user back if the user tries to pan 3035 // outside the view. To set the restriction dynamically, use 3036 // [`setMaxBounds`](#map-setmaxbounds) method. 3037 maxBounds: undefined, 3038 3039 // @option renderer: Renderer = * 3040 // The default method for drawing vector layers on the map. `L.SVG` 3041 // or `L.Canvas` by default depending on browser support. 3042 renderer: undefined, 3043 3044 3045 // @section Animation Options 3046 // @option zoomAnimation: Boolean = true 3047 // Whether the map zoom animation is enabled. By default it's enabled 3048 // in all browsers that support CSS3 Transitions except Android. 3049 zoomAnimation: true, 3050 3051 // @option zoomAnimationThreshold: Number = 4 3052 // Won't animate zoom if the zoom difference exceeds this value. 3053 zoomAnimationThreshold: 4, 3054 3055 // @option fadeAnimation: Boolean = true 3056 // Whether the tile fade animation is enabled. By default it's enabled 3057 // in all browsers that support CSS3 Transitions except Android. 3058 fadeAnimation: true, 3059 3060 // @option markerZoomAnimation: Boolean = true 3061 // Whether markers animate their zoom with the zoom animation, if disabled 3062 // they will disappear for the length of the animation. By default it's 3063 // enabled in all browsers that support CSS3 Transitions except Android. 3064 markerZoomAnimation: true, 3065 3066 // @option transform3DLimit: Number = 2^23 3067 // Defines the maximum size of a CSS translation transform. The default 3068 // value should not be changed unless a web browser positions layers in 3069 // the wrong place after doing a large `panBy`. 3070 transform3DLimit: 8388608, // Precision limit of a 32-bit float 3071 3072 // @section Interaction Options 3073 // @option zoomSnap: Number = 1 3074 // Forces the map's zoom level to always be a multiple of this, particularly 3075 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom. 3076 // By default, the zoom level snaps to the nearest integer; lower values 3077 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0` 3078 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom. 3079 zoomSnap: 1, 3080 3081 // @option zoomDelta: Number = 1 3082 // Controls how much the map's zoom level will change after a 3083 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+` 3084 // or `-` on the keyboard, or using the [zoom controls](#control-zoom). 3085 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity. 3086 zoomDelta: 1, 3087 3088 // @option trackResize: Boolean = true 3089 // Whether the map automatically handles browser window resize to update itself. 3090 trackResize: true 3091 }, 3092 3093 initialize: function (id, options) { // (HTMLElement or String, Object) 3094 options = setOptions(this, options); 3095 3096 // Make sure to assign internal flags at the beginning, 3097 // to avoid inconsistent state in some edge cases. 3098 this._handlers = []; 3099 this._layers = {}; 3100 this._zoomBoundLayers = {}; 3101 this._sizeChanged = true; 3102 3103 this._initContainer(id); 3104 this._initLayout(); 3105 3106 // hack for https://github.com/Leaflet/Leaflet/issues/1980 3107 this._onResize = bind(this._onResize, this); 3108 3109 this._initEvents(); 3110 3111 if (options.maxBounds) { 3112 this.setMaxBounds(options.maxBounds); 3113 } 3114 3115 if (options.zoom !== undefined) { 3116 this._zoom = this._limitZoom(options.zoom); 3117 } 3118 3119 if (options.center && options.zoom !== undefined) { 3120 this.setView(toLatLng(options.center), options.zoom, {reset: true}); 3121 } 3122 3123 this.callInitHooks(); 3124 3125 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera 3126 this._zoomAnimated = TRANSITION && any3d && !mobileOpera && 3127 this.options.zoomAnimation; 3128 3129 // zoom transitions run with the same duration for all layers, so if one of transitionend events 3130 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally 3131 if (this._zoomAnimated) { 3132 this._createAnimProxy(); 3133 on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this); 3134 } 3135 3136 this._addLayers(this.options.layers); 3137 }, 3138 3139 3140 // @section Methods for modifying map state 3141 3142 // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this 3143 // Sets the view of the map (geographical center and zoom) with the given 3144 // animation options. 3145 setView: function (center, zoom, options) { 3146 3147 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom); 3148 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds); 3149 options = options || {}; 3150 3151 this._stop(); 3152 3153 if (this._loaded && !options.reset && options !== true) { 3154 3155 if (options.animate !== undefined) { 3156 options.zoom = extend({animate: options.animate}, options.zoom); 3157 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan); 3158 } 3159 3160 // try animating pan or zoom 3161 var moved = (this._zoom !== zoom) ? 3162 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) : 3163 this._tryAnimatedPan(center, options.pan); 3164 3165 if (moved) { 3166 // prevent resize handler call, the view will refresh after animation anyway 3167 clearTimeout(this._sizeTimer); 3168 return this; 3169 } 3170 } 3171 3172 // animation didn't start, just reset the map view 3173 this._resetView(center, zoom); 3174 3175 return this; 3176 }, 3177 3178 // @method setZoom(zoom: Number, options?: Zoom/pan options): this 3179 // Sets the zoom of the map. 3180 setZoom: function (zoom, options) { 3181 if (!this._loaded) { 3182 this._zoom = zoom; 3183 return this; 3184 } 3185 return this.setView(this.getCenter(), zoom, {zoom: options}); 3186 }, 3187 3188 // @method zoomIn(delta?: Number, options?: Zoom options): this 3189 // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). 3190 zoomIn: function (delta, options) { 3191 delta = delta || (any3d ? this.options.zoomDelta : 1); 3192 return this.setZoom(this._zoom + delta, options); 3193 }, 3194 3195 // @method zoomOut(delta?: Number, options?: Zoom options): this 3196 // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default). 3197 zoomOut: function (delta, options) { 3198 delta = delta || (any3d ? this.options.zoomDelta : 1); 3199 return this.setZoom(this._zoom - delta, options); 3200 }, 3201 3202 // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this 3203 // Zooms the map while keeping a specified geographical point on the map 3204 // stationary (e.g. used internally for scroll zoom and double-click zoom). 3205 // @alternative 3206 // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this 3207 // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary. 3208 setZoomAround: function (latlng, zoom, options) { 3209 var scale = this.getZoomScale(zoom), 3210 viewHalf = this.getSize().divideBy(2), 3211 containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng), 3212 3213 centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale), 3214 newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); 3215 3216 return this.setView(newCenter, zoom, {zoom: options}); 3217 }, 3218 3219 _getBoundsCenterZoom: function (bounds, options) { 3220 3221 options = options || {}; 3222 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds); 3223 3224 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), 3225 paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), 3226 3227 zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); 3228 3229 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom; 3230 3231 if (zoom === Infinity) { 3232 return { 3233 center: bounds.getCenter(), 3234 zoom: zoom 3235 }; 3236 } 3237 3238 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), 3239 3240 swPoint = this.project(bounds.getSouthWest(), zoom), 3241 nePoint = this.project(bounds.getNorthEast(), zoom), 3242 center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom); 3243 3244 return { 3245 center: center, 3246 zoom: zoom 3247 }; 3248 }, 3249 3250 // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this 3251 // Sets a map view that contains the given geographical bounds with the 3252 // maximum zoom level possible. 3253 fitBounds: function (bounds, options) { 3254 3255 bounds = toLatLngBounds(bounds); 3256 3257 if (!bounds.isValid()) { 3258 throw new Error('Bounds are not valid.'); 3259 } 3260 3261 var target = this._getBoundsCenterZoom(bounds, options); 3262 return this.setView(target.center, target.zoom, options); 3263 }, 3264 3265 // @method fitWorld(options?: fitBounds options): this 3266 // Sets a map view that mostly contains the whole world with the maximum 3267 // zoom level possible. 3268 fitWorld: function (options) { 3269 return this.fitBounds([[-90, -180], [90, 180]], options); 3270 }, 3271 3272 // @method panTo(latlng: LatLng, options?: Pan options): this 3273 // Pans the map to a given center. 3274 panTo: function (center, options) { // (LatLng) 3275 return this.setView(center, this._zoom, {pan: options}); 3276 }, 3277 3278 // @method panBy(offset: Point, options?: Pan options): this 3279 // Pans the map by a given number of pixels (animated). 3280 panBy: function (offset, options) { 3281 offset = toPoint(offset).round(); 3282 options = options || {}; 3283 3284 if (!offset.x && !offset.y) { 3285 return this.fire('moveend'); 3286 } 3287 // If we pan too far, Chrome gets issues with tiles 3288 // and makes them disappear or appear in the wrong place (slightly offset) #2602 3289 if (options.animate !== true && !this.getSize().contains(offset)) { 3290 this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); 3291 return this; 3292 } 3293 3294 if (!this._panAnim) { 3295 this._panAnim = new PosAnimation(); 3296 3297 this._panAnim.on({ 3298 'step': this._onPanTransitionStep, 3299 'end': this._onPanTransitionEnd 3300 }, this); 3301 } 3302 3303 // don't fire movestart if animating inertia 3304 if (!options.noMoveStart) { 3305 this.fire('movestart'); 3306 } 3307 3308 // animate pan unless animate: false specified 3309 if (options.animate !== false) { 3310 addClass(this._mapPane, 'leaflet-pan-anim'); 3311 3312 var newPos = this._getMapPanePos().subtract(offset).round(); 3313 this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity); 3314 } else { 3315 this._rawPanBy(offset); 3316 this.fire('move').fire('moveend'); 3317 } 3318 3319 return this; 3320 }, 3321 3322 // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this 3323 // Sets the view of the map (geographical center and zoom) performing a smooth 3324 // pan-zoom animation. 3325 flyTo: function (targetCenter, targetZoom, options) { 3326 3327 options = options || {}; 3328 if (options.animate === false || !any3d) { 3329 return this.setView(targetCenter, targetZoom, options); 3330 } 3331 3332 this._stop(); 3333 3334 var from = this.project(this.getCenter()), 3335 to = this.project(targetCenter), 3336 size = this.getSize(), 3337 startZoom = this._zoom; 3338 3339 targetCenter = toLatLng(targetCenter); 3340 targetZoom = targetZoom === undefined ? startZoom : targetZoom; 3341 3342 var w0 = Math.max(size.x, size.y), 3343 w1 = w0 * this.getZoomScale(startZoom, targetZoom), 3344 u1 = (to.distanceTo(from)) || 1, 3345 rho = 1.42, 3346 rho2 = rho * rho; 3347 3348 function r(i) { 3349 var s1 = i ? -1 : 1, 3350 s2 = i ? w1 : w0, 3351 t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1, 3352 b1 = 2 * s2 * rho2 * u1, 3353 b = t1 / b1, 3354 sq = Math.sqrt(b * b + 1) - b; 3355 3356 // workaround for floating point precision bug when sq = 0, log = -Infinite, 3357 // thus triggering an infinite loop in flyTo 3358 var log = sq < 0.000000001 ? -18 : Math.log(sq); 3359 3360 return log; 3361 } 3362 3363 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; } 3364 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; } 3365 function tanh(n) { return sinh(n) / cosh(n); } 3366 3367 var r0 = r(0); 3368 3369 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); } 3370 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; } 3371 3372 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); } 3373 3374 var start = Date.now(), 3375 S = (r(1) - r0) / rho, 3376 duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8; 3377 3378 function frame() { 3379 var t = (Date.now() - start) / duration, 3380 s = easeOut(t) * S; 3381 3382 if (t <= 1) {
3383 this._flyToFrame = requestAnimFrame(frame, this); 3384 3385 this._move( 3386 this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom), 3387 this.getScaleZoom(w0 / w(s), startZoom), 3388 {flyTo: true}); 3389 3390 } else { 3391 this 3392 ._move(targetCenter, targetZoom) 3393 ._moveEnd(true); 3394 } 3395 } 3396 3397 this._moveStart(true, options.noMoveStart); 3398 3399 frame.call(this); 3400 return this; 3401 }, 3402 3403 // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this 3404 // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto), 3405 // but takes a bounds parameter like [`fitBounds`](#map-fitbounds). 3406 flyToBounds: function (bounds, options) { 3407 var target = this._getBoundsCenterZoom(bounds, options); 3408 return this.flyTo(target.center, target.zoom, options); 3409 }, 3410 3411 // @method setMaxBounds(bounds: LatLngBounds): this 3412 // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option). 3413 setMaxBounds: function (bounds) { 3414 bounds = toLatLngBounds(bounds); 3415 3416 if (!bounds.isValid()) { 3417 this.options.maxBounds = null; 3418 return this.off('moveend', this._panInsideMaxBounds); 3419 } else if (this.options.maxBounds) { 3420 this.off('moveend', this._panInsideMaxBounds); 3421 } 3422 3423 this.options.maxBounds = bounds; 3424 3425 if (this._loaded) { 3426 this._panInsideMaxBounds(); 3427 } 3428 3429 return this.on('moveend', this._panInsideMaxBounds); 3430 }, 3431 3432 // @method setMinZoom(zoom: Number): this 3433 // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option). 3434 setMinZoom: function (zoom) { 3435 var oldZoom = this.options.minZoom; 3436 this.options.minZoom = zoom; 3437 3438 if (this._loaded && oldZoom !== zoom) { 3439 this.fire('zoomlevelschange'); 3440 3441 if (this.getZoom() < this.options.minZoom) { 3442 return this.setZoom(zoom); 3443 } 3444 } 3445 3446 return this; 3447 }, 3448 3449 // @method setMaxZoom(zoom: Number): this 3450 // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option). 3451 setMaxZoom: function (zoom) { 3452 var oldZoom = this.options.maxZoom; 3453 this.options.maxZoom = zoom; 3454 3455 if (this._loaded && oldZoom !== zoom) { 3456 this.fire('zoomlevelschange'); 3457 3458 if (this.getZoom() > this.options.maxZoom) { 3459 return this.setZoom(zoom); 3460 } 3461 } 3462 3463 return this; 3464 }, 3465 3466 // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this 3467 // 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. 3468 panInsideBounds: function (bounds, options) { 3469 this._enforcingBounds = true; 3470 var center = this.getCenter(), 3471 newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds)); 3472 3473 if (!center.equals(newCenter)) { 3474 this.panTo(newCenter, options); 3475 } 3476 3477 this._enforcingBounds = false; 3478 return this; 3479 }, 3480 3481 // @method panInside(latlng: LatLng, options?: options): this 3482 // Pans the map the minimum amount to make the `latlng` visible. Use 3483 // `padding`, `paddingTopLeft` and `paddingTopRight` options to fit 3484 // the display to more restricted bounds, like [`fitBounds`](#map-fitbounds). 3485 // If `latlng` is already within the (optionally padded) display bounds, 3486 // the map will not be panned. 3487 panInside: function (latlng, options) { 3488 options = options || {}; 3489 3490 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]), 3491 paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]), 3492 center = this.getCenter(), 3493 pixelCenter = this.project(center), 3494 pixelPoint = this.project(latlng), 3495 pixelBounds = this.getPixelBounds(), 3496 halfPixelBounds = pixelBounds.getSize().divideBy(2), 3497 paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]); 3498 3499 if (!paddedBounds.contains(pixelPoint)) { 3500 this._enforcingBounds = true; 3501 var diff = pixelCenter.subtract(pixelPoint), 3502 newCenter = toPoint(pixelPoint.x + diff.x, pixelPoint.y + diff.y); 3503 3504 if (pixelPoint.x < paddedBounds.min.x || pixelPoint.x > paddedBounds.max.x) { 3505 newCenter.x = pixelCenter.x - diff.x; 3506 if (diff.x > 0) { 3507 newCenter.x += halfPixelBounds.x - paddingTL.x; 3508 } else { 3509 newCenter.x -= halfPixelBounds.x - paddingBR.x; 3510 } 3511 } 3512 if (pixelPoint.y < paddedBounds.min.y || pixelPoint.y > paddedBounds.max.y) { 3513 newCenter.y = pixelCenter.y - diff.y; 3514 if (diff.y > 0) { 3515 newCenter.y += halfPixelBounds.y - paddingTL.y; 3516 } else { 3517 newCenter.y -= halfPixelBounds.y - paddingBR.y; 3518 } 3519 } 3520 this.panTo(this.unproject(new
vendor: 4,387 bytes, lines 3520-3651
3520Center), options); 3521 this._enforcingBounds = false; 3522 } 3523 return this; 3524 }, 3525 3526 // @method invalidateSize(options: Zoom/pan options): this 3527 // Checks if the map container size changed and updates the map if so â 3528 // call it after you've changed the map size dynamically, also animating 3529 // pan by default. If `options.pan` is `false`, panning will not occur. 3530 // If `options.debounceMoveend` is `true`, it will delay `moveend` event so 3531 // that it doesn't happen often even if the method is called many 3532 // times in a row. 3533 3534 // @alternative 3535 // @method invalidateSize(animate: Boolean): this 3536 // Checks if the map container size changed and updates the map if so â 3537 // call it after you've changed the map size dynamically, also animating 3538 // pan by default. 3539 invalidateSize: function (options) { 3540 if (!this._loaded) { return this; } 3541 3542 options = extend({ 3543 animate: false, 3544 pan: true 3545 }, options === true ? {animate: true} : options); 3546 3547 var oldSize = this.getSize(); 3548 this._sizeChanged = true; 3549 this._lastCenter = null; 3550 3551 var newSize = this.getSize(), 3552 oldCenter = oldSize.divideBy(2).round(), 3553 newCenter = newSize.divideBy(2).round(), 3554 offset = oldCenter.subtract(newCenter); 3555 3556 if (!offset.x && !offset.y) { return this; } 3557 3558 if (options.animate && options.pan) { 3559 this.panBy(offset); 3560 3561 } else { 3562 if (options.pan) { 3563 this._rawPanBy(offset); 3564 } 3565 3566 this.fire('move'); 3567 3568 if (options.debounceMoveend) { 3569 clearTimeout(this._sizeTimer); 3570 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200); 3571 } else { 3572 this.fire('moveend'); 3573 } 3574 } 3575 3576 // @section Map state change events 3577 // @event resize: ResizeEvent 3578 // Fired when the map is resized. 3579 return this.fire('resize', { 3580 oldSize: oldSize, 3581 newSize: newSize 3582 }); 3583 }, 3584 3585 // @section Methods for modifying map state 3586 // @method stop(): this 3587 // Stops the currently running `panTo` or `flyTo` animation, if any. 3588 stop: function () { 3589 this.setZoom(this._limitZoom(this._zoom)); 3590 if (!this.options.zoomSnap) { 3591 this.fire('viewreset'); 3592 } 3593 return this._stop(); 3594 }, 3595 3596 // @section Geolocation methods 3597 // @method locate(options?: Locate options): this 3598 // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound) 3599 // event with location data on success or a [`locationerror`](#map-locationerror) event on failure, 3600 // and optionally sets the map view to the user's location with respect to 3601 // detection accuracy (or to the world view if geolocation failed). 3602 // Note that, if your page doesn't use HTTPS, this method will fail in 3603 // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins)) 3604 // See `Locate options` for more details. 3605 locate: function (options) { 3606 3607 options = this._locateOptions = extend({ 3608 timeout: 10000, 3609 watch: false 3610 // setView: false 3611 // maxZoom: <Number> 3612 // maximumAge: 0 3613 // enableHighAccuracy: false 3614 }, options); 3615 3616 if (!('geolocation' in navigator)) { 3617 this._handleGeolocationError({ 3618 code: 0, 3619 message: 'Geolocation not supported.' 3620 }); 3621 return this; 3622 } 3623 3624 var onResponse = bind(this._handleGeolocationResponse, this), 3625 onError = bind(this._handleGeolocationError, this); 3626 3627 if (options.watch) { 3628 this._locationWatchId = 3629 navigator.geolocation.watchPosition(onResponse, onError, options); 3630 } else { 3631 navigator.geolocation.getCurrentPosition(onResponse, onError, options); 3632 } 3633 return this; 3634 }, 3635 3636 // @method stopLocate(): this 3637 // Stops watching location previously initiated by `map.locate({watch: true})` 3638 // and aborts resetting the map view if map.locate was called with 3639 // `{setView: true}`. 3640 stopLocate: function () { 3641 if (navigator.geolocation && navigator.geolocation.clearWatch) { 3642 navigator.geolocation.clearWatch(this._locationWatchId); 3643 } 3644 if (this._locateOptions) { 3645 this._locateOptions.setView = false; 3646 } 3647 return this; 3648 }, 3649 3650 _handleGeolocationError: function (error) { 3651
vendor: 6,375 bytes, lines 3651-3854
3651var c = error.code, 3652 message = error.message || 3653 (c === 1 ? 'permission denied' : 3654 (c === 2 ? 'position unavailable' : 'timeout')); 3655 3656 if (this._locateOptions.setView && !this._loaded) { 3657 this.fitWorld(); 3658 } 3659 3660 // @section Location events 3661 // @event locationerror: ErrorEvent 3662 // Fired when geolocation (using the [`locate`](#map-locate) method) failed. 3663 this.fire('locationerror', { 3664 code: c, 3665 message: 'Geolocation error: ' + message + '.' 3666 }); 3667 }, 3668 3669 _handleGeolocationResponse: function (pos) { 3670 var lat = pos.coords.latitude, 3671 lng = pos.coords.longitude, 3672 latlng = new LatLng(lat, lng), 3673 bounds = latlng.toBounds(pos.coords.accuracy * 2), 3674 options = this._locateOptions; 3675 3676 if (options.setView) { 3677 var zoom = this.getBoundsZoom(bounds); 3678 this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom); 3679 } 3680 3681 var data = { 3682 latlng: latlng, 3683 bounds: bounds, 3684 timestamp: pos.timestamp 3685 }; 3686 3687 for (var i in pos.coords) { 3688 if (typeof pos.coords[i] === 'number') { 3689 data[i] = pos.coords[i]; 3690 } 3691 } 3692 3693 // @event locationfound: LocationEvent 3694 // Fired when geolocation (using the [`locate`](#map-locate) method) 3695 // went successfully. 3696 this.fire('locationfound', data); 3697 }, 3698 3699 // TODO Appropriate docs section? 3700 // @section Other Methods 3701 // @method addHandler(name: String, HandlerClass: Function): this 3702 // Adds a new `Handler` to the map, given its name and constructor function. 3703 addHandler: function (name, HandlerClass) { 3704 if (!HandlerClass) { return this; } 3705 3706 var handler = this[name] = new HandlerClass(this); 3707 3708 this._handlers.push(handler); 3709 3710 if (this.options[name]) { 3711 handler.enable(); 3712 } 3713 3714 return this; 3715 }, 3716 3717 // @method remove(): this 3718 // Destroys the map and clears all related event listeners. 3719 remove: function () { 3720 3721 this._initEvents(true); 3722 this.off('moveend', this._panInsideMaxBounds); 3723 3724 if (this._containerId !== this._container._leaflet_id) { 3725 throw new Error('Map container is being reused by another instance'); 3726 } 3727 3728 try { 3729 // throws error in IE6-8 3730 delete this._container._leaflet_id; 3731 delete this._containerId; 3732 } catch (e) { 3733 /*eslint-disable */ 3734 this._container._leaflet_id = undefined; 3735 /* eslint-enable */ 3736 this._containerId = undefined; 3737 } 3738 3739 if (this._locationWatchId !== undefined) { 3740 this.stopLocate(); 3741 } 3742 3743 this._stop(); 3744 3745 remove(this._mapPane); 3746 3747 if (this._clearControlPos) { 3748 this._clearControlPos(); 3749 } 3750 if (this._resizeRequest) { 3751 cancelAnimFrame(this._resizeRequest); 3752 this._resizeRequest = null; 3753 } 3754 3755 this._clearHandlers(); 3756 3757 if (this._loaded) { 3758 // @section Map state change events 3759 // @event unload: Event 3760 // Fired when the map is destroyed with [remove](#map-remove) method. 3761 this.fire('unload'); 3762 } 3763 3764 var i; 3765 for (i in this._layers) { 3766 this._layers[i].remove(); 3767 } 3768 for (i in this._panes) { 3769 remove(this._panes[i]); 3770 } 3771 3772 this._layers = []; 3773 this._panes = []; 3774 delete this._mapPane; 3775 delete this._renderer; 3776 3777 return this; 3778 }, 3779 3780 // @section Other Methods 3781 // @method createPane(name: String, container?: HTMLElement): HTMLElement 3782 // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already, 3783 // then returns it. The pane is created as a child of `container`, or 3784 // as a child of the main map pane if not set. 3785 createPane: function (name, container) { 3786 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''), 3787 pane = create$1('div', className, container || this._mapPane); 3788 3789 if (name) { 3790 this._panes[name] = pane; 3791 } 3792 return pane; 3793 }, 3794 3795 // @section Methods for Getting Map State 3796 3797 // @method getCenter(): LatLng 3798 // Returns the geographical center of the map view 3799 getCenter: function () { 3800 this._checkIfLoaded(); 3801 3802 if (this._lastCenter && !this._moved()) { 3803 return this._lastCenter; 3804 } 3805 return this.layerPointToLatLng(this._getCenterLayerPoint()); 3806 }, 3807 3808 // @method getZoom(): Number 3809 // Returns the current zoom level of the map view 3810 getZoom: function () { 3811 return this._zoom; 3812 }, 3813 3814 // @method getBounds(): LatLngBounds 3815 // Returns the geographical bounds visible in the current map view 3816 getBounds: function () { 3817 var bounds = this.getPixelBounds(), 3818 sw = this.unproject(bounds.getBottomLeft()), 3819 ne = this.unproject(bounds.getTopRight()); 3820 3821 return new LatLngBounds(sw, ne); 3822 }, 3823 3824 // @method getMinZoom(): Number 3825 // 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. 3826 getMinZoom: function () { 3827 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom; 3828 }, 3829 3830 // @method getMaxZoom(): Number 3831 // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers). 3832 getMaxZoom: function () { 3833 return this.options.maxZoom === undefined ? 3834 (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) : 3835 this.options.maxZoom; 3836 }, 3837 3838 // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number 3839 // Returns the maximum zoom level on which the given bounds fit to the map 3840 // view in its entirety. If `inside` (optional) is set to `true`, the method 3841 // instead returns the minimum zoom level on which the map view fits into 3842 // the given bounds in its entirety. 3843 getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number 3844 bounds = toLatLngBounds(bounds); 3845 padding = toPoint(padding || [0, 0]); 3846 3847 var zoom = this.getZoom() || 0, 3848 min = this.getMinZoom(), 3849 max = this.getMaxZoom(), 3850 nw = bounds.getNorthWest(), 3851 se = bounds.getSouthEast(), 3852 size = this.getSize().subtract(padding), 3853 boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(), 3854 snap =
vendor: 9,586 bytes, lines 3854-4082
3854any3d ? this.options.zoomSnap : 1, 3855 scalex = size.x / boundsSize.x, 3856 scaley = size.y / boundsSize.y, 3857 scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley); 3858 3859 zoom = this.getScaleZoom(scale, zoom); 3860 3861 if (snap) { 3862 zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level 3863 zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap; 3864 } 3865 3866 return Math.max(min, Math.min(max, zoom)); 3867 }, 3868 3869 // @method getSize(): Point 3870 // Returns the current size of the map container (in pixels). 3871 getSize: function () { 3872 if (!this._size || this._sizeChanged) { 3873 this._size = new Point( 3874 this._container.clientWidth || 0, 3875 this._container.clientHeight || 0); 3876 3877 this._sizeChanged = false; 3878 } 3879 return this._size.clone(); 3880 }, 3881 3882 // @method getPixelBounds(): Bounds 3883 // Returns the bounds of the current map view in projected pixel 3884 // coordinates (sometimes useful in layer and overlay implementations). 3885 getPixelBounds: function (center, zoom) { 3886 var topLeftPoint = this._getTopLeftPoint(center, zoom); 3887 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); 3888 }, 3889 3890 // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to 3891 // the map pane? "left point of the map layer" can be confusing, specially 3892 // since there can be negative offsets. 3893 // @method getPixelOrigin(): Point 3894 // Returns the projected pixel coordinates of the top left point of 3895 // the map layer (useful in custom layer and overlay implementations). 3896 getPixelOrigin: function () { 3897 this._checkIfLoaded(); 3898 return this._pixelOrigin; 3899 }, 3900 3901 // @method getPixelWorldBounds(zoom?: Number): Bounds 3902 // Returns the world's bounds in pixel coordinates for zoom level `zoom`. 3903 // If `zoom` is omitted, the map's current zoom level is used. 3904 getPixelWorldBounds: function (zoom) { 3905 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom); 3906 }, 3907 3908 // @section Other Methods 3909 3910 // @method getPane(pane: String|HTMLElement): HTMLElement 3911 // Returns a [map pane](#map-pane), given its name or its HTML element (its identity). 3912 getPane: function (pane) { 3913 return typeof pane === 'string' ? this._panes[pane] : pane; 3914 }, 3915 3916 // @method getPanes(): Object 3917 // Returns a plain object containing the names of all [panes](#map-pane) as keys and 3918 // the panes as values. 3919 getPanes: function () { 3920 return this._panes; 3921 }, 3922 3923 // @method getContainer: HTMLElement 3924 // Returns the HTML element that contains the map. 3925 getContainer: function () { 3926 return this._container; 3927 }, 3928 3929 3930 // @section Conversion Methods 3931 3932 // @method getZoomScale(toZoom: Number, fromZoom: Number): Number 3933 // Returns the scale factor to be applied to a map transition from zoom level 3934 // `fromZoom` to `toZoom`. Used internally to help with zoom animations. 3935 getZoomScale: function (toZoom, fromZoom) { 3936 // TODO replace with universal implementation after refactoring projections 3937 var crs = this.options.crs; 3938 fromZoom = fromZoom === undefined ? this._zoom : fromZoom; 3939 return crs.scale(toZoom) / crs.scale(fromZoom); 3940 }, 3941 3942 // @method getScaleZoom(scale: Number, fromZoom: Number): Number 3943 // Returns the zoom level that the map would end up at, if it is at `fromZoom` 3944 // level and everything is scaled by a factor of `scale`. Inverse of 3945 // [`getZoomScale`](#map-getZoomScale). 3946 getScaleZoom: function (scale, fromZoom) { 3947 var crs = this.options.crs; 3948 fromZoom = fromZoom === undefined ? this._zoom : fromZoom; 3949 var zoom = crs.zoom(scale * crs.scale(fromZoom)); 3950 return isNaN(zoom) ? Infinity : zoom; 3951 }, 3952 3953 // @method project(latlng: LatLng, zoom: Number): Point 3954 // Projects a geographical coordinate `LatLng` according to the projection 3955 // of the map's CRS, then scales it according to `zoom` and the CRS's 3956 // `Transformation`. The result is pixel coordinate relative to 3957 // the CRS origin. 3958 project: function (latlng, zoom) { 3959 zoom = zoom === undefined ? this._zoom : zoom; 3960 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom); 3961 }, 3962 3963 // @method unproject(point: Point, zoom: Number): LatLng 3964 // Inverse of [`project`](#map-project). 3965 unproject: function (point, zoom) { 3966 zoom = zoom === undefined ? this._zoom : zoom; 3967 return this.options.crs.pointToLatLng(toPoint(point), zoom); 3968 }, 3969 3970 // @method layerPointToLatLng(point: Point): LatLng 3971 // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), 3972 // returns the corresponding geographical coordinate (for the current zoom level). 3973 layerPointToLatLng: function (point) { 3974 var projectedPoint = toPoint(point).add(this.getPixelOrigin()); 3975 return this.unproject(projectedPoint); 3976 }, 3977 3978 // @method latLngToLayerPoint(latlng: LatLng): Point 3979 // Given a geographical coordinate, returns the corresponding pixel coordinate 3980 // relative to the [origin pixel](#map-getpixelorigin). 3981 latLngToLayerPoint: function (latlng) { 3982 var projectedPoint = this.project(toLatLng(latlng))._round(); 3983 return projectedPoint._subtract(this.getPixelOrigin()); 3984 }, 3985 3986 // @method wrapLatLng(latlng: LatLng): LatLng 3987 // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the 3988 // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the 3989 // CRS's bounds. 3990 // By default this means longitude is wrapped around the dateline so its 3991 // value is between -180 and +180 degrees. 3992 wrapLatLng: function (latlng) { 3993 return this.options.crs.wrapLatLng(toLatLng(latlng)); 3994 }, 3995 3996 // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds 3997 // Returns a `LatLngBounds` with the same size as the given one, ensuring that 3998 // its center is within the CRS's bounds. 3999 // By default this means the center longitude is wrapped around the dateline so its 4000 // value is between -180 and +180 degrees, and the majority of the bounds 4001 // overlaps the CRS's bounds. 4002 wrapLatLngBounds: function (latlng) { 4003 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng)); 4004 }, 4005 4006 // @method distance(latlng1: LatLng, latlng2: LatLng): Number 4007 // Returns the distance between two geographical coordinates according to 4008 // the map's CRS. By default this measures distance in meters. 4009 distance: function (latlng1, latlng2) { 4010 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2)); 4011 }, 4012 4013 // @method containerPointToLayerPoint(point: Point): Point 4014 // Given a pixel coordinate relative to the map container, returns the corresponding 4015 // pixel coordinate relative to the [origin pixel](#map-getpixelorigin). 4016 containerPointToLayerPoint: function (point) { // (Point) 4017 return toPoint(point).subtract(this._getMapPanePos()); 4018 }, 4019 4020 // @method layerPointToContainerPoint(point: Point): Point 4021 // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin), 4022 // returns the corresponding pixel coordinate relative to the map container. 4023 layerPointToContainerPoint: function (point) { // (Point) 4024 return toPoint(point).add(this._getMapPanePos()); 4025 }, 4026 4027 // @method containerPointToLatLng(point: Point): LatLng 4028 // Given a pixel coordinate relative to the map container, returns 4029 // the corresponding geographical coordinate (for the current zoom level). 4030 containerPointToLatLng: function (point) { 4031 var layerPoint = this.containerPointToLayerPoint(toPoint(point)); 4032 return this.layerPointToLatLng(layerPoint); 4033 }, 4034 4035 // @method latLngToContainerPoint(latlng: LatLng): Point 4036 // Given a geographical coordinate, returns the corresponding pixel coordinate 4037 // relative to the map container. 4038 latLngToContainerPoint: function (latlng) { 4039 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng))); 4040 }, 4041 4042 // @method mouseEventToContainerPoint(ev: MouseEvent): Point 4043 // Given a MouseEvent object, returns the pixel coordinate relative to the 4044 // map container where the event took place. 4045 mouseEventToContainerPoint: function (e) { 4046 return getMousePosition(e, this._container); 4047 }, 4048 4049 // @method mouseEventToLayerPoint(ev: MouseEvent): Point 4050 // Given a MouseEvent object, returns the pixel coordinate relative to 4051 // the [origin pixel](#map-getpixelorigin) where the event took place. 4052 mouseEventToLayerPoint: function (e) { 4053 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); 4054 }, 4055 4056 // @method mouseEventToLatLng(ev: MouseEvent): LatLng 4057 // Given a MouseEvent object, returns geographical coordinate where the 4058 // event took place. 4059 mouseEventToLatLng: function (e) { // (MouseEvent) 4060 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); 4061 }, 4062 4063 4064 // map initialization methods 4065 4066 _initContainer: function (id) { 4067 var container = this._container = get(id); 4068 4069 if (!container) { 4070 throw new Error('Map container not found.'); 4071 } else if (container._leaflet_id) { 4072 throw new Error('Map container is already initialized.'); 4073 } 4074 4075 on(container, 'scroll', this._onScroll, this); 4076 this._containerId = stamp(container); 4077 }, 4078 4079 _initLayout: function () { 4080 var container = this._container; 4081 4082 this._fadeAnimated = this.options.fadeAnimation &&
vendor: 9,382 bytes, lines 4082-4363
4082any3d; 4083 4084 addClass(container, 'leaflet-container' + 4085 (touch ? ' leaflet-touch' : '') + 4086 (retina ? ' leaflet-retina' : '') + 4087 (ielt9 ? ' leaflet-oldie' : '') + 4088 (safari ? ' leaflet-safari' : '') + 4089 (this._fadeAnimated ? ' leaflet-fade-anim' : '')); 4090 4091 var position = getStyle(container, 'position'); 4092 4093 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') { 4094 container.style.position = 'relative'; 4095 } 4096 4097 this._initPanes(); 4098 4099 if (this._initControlPos) { 4100 this._initControlPos(); 4101 } 4102 }, 4103 4104 _initPanes: function () { 4105 var panes = this._panes = {}; 4106 this._paneRenderers = {}; 4107 4108 // @section 4109 // 4110 // Panes are DOM elements used to control the ordering of layers on the map. You 4111 // can access panes with [`map.getPane`](#map-getpane) or 4112 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the 4113 // [`map.createPane`](#map-createpane) method. 4114 // 4115 // Every map has the following default panes that differ only in zIndex. 4116 // 4117 // @pane mapPane: HTMLElement = 'auto' 4118 // Pane that contains all other map panes 4119 4120 this._mapPane = this.createPane('mapPane', this._container); 4121 setPosition(this._mapPane, new Point(0, 0)); 4122 4123 // @pane tilePane: HTMLElement = 200 4124 // Pane for `GridLayer`s and `TileLayer`s 4125 this.createPane('tilePane'); 4126 // @pane overlayPane: HTMLElement = 400 4127 // Pane for overlay shadows (e.g. `Marker` shadows) 4128 this.createPane('shadowPane'); 4129 // @pane shadowPane: HTMLElement = 500 4130 // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s 4131 this.createPane('overlayPane'); 4132 // @pane markerPane: HTMLElement = 600 4133 // Pane for `Icon`s of `Marker`s 4134 this.createPane('markerPane'); 4135 // @pane tooltipPane: HTMLElement = 650 4136 // Pane for `Tooltip`s. 4137 this.createPane('tooltipPane'); 4138 // @pane popupPane: HTMLElement = 700 4139 // Pane for `Popup`s. 4140 this.createPane('popupPane'); 4141 4142 if (!this.options.markerZoomAnimation) { 4143 addClass(panes.markerPane, 'leaflet-zoom-hide'); 4144 addClass(panes.shadowPane, 'leaflet-zoom-hide'); 4145 } 4146 }, 4147 4148 4149 // private methods that modify map state 4150 4151 // @section Map state change events 4152 _resetView: function (center, zoom) { 4153 setPosition(this._mapPane, new Point(0, 0)); 4154 4155 var loading = !this._loaded; 4156 this._loaded = true; 4157 zoom = this._limitZoom(zoom); 4158 4159 this.fire('viewprereset'); 4160 4161 var zoomChanged = this._zoom !== zoom; 4162 this 4163 ._moveStart(zoomChanged, false) 4164 ._move(center, zoom) 4165 ._moveEnd(zoomChanged); 4166 4167 // @event viewreset: Event 4168 // Fired when the map needs to redraw its content (this usually happens 4169 // on map zoom or load). Very useful for creating custom overlays. 4170 this.fire('viewreset'); 4171 4172 // @event load: Event 4173 // Fired when the map is initialized (when its center and zoom are set 4174 // for the first time). 4175 if (loading) { 4176 this.fire('load'); 4177 } 4178 }, 4179 4180 _moveStart: function (zoomChanged, noMoveStart) { 4181 // @event zoomstart: Event 4182 // Fired when the map zoom is about to change (e.g. before zoom animation). 4183 // @event movestart: Event 4184 // Fired when the view of the map starts changing (e.g. user starts dragging the map). 4185 if (zoomChanged) { 4186 this.fire('zoomstart'); 4187 } 4188 if (!noMoveStart) { 4189 this.fire('movestart'); 4190 } 4191 return this; 4192 }, 4193 4194 _move: function (center, zoom, data) { 4195 if (zoom === undefined) { 4196 zoom = this._zoom; 4197 } 4198 var zoomChanged = this._zoom !== zoom; 4199 4200 this._zoom = zoom; 4201 this._lastCenter = center; 4202 this._pixelOrigin = this._getNewPixelOrigin(center); 4203 4204 // @event zoom: Event 4205 // Fired repeatedly during any change in zoom level, including zoom 4206 // and fly animations. 4207 if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530 4208 this.fire('zoom', data); 4209 } 4210 4211 // @event move: Event 4212 // Fired repeatedly during any movement of the map, including pan and 4213 // fly animations. 4214 return this.fire('move', data); 4215 }, 4216 4217 _moveEnd: function (zoomChanged) { 4218 // @event zoomend: Event 4219 // Fired when the map has changed, after any animations. 4220 if (zoomChanged) { 4221 this.fire('zoomend'); 4222 } 4223 4224 // @event moveend: Event 4225 // Fired when the center of the map stops changing (e.g. user stopped 4226 // dragging the map). 4227 return this.fire('moveend'); 4228 }, 4229 4230 _stop: function () { 4231 cancelAnimFrame(this._flyToFrame); 4232 if (this._panAnim) { 4233 this._panAnim.stop(); 4234 } 4235 return this; 4236 }, 4237 4238 _rawPanBy: function (offset) { 4239 setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); 4240 }, 4241 4242 _getZoomSpan: function () { 4243 return this.getMaxZoom() - this.getMinZoom(); 4244 }, 4245 4246 _panInsideMaxBounds: function () { 4247 if (!this._enforcingBounds) { 4248 this.panInsideBounds(this.options.maxBounds); 4249 } 4250 }, 4251 4252 _checkIfLoaded: function () { 4253 if (!this._loaded) { 4254 throw new Error('Set map center and zoom first.'); 4255 } 4256 }, 4257 4258 // DOM event handling 4259 4260 // @section Interaction events 4261 _initEvents: function (remove) { 4262 this._targets = {}; 4263 this._targets[stamp(this._container)] = this; 4264 4265 var onOff = remove ? off : on; 4266 4267 // @event click: MouseEvent 4268 // Fired when the user clicks (or taps) the map. 4269 // @event dblclick: MouseEvent 4270 // Fired when the user double-clicks (or double-taps) the map. 4271 // @event mousedown: MouseEvent 4272 // Fired when the user pushes the mouse button on the map. 4273 // @event mouseup: MouseEvent 4274 // Fired when the user releases the mouse button on the map. 4275 // @event mouseover: MouseEvent 4276 // Fired when the mouse enters the map. 4277 // @event mouseout: MouseEvent 4278 // Fired when the mouse leaves the map. 4279 // @event mousemove: MouseEvent 4280 // Fired while the mouse moves over the map. 4281 // @event contextmenu: MouseEvent 4282 // Fired when the user pushes the right mouse button on the map, prevents 4283 // default browser context menu from showing if there are listeners on 4284 // this event. Also fired on mobile when the user holds a single touch 4285 // for a second (also called long press). 4286 // @event keypress: KeyboardEvent 4287 // Fired when the user presses a key from the keyboard that produces a character value while the map is focused. 4288 // @event keydown: KeyboardEvent 4289 // Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event, 4290 // the `keydown` event is fired for keys that produce a character value and for keys 4291 // that do not produce a character value. 4292 // @event keyup: KeyboardEvent 4293 // Fired when the user releases a key from the keyboard while the map is focused. 4294 onOff(this._container, 'click dblclick mousedown mouseup ' + 4295 'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this); 4296 4297 if (this.options.trackResize) { 4298 onOff(window, 'resize', this._onResize, this); 4299 } 4300 4301 if (any3d && this.options.transform3DLimit) { 4302 (remove ? this.off : this.on).call(this, 'moveend', this._onMoveEnd); 4303 } 4304 }, 4305 4306 _onResize: function () { 4307 cancelAnimFrame(this._resizeRequest); 4308 this._resizeRequest = requestAnimFrame( 4309 function () { this.invalidateSize({debounceMoveend: true}); }, this); 4310 }, 4311 4312 _onScroll: function () { 4313 this._container.scrollTop = 0; 4314 this._container.scrollLeft = 0; 4315 }, 4316 4317 _onMoveEnd: function () { 4318 var pos = this._getMapPanePos(); 4319 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) { 4320 // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have 4321 // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/ 4322 this._resetView(this.getCenter(), this.getZoom()); 4323 } 4324 }, 4325 4326 _findEventTargets: function (e, type) { 4327 var targets = [], 4328 target, 4329 isHover = type === 'mouseout' || type === 'mouseover', 4330 src = e.target || e.srcElement, 4331 dragging = false; 4332 4333 while (src) { 4334 target = this._targets[stamp(src)]; 4335 if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) { 4336 // Prevent firing click after you just dragged an object. 4337 dragging = true; 4338 break; 4339 } 4340 if (target && target.listens(type, true)) { 4341 if (isHover && !isExternalTarget(src, e)) { break; } 4342 targets.push(target); 4343 if (isHover) { break; } 4344 } 4345 if (src === this._container) { break; } 4346 src = src.parentNode; 4347 } 4348 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) { 4349 targets = [this]; 4350 } 4351 return targets; 4352 }, 4353 4354 _handleDOMEvent: function (e) { 4355 if (!this._loaded || skipped(e)) { return; } 4356 4357 var type = e.type; 4358 4359 if (type === 'mousedown' || type === 'keypress' || type === 'keyup' || type === 'keydown') { 4360 // prevents outline when clicking on keyboard-focusable element 4361 preventOutline(e.target || e.srcElement); 4362 } 4363
4364 this._fireDOMEvent(e, type); 4365 }, 4366 4367 _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'], 4368 4369 _fireDOMEvent: function (e, type, targets) { 4370 4371 if (e.type === 'click') { 4372 // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups). 4373 // @event preclick: MouseEvent 4374 // Fired before mouse click on the map (sometimes useful when you 4375 // want something to happen on click before any existing click 4376 // handlers start running). 4377 var synth = extend({}, e); 4378 synth.type = 'preclick'; 4379 this._fireDOMEvent(synth, synth.type, targets); 4380 } 4381 4382 if (e._stopped) { return; } 4383 4384 // Find the layer the event is propagating from and its parents. 4385 targets = (targets || []).concat(this._findEventTargets(e, type)); 4386 4387 if (!targets.length) { return; } 4388 4389 var target = targets[0]; 4390 if (type === 'contextmenu' && target.listens(type, true)) { 4391 preventDefault(e); 4392 } 4393 4394 var data = { 4395 originalEvent: e 4396 }; 4397 4398 if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') { 4399 var isMarker = target.getLatLng && (!target._radius || target._radius <= 10); 4400 data.containerPoint = isMarker ? 4401 this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e); 4402 data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); 4403 data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint); 4404 } 4405 4406 for (var i = 0; i < targets.length; i++) { 4407 targets[i].fire(type, data, true); 4408 if (data.originalEvent._stopped || 4409 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; } 4410 } 4411 }, 4412 4413 _draggableMoved: function (obj) { 4414 obj = obj.dragging && obj.dragging.enabled() ? obj : this; 4415 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved()); 4416 }, 4417 4418 _clearHandlers: function () { 4419 for (var i = 0, len = this._handlers.length; i < len; i++) { 4420 this._handlers[i].disable(); 4421 } 4422 }, 4423 4424 // @section Other Methods 4425 4426 // @method whenReady(fn: Function, context?: Object): this 4427 // Runs the given function `fn` when the map gets initialized with 4428 // a view (center and zoom) and at least one layer, or immediately 4429 // if it's already initialized, optionally passing a function context. 4430 whenReady: function (callback, context) { 4431 if (this._loaded) { 4432 callback.call(context || this, {target: this}); 4433 } else { 4434 this.on('load', callback, context); 4435 } 4436 return this; 4437 }, 4438 4439 4440 // private methods for getting map state 4441 4442 _getMapPanePos: function () { 4443 return getPosition(this._mapPane) || new Point(0, 0); 4444 }, 4445 4446 _moved: function () { 4447 var pos = this._getMapPanePos(); 4448 return pos && !pos.equals([0, 0]); 4449 }, 4450 4451 _getTopLeftPoint: function (center, zoom) { 4452 var pixelOrigin = center && zoom !== undefined ? 4453 this._getNewPixelOrigin(center, zoom) : 4454 this.getPixelOrigin(); 4455 return pixelOrigin.subtract(this._getMapPanePos()); 4456 }, 4457 4458 _getNewPixelOrigin: function (center, zoom) { 4459 var viewHalf = this.getSize()._divideBy(2); 4460 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round(); 4461 }, 4462 4463 _latLngToNewLayerPoint: function (latlng, zoom, center) { 4464 var topLeft = this._getNewPixelOrigin(center, zoom); 4465 return this.project(latlng, zoom)._subtract(topLeft); 4466 }, 4467 4468 _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) { 4469 var topLeft = this._getNewPixelOrigin(center, zoom); 4470 return toBounds([
vendor: 43,530 bytes, lines 4471-5916
4471 this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft), 4472 this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft), 4473 this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft), 4474 this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft) 4475 ]); 4476 }, 4477 4478 // layer point of the current center 4479 _getCenterLayerPoint: function () { 4480 return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); 4481 }, 4482 4483 // offset of the specified place to the current center in pixels 4484 _getCenterOffset: function (latlng) { 4485 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); 4486 }, 4487 4488 // adjust center for view to get inside bounds 4489 _limitCenter: function (center, zoom, bounds) { 4490 4491 if (!bounds) { return center; } 4492 4493 var centerPoint = this.project(center, zoom), 4494 viewHalf = this.getSize().divideBy(2), 4495 viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), 4496 offset = this._getBoundsOffset(viewBounds, bounds, zoom); 4497 4498 // If offset is less than a pixel, ignore. 4499 // This prevents unstable projections from getting into 4500 // an infinite loop of tiny offsets. 4501 if (offset.round().equals([0, 0])) { 4502 return center; 4503 } 4504 4505 return this.unproject(centerPoint.add(offset), zoom); 4506 }, 4507 4508 // adjust offset for view to get inside bounds 4509 _limitOffset: function (offset, bounds) { 4510 if (!bounds) { return offset; } 4511 4512 var viewBounds = this.getPixelBounds(), 4513 newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); 4514 4515 return offset.add(this._getBoundsOffset(newBounds, bounds)); 4516 }, 4517 4518 // returns offset needed for pxBounds to get inside maxBounds at a specified zoom 4519 _getBoundsOffset: function (pxBounds, maxBounds, zoom) { 4520 var projectedMaxBounds = toBounds( 4521 this.project(maxBounds.getNorthEast(), zoom), 4522 this.project(maxBounds.getSouthWest(), zoom) 4523 ), 4524 minOffset = projectedMaxBounds.min.subtract(pxBounds.min), 4525 maxOffset = projectedMaxBounds.max.subtract(pxBounds.max), 4526 4527 dx = this._rebound(minOffset.x, -maxOffset.x), 4528 dy = this._rebound(minOffset.y, -maxOffset.y); 4529 4530 return new Point(dx, dy); 4531 }, 4532 4533 _rebound: function (left, right) { 4534 return left + right > 0 ? 4535 Math.round(left - right) / 2 : 4536 Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); 4537 }, 4538 4539 _limitZoom: function (zoom) { 4540 var min = this.getMinZoom(), 4541 max = this.getMaxZoom(), 4542 snap = any3d ? this.options.zoomSnap : 1; 4543 if (snap) { 4544 zoom = Math.round(zoom / snap) * snap; 4545 } 4546 return Math.max(min, Math.min(max, zoom)); 4547 }, 4548 4549 _onPanTransitionStep: function () { 4550 this.fire('move'); 4551 }, 4552 4553 _onPanTransitionEnd: function () { 4554 removeClass(this._mapPane, 'leaflet-pan-anim'); 4555 this.fire('moveend'); 4556 }, 4557 4558 _tryAnimatedPan: function (center, options) { 4559 // difference between the new and current centers in pixels 4560 var offset = this._getCenterOffset(center)._trunc(); 4561 4562 // don't animate too far unless animate: true specified in options 4563 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; } 4564 4565 this.panBy(offset, options); 4566 4567 return true; 4568 }, 4569 4570 _createAnimProxy: function () { 4571 4572 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated'); 4573 this._panes.mapPane.appendChild(proxy); 4574 4575 this.on('zoomanim', function (e) { 4576 var prop = TRANSFORM, 4577 transform = this._proxy.style[prop]; 4578 4579 setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); 4580 4581 // workaround for case when transform is the same and so transitionend event is not fired 4582 if (transform === this._proxy.style[prop] && this._animatingZoom) { 4583 this._onZoomTransitionEnd(); 4584 } 4585 }, this); 4586 4587 this.on('load moveend', this._animMoveEnd, this); 4588 4589 this._on('unload', this._destroyAnimProxy, this); 4590 }, 4591 4592 _destroyAnimProxy: function () { 4593 remove(this._proxy); 4594 this.off('load moveend', this._animMoveEnd, this); 4595 delete this._proxy; 4596 }, 4597 4598 _animMoveEnd: function () { 4599 var c = this.getCenter(), 4600 z = this.getZoom(); 4601 setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1)); 4602 }, 4603 4604 _catchTransitionEnd: function (e) { 4605 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) { 4606 this._onZoomTransitionEnd(); 4607 } 4608 }, 4609 4610 _nothingToAnimate: function () { 4611 return !this._container.getElementsByClassName('leaflet-zoom-animated').length; 4612 }, 4613 4614 _tryAnimatedZoom: function (center, zoom, options) { 4615 4616 if (this._animatingZoom) { return true; } 4617 4618 options = options || {}; 4619 4620 // don't animate if disabled, not supported or zoom difference is too large 4621 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || 4622 Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; } 4623 4624 // offset is the pixel coords of the zoom origin relative to the current center 4625 var scale = this.getZoomScale(zoom), 4626 offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale); 4627 4628 // don't animate if the zoom origin isn't within one screen from the current center, unless forced 4629 if (options.animate !== true && !this.getSize().contains(offset)) { return false; } 4630 4631 requestAnimFrame(function () { 4632 this 4633 ._moveStart(true, false) 4634 ._animateZoom(center, zoom, true); 4635 }, this); 4636 4637 return true; 4638 }, 4639 4640 _animateZoom: function (center, zoom, startAnim, noUpdate) { 4641 if (!this._mapPane) { return; } 4642 4643 if (startAnim) { 4644 this._animatingZoom = true; 4645 4646 // remember what center/zoom to set after animation 4647 this._animateToCenter = center; 4648 this._animateToZoom = zoom; 4649 4650 addClass(this._mapPane, 'leaflet-zoom-anim'); 4651 } 4652 4653 // @section Other Events 4654 // @event zoomanim: ZoomAnimEvent 4655 // Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom. 4656 this.fire('zoomanim', { 4657 center: center, 4658 zoom: zoom, 4659 noUpdate: noUpdate 4660 }); 4661 4662 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693 4663 setTimeout(bind(this._onZoomTransitionEnd, this), 250); 4664 }, 4665 4666 _onZoomTransitionEnd: function () { 4667 if (!this._animatingZoom) { return; } 4668 4669 if (this._mapPane) { 4670 removeClass(this._mapPane, 'leaflet-zoom-anim'); 4671 } 4672 4673 this._animatingZoom = false; 4674 4675 this._move(this._animateToCenter, this._animateToZoom); 4676 4677 // This anim frame should prevent an obscure iOS webkit tile loading race condition. 4678 requestAnimFrame(function () { 4679 this._moveEnd(true); 4680 }, this); 4681 } 4682 }); 4683 4684 // @section 4685 4686 // @factory L.map(id: String, options?: Map options) 4687 // Instantiates a map object given the DOM ID of a `<div>` element 4688 // and optionally an object literal with `Map options`. 4689 // 4690 // @alternative 4691 // @factory L.map(el: HTMLElement, options?: Map options) 4692 // Instantiates a map object given an instance of a `<div>` HTML element 4693 // and optionally an object literal with `Map options`. 4694 function createMap(id, options) { 4695 return new Map(id, options); 4696 } 4697 4698 /* 4699 * @class Control 4700 * @aka L.Control 4701 * @inherits Class 4702 * 4703 * L.Control is a base class for implementing map controls. Handles positioning. 4704 * All other controls extend from this class. 4705 */ 4706 4707 var Control = Class.extend({ 4708 // @section 4709 // @aka Control options 4710 options: { 4711 // @option position: String = 'topright' 4712 // The position of the control (one of the map corners). Possible values are `'topleft'`, 4713 // `'topright'`, `'bottomleft'` or `'bottomright'` 4714 position: 'topright' 4715 }, 4716 4717 initialize: function (options) { 4718 setOptions(this, options); 4719 }, 4720 4721 /* @section 4722 * Classes extending L.Control will inherit the following methods: 4723 * 4724 * @method getPosition: string 4725 * Returns the position of the control. 4726 */ 4727 getPosition: function () { 4728 return this.options.position; 4729 }, 4730 4731 // @method setPosition(position: string): this 4732 // Sets the position of the control. 4733 setPosition: function (position) { 4734 var map = this._map; 4735 4736 if (map) { 4737 map.removeControl(this); 4738 } 4739 4740 this.options.position = position; 4741 4742 if (map) { 4743 map.addControl(this); 4744 } 4745 4746 return this; 4747 }, 4748 4749 // @method getContainer: HTMLElement 4750 // Returns the HTMLElement that contains the control. 4751 getContainer: function () { 4752 return this._container; 4753 }, 4754 4755 // @method addTo(map: Map): this 4756 // Adds the control to the given map. 4757 addTo: function (map) { 4758 this.remove(); 4759 this._map = map; 4760 4761 var container = this._container = this.onAdd(map), 4762 pos = this.getPosition(), 4763 corner = map._controlCorners[pos]; 4764 4765 addClass(container, 'leaflet-control'); 4766 4767 if (pos.indexOf('bottom') !== -1) { 4768 corner.insertBefore(container, corner.firstChild); 4769 } else { 4770 corner.appendChild(container); 4771 } 4772 4773 this._map.on('unload', this.remove, this); 4774 4775 return this; 4776 }, 4777 4778 // @method remove: this 4779 // Removes the control from the map it is currently active on. 4780 remove: function () { 4781 if (!this._map) { 4782 return this; 4783 } 4784 4785 remove(this._container); 4786 4787 if (this.onRemove) { 4788 this.onRemove(this._map); 4789 } 4790 4791 this._map.off('unload', this.remove, this); 4792 this._map = null; 4793 4794 return this; 4795 }, 4796 4797 _refocusOnMap: function (e) { 4798 // if map exists and event is not a keyboard event 4799 if (this._map && e && e.screenX > 0 && e.screenY > 0) { 4800 this._map.getContainer().focus(); 4801 } 4802 } 4803 }); 4804 4805 var control = function (options) { 4806 return new Control(options); 4807 }; 4808 4809 /* @section Extension methods 4810 * @uninheritable 4811 * 4812 * Every control should extend from `L.Control` and (re-)implement the following methods. 4813 * 4814 * @method onAdd(map: Map): HTMLElement 4815 * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo). 4816 * 4817 * @method onRemove(map: Map) 4818 * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove). 4819 */ 4820 4821 /* @namespace Map 4822 * @section Methods for Layers and Controls 4823 */ 4824 Map.include({ 4825 // @method addControl(control: Control): this 4826 // Adds the given control to the map 4827 addControl: function (control) { 4828 control.addTo(this); 4829 return this; 4830 }, 4831 4832 // @method removeControl(control: Control): this 4833 // Removes the given control from the map 4834 removeControl: function (control) { 4835 control.remove(); 4836 return this; 4837 }, 4838 4839 _initControlPos: function () { 4840 var corners = this._controlCorners = {}, 4841 l = 'leaflet-', 4842 container = this._controlContainer = 4843 create$1('div', l + 'control-container', this._container); 4844 4845 function createCorner(vSide, hSide) { 4846 var className = l + vSide + ' ' + l + hSide; 4847 4848 corners[vSide + hSide] = create$1('div', className, container); 4849 } 4850 4851 createCorner('top', 'left'); 4852 createCorner('top', 'right'); 4853 createCorner('bottom', 'left'); 4854 createCorner('bottom', 'right'); 4855 }, 4856 4857 _clearControlPos: function () { 4858 for (var i in this._controlCorners) { 4859 remove(this._controlCorners[i]); 4860 } 4861 remove(this._controlContainer); 4862 delete this._controlCorners; 4863 delete this._controlContainer; 4864 } 4865 }); 4866 4867 /* 4868 * @class Control.Layers 4869 * @aka L.Control.Layers 4870 * @inherits Control 4871 * 4872 * 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`. 4873 * 4874 * @example 4875 * 4876 * ```js 4877 * var baseLayers = { 4878 * "Mapbox": mapbox, 4879 * "OpenStreetMap": osm 4880 * }; 4881 * 4882 * var overlays = { 4883 * "Marker": marker, 4884 * "Roads": roadsLayer 4885 * }; 4886 * 4887 * L.control.layers(baseLayers, overlays).addTo(map); 4888 * ``` 4889 * 4890 * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values: 4891 * 4892 * ```js 4893 * { 4894 * "<someName1>": layer1, 4895 * "<someName2>": layer2 4896 * } 4897 * ``` 4898 * 4899 * The layer names can contain HTML, which allows you to add additional styling to the items: 4900 * 4901 * ```js 4902 * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer} 4903 * ``` 4904 */ 4905 4906 var Layers = Control.extend({ 4907 // @section 4908 // @aka Control.Layers options 4909 options: { 4910 // @option collapsed: Boolean = true 4911 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch. 4912 collapsed: true, 4913 position: 'topright', 4914 4915 // @option autoZIndex: Boolean = true 4916 // 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. 4917 autoZIndex: true, 4918 4919 // @option hideSingleBase: Boolean = false 4920 // If `true`, the base layers in the control will be hidden when there is only one. 4921 hideSingleBase: false, 4922 4923 // @option sortLayers: Boolean = false 4924 // Whether to sort the layers. When `false`, layers will keep the order 4925 // in which they were added to the control. 4926 sortLayers: false, 4927 4928 // @option sortFunction: Function = * 4929 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 4930 // that will be used for sorting the layers, when `sortLayers` is `true`. 4931 // The function receives both the `L.Layer` instances and their names, as in 4932 // `sortFunction(layerA, layerB, nameA, nameB)`. 4933 // By default, it sorts layers alphabetically by their name. 4934 sortFunction: function (layerA, layerB, nameA, nameB) { 4935 return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0); 4936 } 4937 }, 4938 4939 initialize: function (baseLayers, overlays, options) { 4940 setOptions(this, options); 4941 4942 this._layerControlInputs = []; 4943 this._layers = []; 4944 this._lastZIndex = 0; 4945 this._handlingClick = false; 4946 4947 for (var i in baseLayers) { 4948 this._addLayer(baseLayers[i], i); 4949 } 4950 4951 for (i in overlays) { 4952 this._addLayer(overlays[i], i, true); 4953 } 4954 }, 4955 4956 onAdd: function (map) { 4957 this._initLayout(); 4958 this._update(); 4959 4960 this._map = map; 4961 map.on('zoomend', this._checkDisabledLayers, this); 4962 4963 for (var i = 0; i < this._layers.length; i++) { 4964 this._layers[i].layer.on('add remove', this._onLayerChange, this); 4965 } 4966 4967 return this._container; 4968 }, 4969 4970 addTo: function (map) { 4971 Control.prototype.addTo.call(this, map); 4972 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height. 4973 return this._expandIfNotCollapsed(); 4974 }, 4975 4976 onRemove: function () { 4977 this._map.off('zoomend', this._checkDisabledLayers, this); 4978 4979 for (var i = 0; i < this._layers.length; i++) { 4980 this._layers[i].layer.off('add remove', this._onLayerChange, this); 4981 } 4982 }, 4983 4984 // @method addBaseLayer(layer: Layer, name: String): this 4985 // Adds a base layer (radio button entry) with the given name to the control. 4986 addBaseLayer: function (layer, name) { 4987 this._addLayer(layer, name); 4988 return (this._map) ? this._update() : this; 4989 }, 4990 4991 // @method addOverlay(layer: Layer, name: String): this 4992 // Adds an overlay (checkbox entry) with the given name to the control. 4993 addOverlay: function (layer, name) { 4994 this._addLayer(layer, name, true); 4995 return (this._map) ? this._update() : this; 4996 }, 4997 4998 // @method removeLayer(layer: Layer): this 4999 // Remove the given layer from the control. 5000 removeLayer: function (layer) { 5001 layer.off('add remove', this._onLayerChange, this); 5002 5003 var obj = this._getLayer(stamp(layer)); 5004 if (obj) { 5005 this._layers.splice(this._layers.indexOf(obj), 1); 5006 } 5007 return (this._map) ? this._update() : this; 5008 }, 5009 5010 // @method expand(): this 5011 // Expand the control container if collapsed. 5012 expand: function () { 5013 addClass(this._container, 'leaflet-control-layers-expanded'); 5014 this._section.style.height = null; 5015 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50); 5016 if (acceptableHeight < this._section.clientHeight) { 5017 addClass(this._section, 'leaflet-control-layers-scrollbar'); 5018 this._section.style.height = acceptableHeight + 'px'; 5019 } else { 5020 removeClass(this._section, 'leaflet-control-layers-scrollbar'); 5021 } 5022 this._checkDisabledLayers(); 5023 return this; 5024 }, 5025 5026 // @method collapse(): this 5027 // Collapse the control container if expanded. 5028 collapse: function () { 5029 removeClass(this._container, 'leaflet-control-layers-expanded'); 5030 return this; 5031 }, 5032 5033 _initLayout: function () { 5034 var className = 'leaflet-control-layers', 5035 container = this._container = create$1('div', className), 5036 collapsed = this.options.collapsed; 5037 5038 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released 5039 container.setAttribute('aria-haspopup', true); 5040 5041 disableClickPropagation(container); 5042 disableScrollPropagation(container); 5043 5044 var section = this._section = create$1('section', className + '-list'); 5045 5046 if (collapsed) { 5047 this._map.on('click', this.collapse, this); 5048 5049 if (!android) { 5050 on(container, { 5051 mouseenter: this.expand, 5052 mouseleave: this.collapse 5053 }, this); 5054 } 5055 } 5056 5057 var link = this._layersLink = create$1('a', className + '-toggle', container); 5058 link.href = '#'; 5059 link.title = 'Layers'; 5060 5061 if (touch) { 5062 on(link, 'click', stop); 5063 on(link, 'click', this.expand, this); 5064 } else { 5065 on(link, 'focus', this.expand, this); 5066 } 5067 5068 if (!collapsed) { 5069 this.expand(); 5070 } 5071 5072 this._baseLayersList = create$1('div', className + '-base', section); 5073 this._separator = create$1('div', className + '-separator', section); 5074 this._overlaysList = create$1('div', className + '-overlays', section); 5075 5076 container.appendChild(section); 5077 }, 5078 5079 _getLayer: function (id) { 5080 for (var i = 0; i < this._layers.length; i++) { 5081 5082 if (this._layers[i] && stamp(this._layers[i].layer) === id) { 5083 return this._layers[i]; 5084 } 5085 } 5086 }, 5087 5088 _addLayer: function (layer, name, overlay) { 5089 if (this._map) { 5090 layer.on('add remove', this._onLayerChange, this); 5091 } 5092 5093 this._layers.push({ 5094 layer: layer, 5095 name: name, 5096 overlay: overlay 5097 }); 5098 5099 if (this.options.sortLayers) { 5100 this._layers.sort(bind(function (a, b) { 5101 return this.options.sortFunction(a.layer, b.layer, a.name, b.name); 5102 }, this)); 5103 } 5104 5105 if (this.options.autoZIndex && layer.setZIndex) { 5106 this._lastZIndex++; 5107 layer.setZIndex(this._lastZIndex); 5108 } 5109 5110 this._expandIfNotCollapsed(); 5111 }, 5112 5113 _update: function () { 5114 if (!this._container) { return this; } 5115 5116 empty(this._baseLayersList); 5117 empty(this._overlaysList); 5118 5119 this._layerControlInputs = []; 5120 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0; 5121 5122 for (i = 0; i < this._layers.length; i++) { 5123 obj = this._layers[i]; 5124 this._addItem(obj); 5125 overlaysPresent = overlaysPresent || obj.overlay; 5126 baseLayersPresent = baseLayersPresent || !obj.overlay; 5127 baseLayersCount += !obj.overlay ? 1 : 0; 5128 } 5129 5130 // Hide base layers section if there's only one layer. 5131 if (this.options.hideSingleBase) { 5132 baseLayersPresent = baseLayersPresent && baseLayersCount > 1; 5133 this._baseLayersList.style.display = baseLayersPresent ? '' : 'none'; 5134 } 5135 5136 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; 5137 5138 return this; 5139 }, 5140 5141 _onLayerChange: function (e) { 5142 if (!this._handlingClick) { 5143 this._update(); 5144 } 5145 5146 var obj = this._getLayer(stamp(e.target)); 5147 5148 // @namespace Map 5149 // @section Layer events 5150 // @event baselayerchange: LayersControlEvent 5151 // Fired when the base layer is changed through the [layers control](#control-layers). 5152 // @event overlayadd: LayersControlEvent 5153 // Fired when an overlay is selected through the [layers control](#control-layers). 5154 // @event overlayremove: LayersControlEvent 5155 // Fired when an overlay is deselected through the [layers control](#control-layers). 5156 // @namespace Control.Layers 5157 var type = obj.overlay ? 5158 (e.type === 'add' ? 'overlayadd' : 'overlayremove') : 5159 (e.type === 'add' ? 'baselayerchange' : null); 5160 5161 if (type) { 5162 this._map.fire(type, obj); 5163 } 5164 }, 5165 5166 // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) 5167 _createRadioElement: function (name, checked) { 5168 5169 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' + 5170 name + '"' + (checked ? ' checked="checked"' : '') + '/>'; 5171 5172 var radioFragment = document.createElement('div'); 5173 radioFragment.innerHTML = radioHtml; 5174 5175 return radioFragment.firstChild; 5176 }, 5177 5178 _addItem: function (obj) { 5179 var label = document.createElement('label'), 5180 checked = this._map.hasLayer(obj.layer), 5181 input; 5182 5183 if (obj.overlay) { 5184 input = document.createElement('input'); 5185 input.type = 'checkbox'; 5186 input.className = 'leaflet-control-layers-selector'; 5187 input.defaultChecked = checked; 5188 } else { 5189 input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked); 5190 } 5191 5192 this._layerControlInputs.push(input); 5193 input.layerId = stamp(obj.layer); 5194 5195 on(input, 'click', this._onInputClick, this); 5196 5197 var name = document.createElement('span'); 5198 name.innerHTML = ' ' + obj.name; 5199 5200 // Helps from preventing layer control flicker when checkboxes are disabled 5201 // https://github.com/Leaflet/Leaflet/issues/2771 5202 var holder = document.createElement('div'); 5203 5204 label.appendChild(holder); 5205 holder.appendChild(input); 5206 holder.appendChild(name); 5207 5208 var container = obj.overlay ? this._overlaysList : this._baseLayersList; 5209 container.appendChild(label); 5210 5211 this._checkDisabledLayers(); 5212 return label; 5213 }, 5214 5215 _onInputClick: function () { 5216 var inputs = this._layerControlInputs, 5217 input, layer; 5218 var addedLayers = [], 5219 removedLayers = []; 5220 5221 this._handlingClick = true; 5222 5223 for (var i = inputs.length - 1; i >= 0; i--) { 5224 input = inputs[i]; 5225 layer = this._getLayer(input.layerId).layer; 5226 5227 if (input.checked) { 5228 addedLayers.push(layer); 5229 } else if (!input.checked) { 5230 removedLayers.push(layer); 5231 } 5232 } 5233 5234 // Bugfix issue 2318: Should remove all old layers before readding new ones 5235 for (i = 0; i < removedLayers.length; i++) { 5236 if (this._map.hasLayer(removedLayers[i])) { 5237 this._map.removeLayer(removedLayers[i]); 5238 } 5239 } 5240 for (i = 0; i < addedLayers.length; i++) { 5241 if (!this._map.hasLayer(addedLayers[i])) { 5242 this._map.addLayer(addedLayers[i]); 5243 } 5244 } 5245 5246 this._handlingClick = false; 5247 5248 this._refocusOnMap(); 5249 }, 5250 5251 _checkDisabledLayers: function () { 5252 var inputs = this._layerControlInputs, 5253 input, 5254 layer, 5255 zoom = this._map.getZoom(); 5256 5257 for (var i = inputs.length - 1; i >= 0; i--) { 5258 input = inputs[i]; 5259 layer = this._getLayer(input.layerId).layer; 5260 input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) || 5261 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom); 5262 5263 } 5264 }, 5265 5266 _expandIfNotCollapsed: function () { 5267 if (this._map && !this.options.collapsed) { 5268 this.expand(); 5269 } 5270 return this; 5271 }, 5272 5273 _expand: function () { 5274 // Backward compatibility, remove me in 1.1. 5275 return this.expand(); 5276 }, 5277 5278 _collapse: function () { 5279 // Backward compatibility, remove me in 1.1. 5280 return this.collapse(); 5281 } 5282 5283 }); 5284 5285 5286 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options) 5287 // Creates a layers 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. 5288 var layers = function (baseLayers, overlays, options) { 5289 return new Layers(baseLayers, overlays, options); 5290 }; 5291 5292 /* 5293 * @class Control.Zoom 5294 * @aka L.Control.Zoom 5295 * @inherits Control 5296 * 5297 * 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`. 5298 */ 5299 5300 var Zoom = Control.extend({ 5301 // @section 5302 // @aka Control.Zoom options 5303 options: { 5304 position: 'topleft', 5305 5306 // @option zoomInText: String = '+' 5307 // The text set on the 'zoom in' button. 5308 zoomInText: '+', 5309 5310 // @option zoomInTitle: String = 'Zoom in' 5311 // The title set on the 'zoom in' button. 5312 zoomInTitle: 'Zoom in', 5313 5314 // @option zoomOutText: String = '−' 5315 // The text set on the 'zoom out' button. 5316 zoomOutText: '−', 5317 5318 // @option zoomOutTitle: String = 'Zoom out' 5319 // The title set on the 'zoom out' button. 5320 zoomOutTitle: 'Zoom out' 5321 }, 5322 5323 onAdd: function (map) { 5324 var zoomName = 'leaflet-control-zoom', 5325 container = create$1('div', zoomName + ' leaflet-bar'), 5326 options = this.options; 5327 5328 this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle, 5329 zoomName + '-in', container, this._zoomIn); 5330 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle, 5331 zoomName + '-out', container, this._zoomOut); 5332 5333 this._updateDisabled(); 5334 map.on('zoomend zoomlevelschange', this._updateDisabled, this); 5335 5336 return container; 5337 }, 5338 5339 onRemove: function (map) { 5340 map.off('zoomend zoomlevelschange', this._updateDisabled, this); 5341 }, 5342 5343 disable: function () { 5344 this._disabled = true; 5345 this._updateDisabled(); 5346 return this; 5347 }, 5348 5349 enable: function () { 5350 this._disabled = false; 5351 this._updateDisabled(); 5352 return this; 5353 }, 5354 5355 _zoomIn: function (e) { 5356 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) { 5357 this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); 5358 } 5359 }, 5360 5361 _zoomOut: function (e) { 5362 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) { 5363 this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1)); 5364 } 5365 }, 5366 5367 _createButton: function (html, title, className, container, fn) { 5368 var link = create$1('a', className, container); 5369 link.innerHTML = html; 5370 link.href = '#'; 5371 link.title = title; 5372 5373 /* 5374 * Will force screen readers like VoiceOver to read this as "Zoom in - button" 5375 */ 5376 link.setAttribute('role', 'button'); 5377 link.setAttribute('aria-label', title); 5378 5379 disableClickPropagation(link); 5380 on(link, 'click', stop); 5381 on(link, 'click', fn, this); 5382 on(link, 'click', this._refocusOnMap, this); 5383 5384 return link; 5385 }, 5386 5387 _updateDisabled: function () { 5388 var map = this._map, 5389 className = 'leaflet-disabled'; 5390 5391 removeClass(this._zoomInButton, className); 5392 removeClass(this._zoomOutButton, className); 5393 5394 if (this._disabled || map._zoom === map.getMinZoom()) { 5395 addClass(this._zoomOutButton, className); 5396 } 5397 if (this._disabled || map._zoom === map.getMaxZoom()) { 5398 addClass(this._zoomInButton, className); 5399 } 5400 } 5401 }); 5402 5403 // @namespace Map 5404 // @section Control options 5405 // @option zoomControl: Boolean = true 5406 // Whether a [zoom control](#control-zoom) is added to the map by default. 5407 Map.mergeOptions({ 5408 zoomControl: true 5409 }); 5410 5411 Map.addInitHook(function () { 5412 if (this.options.zoomControl) { 5413 // @section Controls 5414 // @property zoomControl: Control.Zoom 5415 // The default zoom control (only available if the 5416 // [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map). 5417 this.zoomControl = new Zoom(); 5418 this.addControl(this.zoomControl); 5419 } 5420 }); 5421 5422 // @namespace Control.Zoom 5423 // @factory L.control.zoom(options: Control.Zoom options) 5424 // Creates a zoom control 5425 var zoom = function (options) { 5426 return new Zoom(options); 5427 }; 5428 5429 /* 5430 * @class Control.Scale 5431 * @aka L.Control.Scale 5432 * @inherits Control 5433 * 5434 * 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`. 5435 * 5436 * @example 5437 * 5438 * ```js 5439 * L.control.scale().addTo(map); 5440 * ``` 5441 */ 5442 5443 var Scale = Control.extend({ 5444 // @section 5445 // @aka Control.Scale options 5446 options: { 5447 position: 'bottomleft', 5448 5449 // @option maxWidth: Number = 100 5450 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500). 5451 maxWidth: 100, 5452 5453 // @option metric: Boolean = True 5454 // Whether to show the metric scale line (m/km). 5455 metric: true, 5456 5457 // @option imperial: Boolean = True 5458 // Whether to show the imperial scale line (mi/ft). 5459 imperial: true 5460 5461 // @option updateWhenIdle: Boolean = false 5462 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)). 5463 }, 5464 5465 onAdd: function (map) { 5466 var className = 'leaflet-control-scale', 5467 container = create$1('div', className), 5468 options = this.options; 5469 5470 this._addScales(options, className + '-line', container); 5471 5472 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this); 5473 map.whenReady(this._update, this); 5474 5475 return container; 5476 }, 5477 5478 onRemove: function (map) { 5479 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this); 5480 }, 5481 5482 _addScales: function (options, className, container) { 5483 if (options.metric) { 5484 this._mScale = create$1('div', className, container); 5485 } 5486 if (options.imperial) { 5487 this._iScale = create$1('div', className, container); 5488 } 5489 }, 5490 5491 _update: function () { 5492 var map = this._map, 5493 y = map.getSize().y / 2; 5494 5495 var maxMeters = map.distance( 5496 map.containerPointToLatLng([0, y]), 5497 map.containerPointToLatLng([this.options.maxWidth, y])); 5498 5499 this._updateScales(maxMeters); 5500 }, 5501 5502 _updateScales: function (maxMeters) { 5503 if (this.options.metric && maxMeters) { 5504 this._updateMetric(maxMeters); 5505 } 5506 if (this.options.imperial && maxMeters) { 5507 this._updateImperial(maxMeters); 5508 } 5509 }, 5510 5511 _updateMetric: function (maxMeters) { 5512 var meters = this._getRoundNum(maxMeters), 5513 label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km'; 5514 5515 this._updateScale(this._mScale, label, meters / maxMeters); 5516 }, 5517 5518 _updateImperial: function (maxMeters) { 5519 var maxFeet = maxMeters * 3.2808399, 5520 maxMiles, miles, feet; 5521 5522 if (maxFeet > 5280) { 5523 maxMiles = maxFeet / 5280; 5524 miles = this._getRoundNum(maxMiles); 5525 this._updateScale(this._iScale, miles + ' mi', miles / maxMiles); 5526 5527 } else { 5528 feet = this._getRoundNum(maxFeet); 5529 this._updateScale(this._iScale, feet + ' ft', feet / maxFeet); 5530 } 5531 }, 5532 5533 _updateScale: function (scale, text, ratio) { 5534 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px'; 5535 scale.innerHTML = text; 5536 }, 5537 5538 _getRoundNum: function (num) { 5539 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1), 5540 d = num / pow10; 5541 5542 d = d >= 10 ? 10 : 5543 d >= 5 ? 5 : 5544 d >= 3 ? 3 : 5545 d >= 2 ? 2 : 1; 5546 5547 return pow10 * d; 5548 } 5549 }); 5550 5551 5552 // @factory L.control.scale(options?: Control.Scale options) 5553 // Creates an scale control with the given options. 5554 var scale = function (options) { 5555 return new Scale(options); 5556 }; 5557 5558 /* 5559 * @class Control.Attribution 5560 * @aka L.Control.Attribution 5561 * @inherits Control 5562 * 5563 * 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. 5564 */ 5565 5566 var Attribution = Control.extend({ 5567 // @section 5568 // @aka Control.Attribution options 5569 options: { 5570 position: 'bottomright', 5571 5572 // @option prefix: String = 'Leaflet' 5573 // The HTML text shown before the attributions. Pass `false` to disable. 5574 prefix: '<a href="https://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>' 5575 }, 5576 5577 initialize: function (options) { 5578 setOptions(this, options); 5579 5580 this._attributions = {}; 5581 }, 5582 5583 onAdd: function (map) { 5584 map.attributionControl = this; 5585 this._container = create$1('div', 'leaflet-control-attribution'); 5586 disableClickPropagation(this._container); 5587 5588 // TODO ugly, refactor 5589 for (var i in map._layers) { 5590 if (map._layers[i].getAttribution) { 5591 this.addAttribution(map._layers[i].getAttribution()); 5592 } 5593 } 5594 5595 this._update(); 5596 5597 return this._container; 5598 }, 5599 5600 // @method setPrefix(prefix: String): this 5601 // Sets the text before the attributions. 5602 setPrefix: function (prefix) { 5603 this.options.prefix = prefix; 5604 this._update(); 5605 return this; 5606 }, 5607 5608 // @method addAttribution(text: String): this 5609 // Adds an attribution text (e.g. `'Vector data © Mapbox'`). 5610 addAttribution: function (text) { 5611 if (!text) { return this; } 5612 5613 if (!this._attributions[text]) { 5614 this._attributions[text] = 0; 5615 } 5616 this._attributions[text]++; 5617 5618 this._update(); 5619 5620 return this; 5621 }, 5622 5623 // @method removeAttribution(text: String): this 5624 // Removes an attribution text. 5625 removeAttribution: function (text) { 5626 if (!text) { return this; } 5627 5628 if (this._attributions[text]) { 5629 this._attributions[text]--; 5630 this._update(); 5631 } 5632 5633 return this; 5634 }, 5635 5636 _update: function () { 5637 if (!this._map) { return; } 5638 5639 var attribs = []; 5640 5641 for (var i in this._attributions) { 5642 if (this._attributions[i]) { 5643 attribs.push(i); 5644 } 5645 } 5646 5647 var prefixAndAttribs = []; 5648 5649 if (this.options.prefix) { 5650 prefixAndAttribs.push(this.options.prefix); 5651 } 5652 if (attribs.length) { 5653 prefixAndAttribs.push(attribs.join(', ')); 5654 } 5655 5656 this._container.innerHTML = prefixAndAttribs.join(' | '); 5657 } 5658 }); 5659 5660 // @namespace Map 5661 // @section Control options 5662 // @option attributionControl: Boolean = true 5663 // Whether a [attribution control](#control-attribution) is added to the map by default. 5664 Map.mergeOptions({ 5665 attributionControl: true 5666 }); 5667 5668 Map.addInitHook(function () { 5669 if (this.options.attributionControl) { 5670 new Attribution().addTo(this); 5671 } 5672 }); 5673 5674 // @namespace Control.Attribution 5675 // @factory L.control.attribution(options: Control.Attribution options) 5676 // Creates an attribution control. 5677 var attribution = function (options) { 5678 return new Attribution(options); 5679 }; 5680 5681 Control.Layers = Layers; 5682 Control.Zoom = Zoom; 5683 Control.Scale = Scale; 5684 Control.Attribution = Attribution; 5685 5686 control.layers = layers; 5687 control.zoom = zoom; 5688 control.scale = scale; 5689 control.attribution = attribution; 5690 5691 /* 5692 L.Handler is a base class for handler classes that are used internally to inject 5693 interaction features like dragging to classes like Map and Marker. 5694 */ 5695 5696 // @class Handler 5697 // @aka L.Handler 5698 // Abstract class for map interaction handlers 5699 5700 var Handler = Class.extend({ 5701 initialize: function (map) { 5702 this._map = map; 5703 }, 5704 5705 // @method enable(): this 5706 // Enables the handler 5707 enable: function () { 5708 if (this._enabled) { return this; } 5709 5710 this._enabled = true; 5711 this.addHooks(); 5712 return this; 5713 }, 5714 5715 // @method disable(): this 5716 // Disables the handler 5717 disable: function () { 5718 if (!this._enabled) { return this; } 5719 5720 this._enabled = false; 5721 this.removeHooks(); 5722 return this; 5723 }, 5724 5725 // @method enabled(): Boolean 5726 // Returns `true` if the handler is enabled 5727 enabled: function () { 5728 return !!this._enabled; 5729 } 5730 5731 // @section Extension methods 5732 // Classes inheriting from `Handler` must implement the two following methods: 5733 // @method addHooks() 5734 // Called when the handler is enabled, should add event hooks. 5735 // @method removeHooks() 5736 // Called when the handler is disabled, should remove the event hooks added previously. 5737 }); 5738 5739 // @section There is static function which can be called without instantiating L.Handler: 5740 // @function addTo(map: Map, name: String): this 5741 // Adds a new Handler to the given map with the given name. 5742 Handler.addTo = function (map, name) { 5743 map.addHandler(name, this); 5744 return this; 5745 }; 5746 5747 var Mixin = {Events: Events}; 5748 5749 /* 5750 * @class Draggable 5751 * @aka L.Draggable 5752 * @inherits Evented 5753 * 5754 * A class for making DOM elements draggable (including touch support). 5755 * Used internally for map and marker dragging. Only works for elements 5756 * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition). 5757 * 5758 * @example 5759 * ```js 5760 * var draggable = new L.Draggable(elementToDrag); 5761 * draggable.enable(); 5762 * ``` 5763 */ 5764 5765 var START = touch ? 'touchstart mousedown' : 'mousedown'; 5766 var END = { 5767 mousedown: 'mouseup', 5768 touchstart: 'touchend', 5769 pointerdown: 'touchend', 5770 MSPointerDown: 'touchend' 5771 }; 5772 var MOVE = { 5773 mousedown: 'mousemove', 5774 touchstart: 'touchmove', 5775 pointerdown: 'touchmove', 5776 MSPointerDown: 'touchmove' 5777 }; 5778 5779 5780 var Draggable = Evented.extend({ 5781 5782 options: { 5783 // @section 5784 // @aka Draggable options 5785 // @option clickTolerance: Number = 3 5786 // The max number of pixels a user can shift the mouse pointer during a click 5787 // for it to be considered a valid click (as opposed to a mouse drag). 5788 clickTolerance: 3 5789 }, 5790 5791 // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options) 5792 // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default). 5793 initialize: function (element, dragStartTarget, preventOutline, options) { 5794 setOptions(this, options); 5795 5796 this._element = element; 5797 this._dragStartTarget = dragStartTarget || element; 5798 this._preventOutline = preventOutline; 5799 }, 5800 5801 // @method enable() 5802 // Enables the dragging ability 5803 enable: function () { 5804 if (this._enabled) { return; } 5805 5806 on(this._dragStartTarget, START, this._onDown, this); 5807 5808 this._enabled = true; 5809 }, 5810 5811 // @method disable() 5812 // Disables the dragging ability 5813 disable: function () { 5814 if (!this._enabled) { return; } 5815 5816 // If we're currently dragging this draggable, 5817 // disabling it counts as first ending the drag. 5818 if (Draggable._dragging === this) { 5819 this.finishDrag(); 5820 } 5821 5822 off(this._dragStartTarget, START, this._onDown, this); 5823 5824 this._enabled = false; 5825 this._moved = false; 5826 }, 5827 5828 _onDown: function (e) { 5829 // Ignore simulated events, since we handle both touch and 5830 // mouse explicitly; otherwise we risk getting duplicates of 5831 // touch events, see #4315. 5832 // Also ignore the event if disabled; this happens in IE11 5833 // under some circumstances, see #3666. 5834 if (e._simulated || !this._enabled) { return; } 5835 5836 this._moved = false; 5837 5838 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; } 5839 5840 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; } 5841 Draggable._dragging = this; // Prevent dragging multiple objects at once. 5842 5843 if (this._preventOutline) { 5844 preventOutline(this._element); 5845 } 5846 5847 disableImageDrag(); 5848 disableTextSelection(); 5849 5850 if (this._moving) { return; } 5851 5852 // @event down: Event 5853 // Fired when a drag is about to start. 5854 this.fire('down'); 5855 5856 var first = e.touches ? e.touches[0] : e, 5857 sizedParent = getSizedParentNode(this._element); 5858 5859 this._startPoint = new Point(first.clientX, first.clientY); 5860 5861 // Cache the scale, so that we can continuously compensate for it during drag (_onMove). 5862 this._parentScale = getScale(sizedParent); 5863 5864 on(document, MOVE[e.type], this._onMove, this); 5865 on(document, END[e.type], this._onUp, this); 5866 }, 5867 5868 _onMove: function (e) { 5869 // Ignore simulated events, since we handle both touch and 5870 // mouse explicitly; otherwise we risk getting duplicates of 5871 // touch events, see #4315. 5872 // Also ignore the event if disabled; this happens in IE11 5873 // under some circumstances, see #3666. 5874 if (e._simulated || !this._enabled) { return; } 5875 5876 if (e.touches && e.touches.length > 1) { 5877 this._moved = true; 5878 return; 5879 } 5880 5881 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), 5882 offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint); 5883 5884 if (!offset.x && !offset.y) { return; } 5885 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; } 5886 5887 // We assume that the parent container's position, border and scale do not change for the duration of the drag. 5888 // Therefore there is no need to account for the position and border (they are eliminated by the subtraction) 5889 // and we can use the cached value for the scale. 5890 offset.x /= this._parentScale.x; 5891 offset.y /= this._parentScale.y; 5892 5893 preventDefault(e); 5894 5895 if (!this._moved) { 5896 // @event dragstart: Event 5897 // Fired when a drag starts 5898 this.fire('dragstart'); 5899 5900 this._moved = true; 5901 this._startPos = getPosition(this._element).subtract(offset); 5902 5903 addClass(document.body, 'leaflet-dragging'); 5904 5905 this._lastTarget = e.target || e.srcElement; 5906 // IE and Edge do not give the <use> element, so fetch it 5907 // if necessary 5908 if (window.SVGElementInstance && this._lastTarget instanceof window.SVGElementInstance) { 5909 this._lastTarget = this._lastTarget.correspondingUseElement; 5910 } 5911 addClass(this._lastTarget, 'leaflet-drag-target'); 5912 } 5913 5914 this._newPos = this._startPos.add(offset); 5915 this._moving = true; 5916
5917 cancelAnimFrame(this._animRequest); 5918 this._lastEvent = e; 5919 this._animRequest = requestAnimFrame(this._updatePosition, this, true); 5920 }, 5921 5922 _updatePosition: function () { 5923 var e = {originalEvent: this._lastEvent}; 5924 5925 // @event predrag: Event 5926 // Fired continuously during dragging *before* each corresponding 5927 // update of the element's position. 5928 this.fire('predrag', e); 5929 setPosition(this._element, this._newPos); 5930 5931 // @event drag: Event 5932 // Fired continuously during dragging. 5933 this.fire('drag', e); 5934 }, 5935 5936 _onUp: function (e) { 5937 // Ignore simulated events, since we handle both touch and 5938 // mouse explicitly; otherwise we risk getting duplicates of 5939 // touch events, see #4315. 5940 // Also ignore the event if disabled; this happens in IE11 5941 // under some circumstances, see #3666. 5942 if (e._simulated || !this._enabled) { return; } 5943 this.finishDrag(); 5944 }, 5945 5946 finishDrag: function () { 5947 removeClass(document.body, 'leaflet-dragging'); 5948 5949 if (this._lastTarget) { 5950 removeClass(this._lastTarget, 'leaflet-drag-target'); 5951 this._lastTarget = null; 5952 } 5953 5954 for (var i in MOVE) { 5955 off(document, MOVE[i], this._onMove, this); 5956 off(document, END[i], this._onUp, this); 5957 } 5958 5959 enableImageDrag(); 5960 enableTextSelection(); 5961 5962 if (this._moved && this._moving) { 5963 // ensure drag is not fired after dragend 5964 cancelAnimFrame(this._animRequest); 5965 5966 // @event dragend: DragEndEvent 5967 // Fired when the drag ends. 5968 this.fire('dragend', { 5969 distance: this._newPos.distanceTo(this._startPos) 5970 }); 5971 } 5972 5973 this._moving = false; 5974 Draggable._dragging = false; 5975 } 5976 5977 }); 5978 5979 /* 5980 * @namespace LineUtil 5981 * 5982 * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast. 5983 */ 5984 5985 // Simplify polyline with vertex reduction and Douglas-Peucker simplification. 5986 // Improves rendering performance dramatically by lessening the number of points to draw. 5987 5988 // @function simplify(points: Point[], tolerance: Number): Point[] 5989 // Dramatically reduces the number of points in a polyline while retaining 5990 // its shape and returns a new array of simplified points, using the 5991 // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm). 5992 // Used for a huge performance boost when processing/displaying Leaflet polylines for 5993 // each zoom level and also reducing visual noise. tolerance affects the amount of 5994 // simplification (lesser value means higher quality but slower and with more points). 5995 // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/). 5996 function simplify(points, tolerance) { 5997 if (!tolerance || !points.length) { 5998 return points.slice(); 5999 } 6000 6001 var sqTolerance = tolerance * tolerance; 6002 6003 // stage 1: vertex reduction 6004 points = _reducePoints(points, sqTolerance); 6005 6006 // stage 2: Douglas-Peucker simplification 6007 points = _simplifyDP(points, sqTolerance); 6008 6009 return points; 6010 } 6011 6012 // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number 6013 // Returns the distance between point `p` and segment `p1` to `p2`. 6014 function pointToSegmentDistance(p, p1, p2) { 6015 return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true)); 6016 } 6017 6018 // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number 6019 // Returns the closest point from a point `p` on a segment `p1` to `p2`. 6020 function closestPointOnSegment(p, p1, p2) { 6021 return _sqClosestPointOnSegment(p, p1, p2); 6022 } 6023 6024 // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm 6025 function _simplifyDP(points, sqTolerance) { 6026 6027 var len = points.length, 6028 ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array, 6029 markers = new ArrayConstructor(len); 6030 6031 markers[0] = markers[len - 1] = 1; 6032 6033 _simplifyDPStep(points, markers, sqTolerance, 0, len - 1); 6034 6035 var i, 6036 newPoints = []; 6037 6038 for (i = 0; i < len; i++) { 6039 if (markers[i]) { 6040 newPoints.push(points[i]); 6041 } 6042 } 6043 6044 return newPoints; 6045 } 6046 6047 function _simplifyDPStep(points, markers, sqTolerance, first, last) { 6048 6049 var maxSqDist = 0, 6050 index, i, sqDist; 6051 6052 for (i = first + 1; i <= last - 1; i++) { 6053 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true); 6054 6055 if (sqDist > maxSqDist) { 6056 index = i; 6057 maxSqDist = sqDist; 6058 } 6059 } 6060 6061 if (maxSqDist > sqTolerance) { 6062 markers[index] = 1; 6063 6064 _simplifyDPStep(points, markers, sqTolerance, first, index); 6065 _simplifyDPStep(points, markers, sqTolerance, index, last); 6066 } 6067 } 6068 6069 // reduce points that are too close to each other to a single point 6070 function _reducePoints(points, sqTolerance) { 6071 var reducedPoints = [points[0]]; 6072 6073 for (var i = 1, prev = 0, len = points.length; i < len; i++) { 6074 if (_sqDist(points[i], points[prev]) > sqTolerance) { 6075 reducedPoints.push(points[i]); 6076 prev = i; 6077 } 6078 } 6079 if (prev < len - 1) { 6080 reducedPoints.push(points[len - 1]); 6081 } 6082 return reducedPoints; 6083 } 6084 6085 var _lastCode; 6086 6087 // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, roun
vendor: 7,724 bytes, lines 6087-6365
6087d?: Boolean): Point[]|Boolean 6088 // Clips the segment a to b by rectangular bounds with the 6089 // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm) 6090 // (modifying the segment points directly!). Used by Leaflet to only show polyline 6091 // points that are on the screen or near, increasing performance. 6092 function clipSegment(a, b, bounds, useLastCode, round) { 6093 var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds), 6094 codeB = _getBitCode(b, bounds), 6095 6096 codeOut, p, newCode; 6097 6098 // save 2nd code to avoid calculating it on the next segment 6099 _lastCode = codeB; 6100 6101 while (true) { 6102 // if a,b is inside the clip window (trivial accept) 6103 if (!(codeA | codeB)) { 6104 return [a, b]; 6105 } 6106 6107 // if a,b is outside the clip window (trivial reject) 6108 if (codeA & codeB) { 6109 return false; 6110 } 6111 6112 // other cases 6113 codeOut = codeA || codeB; 6114 p = _getEdgeIntersection(a, b, codeOut, bounds, round); 6115 newCode = _getBitCode(p, bounds); 6116 6117 if (codeOut === codeA) { 6118 a = p; 6119 codeA = newCode; 6120 } else { 6121 b = p; 6122 codeB = newCode; 6123 } 6124 } 6125 } 6126 6127 function _getEdgeIntersection(a, b, code, bounds, round) { 6128 var dx = b.x - a.x, 6129 dy = b.y - a.y, 6130 min = bounds.min, 6131 max = bounds.max, 6132 x, y; 6133 6134 if (code & 8) { // top 6135 x = a.x + dx * (max.y - a.y) / dy; 6136 y = max.y; 6137 6138 } else if (code & 4) { // bottom 6139 x = a.x + dx * (min.y - a.y) / dy; 6140 y = min.y; 6141 6142 } else if (code & 2) { // right 6143 x = max.x; 6144 y = a.y + dy * (max.x - a.x) / dx; 6145 6146 } else if (code & 1) { // left 6147 x = min.x; 6148 y = a.y + dy * (min.x - a.x) / dx; 6149 } 6150 6151 return new Point(x, y, round); 6152 } 6153 6154 function _getBitCode(p, bounds) { 6155 var code = 0; 6156 6157 if (p.x < bounds.min.x) { // left 6158 code |= 1; 6159 } else if (p.x > bounds.max.x) { // right 6160 code |= 2; 6161 } 6162 6163 if (p.y < bounds.min.y) { // bottom 6164 code |= 4; 6165 } else if (p.y > bounds.max.y) { // top 6166 code |= 8; 6167 } 6168 6169 return code; 6170 } 6171 6172 // square distance (to avoid unnecessary Math.sqrt calls) 6173 function _sqDist(p1, p2) { 6174 var dx = p2.x - p1.x, 6175 dy = p2.y - p1.y; 6176 return dx * dx + dy * dy; 6177 } 6178 6179 // return closest point on segment or distance to that point 6180 function _sqClosestPointOnSegment(p, p1, p2, sqDist) { 6181 var x = p1.x, 6182 y = p1.y, 6183 dx = p2.x - x, 6184 dy = p2.y - y, 6185 dot = dx * dx + dy * dy, 6186 t; 6187 6188 if (dot > 0) { 6189 t = ((p.x - x) * dx + (p.y - y) * dy) / dot; 6190 6191 if (t > 1) { 6192 x = p2.x; 6193 y = p2.y; 6194 } else if (t > 0) { 6195 x += dx * t; 6196 y += dy * t; 6197 } 6198 } 6199 6200 dx = p.x - x; 6201 dy = p.y - y; 6202 6203 return sqDist ? dx * dx + dy * dy : new Point(x, y); 6204 } 6205 6206 6207 // @function isFlat(latlngs: LatLng[]): Boolean 6208 // Returns true if `latlngs` is a flat array, false is nested. 6209 function isFlat(latlngs) { 6210 return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined'); 6211 } 6212 6213 function _flat(latlngs) { 6214 console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.'); 6215 return isFlat(latlngs); 6216 } 6217 6218 var LineUtil = { 6219 __proto__: null, 6220 simplify: simplify, 6221 pointToSegmentDistance: pointToSegmentDistance, 6222 closestPointOnSegment: closestPointOnSegment, 6223 clipSegment: clipSegment, 6224 _getEdgeIntersection: _getEdgeIntersection, 6225 _getBitCode: _getBitCode, 6226 _sqClosestPointOnSegment: _sqClosestPointOnSegment, 6227 isFlat: isFlat, 6228 _flat: _flat 6229 }; 6230 6231 /* 6232 * @namespace PolyUtil 6233 * Various utility functions for polygon geometries. 6234 */ 6235 6236 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[] 6237 * 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)). 6238 * Used by Leaflet to only show polygon points that are on the screen or near, increasing 6239 * performance. Note that polygon points needs different algorithm for clipping 6240 * than polyline, so there's a separate method for it. 6241 */ 6242 function clipPolygon(points, bounds, round) { 6243 var clippedPoints, 6244 edges = [1, 4, 2, 8], 6245 i, j, k, 6246 a, b, 6247 len, edge, p; 6248 6249 for (i = 0, len = points.length; i < len; i++) { 6250 points[i]._code = _getBitCode(points[i], bounds); 6251 } 6252 6253 // for each edge (left, bottom, right, top) 6254 for (k = 0; k < 4; k++) { 6255 edge = edges[k]; 6256 clippedPoints = []; 6257 6258 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { 6259 a = points[i]; 6260 b = points[j]; 6261 6262 // if a is inside the clip window 6263 if (!(a._code & edge)) { 6264 // if b is outside the clip window (a->b goes out of screen) 6265 if (b._code & edge) { 6266 p = _getEdgeIntersection(b, a, edge, bounds, round); 6267 p._code = _getBitCode(p, bounds); 6268 clippedPoints.push(p); 6269 } 6270 clippedPoints.push(a); 6271 6272 // else if b is inside the clip window (a->b enters the screen) 6273 } else if (!(b._code & edge)) { 6274 p = _getEdgeIntersection(b, a, edge, bounds, round); 6275 p._code = _getBitCode(p, bounds); 6276 clippedPoints.push(p); 6277 } 6278 } 6279 points = clippedPoints; 6280 } 6281 6282 return points; 6283 } 6284 6285 var PolyUtil = { 6286 __proto__: null, 6287 clipPolygon: clipPolygon 6288 }; 6289 6290 /* 6291 * @namespace Projection 6292 * @section 6293 * Leaflet comes with a set of already defined Projections out of the box: 6294 * 6295 * @projection L.Projection.LonLat 6296 * 6297 * Equirectangular, or Plate Carree projection â the most simple projection, 6298 * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as 6299 * latitude. Also suitable for flat worlds, e.g. game maps. Used by the 6300 * `EPSG:4326` and `Simple` CRS. 6301 */ 6302 6303 var LonLat = { 6304 project: function (latlng) { 6305 return new Point(latlng.lng, latlng.lat); 6306 }, 6307 6308 unproject: function (point) { 6309 return new LatLng(point.y, point.x); 6310 }, 6311 6312 bounds: new Bounds([-180, -90], [180, 90]) 6313 }; 6314 6315 /* 6316 * @namespace Projection 6317 * @projection L.Projection.Mercator 6318 * 6319 * Elliptical Mercator projection â more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS. 6320 */ 6321 6322 var Mercator = { 6323 R: 6378137, 6324 R_MINOR: 6356752.314245179, 6325 6326 bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), 6327 6328 project: function (latlng) { 6329 var d = Math.PI / 180, 6330 r = this.R, 6331 y = latlng.lat * d, 6332 tmp = this.R_MINOR / r, 6333 e = Math.sqrt(1 - tmp * tmp), 6334 con = e * Math.sin(y); 6335 6336 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2); 6337 y = -r * Math.log(Math.max(ts, 1E-10)); 6338 6339 return new Point(latlng.lng * d * r, y); 6340 }, 6341 6342 unproject: function (point) { 6343 var d = 180 / Math.PI, 6344 r = this.R, 6345 tmp = this.R_MINOR / r, 6346 e = Math.sqrt(1 - tmp * tmp), 6347 ts = Math.exp(-point.y / r), 6348 phi = Math.PI / 2 - 2 * Math.atan(ts); 6349 6350 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) { 6351 con = e * Math.sin(phi); 6352 con = Math.pow((1 - con) / (1 + con), e / 2); 6353 dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi; 6354 phi += dphi; 6355 } 6356 6357 return new LatLng(phi * d, point.x * d / r); 6358 } 6359 }; 6360 6361 /* 6362 * @class Projection 6363 6364 * An object with methods for projecting geographical coordinates of the world onto 6365 * a flat surface (and back). See [Map projection](http
vendor: 6,145 bytes, lines 6365-6576
6365://en.wikipedia.org/wiki/Map_projection). 6366 6367 * @property bounds: Bounds 6368 * The bounds (specified in CRS units) where the projection is valid 6369 6370 * @method project(latlng: LatLng): Point 6371 * Projects geographical coordinates into a 2D point. 6372 * Only accepts actual `L.LatLng` instances, not arrays. 6373 6374 * @method unproject(point: Point): LatLng 6375 * The inverse of `project`. Projects a 2D point into a geographical location. 6376 * Only accepts actual `L.Point` instances, not arrays. 6377 6378 * Note that the projection instances do not inherit from Leaflet's `Class` object, 6379 * and can't be instantiated. Also, new classes can't inherit from them, 6380 * and methods can't be added to them with the `include` function. 6381 6382 */ 6383 6384 var index = { 6385 __proto__: null, 6386 LonLat: LonLat, 6387 Mercator: Mercator, 6388 SphericalMercator: SphericalMercator 6389 }; 6390 6391 /* 6392 * @namespace CRS 6393 * @crs L.CRS.EPSG3395 6394 * 6395 * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection. 6396 */ 6397 var EPSG3395 = extend({}, Earth, { 6398 code: 'EPSG:3395', 6399 projection: Mercator, 6400 6401 transformation: (function () { 6402 var scale = 0.5 / (Math.PI * Mercator.R); 6403 return toTransformation(scale, 0.5, -scale, 0.5); 6404 }()) 6405 }); 6406 6407 /* 6408 * @namespace CRS 6409 * @crs L.CRS.EPSG4326 6410 * 6411 * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection. 6412 * 6413 * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic), 6414 * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer` 6415 * with this CRS, ensure that there are two 256x256 pixel tiles covering the 6416 * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90), 6417 * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set. 6418 */ 6419 6420 var EPSG4326 = extend({}, Earth, { 6421 code: 'EPSG:4326', 6422 projection: LonLat, 6423 transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5) 6424 }); 6425 6426 /* 6427 * @namespace CRS 6428 * @crs L.CRS.Simple 6429 * 6430 * A simple CRS that maps longitude and latitude into `x` and `y` directly. 6431 * May be used for maps of flat surfaces (e.g. game maps). Note that the `y` 6432 * axis should still be inverted (going from bottom to top). `distance()` returns 6433 * simple euclidean distance. 6434 */ 6435 6436 var Simple = extend({}, CRS, { 6437 projection: LonLat, 6438 transformation: toTransformation(1, 0, -1, 0), 6439 6440 scale: function (zoom) { 6441 return Math.pow(2, zoom); 6442 }, 6443 6444 zoom: function (scale) { 6445 return Math.log(scale) / Math.LN2; 6446 }, 6447 6448 distance: function (latlng1, latlng2) { 6449 var dx = latlng2.lng - latlng1.lng, 6450 dy = latlng2.lat - latlng1.lat; 6451 6452 return Math.sqrt(dx * dx + dy * dy); 6453 }, 6454 6455 infinite: true 6456 }); 6457 6458 CRS.Earth = Earth; 6459 CRS.EPSG3395 = EPSG3395; 6460 CRS.EPSG3857 = EPSG3857; 6461 CRS.EPSG900913 = EPSG900913; 6462 CRS.EPSG4326 = EPSG4326; 6463 CRS.Simple = Simple; 6464 6465 /* 6466 * @class Layer 6467 * @inherits Evented 6468 * @aka L.Layer 6469 * @aka ILayer 6470 * 6471 * A set of methods from the Layer base class that all Leaflet layers use. 6472 * Inherits all methods, options and events from `L.Evented`. 6473 * 6474 * @example 6475 * 6476 * ```js 6477 * var layer = L.marker(latlng).addTo(map); 6478 * layer.addTo(map); 6479 * layer.remove(); 6480 * ``` 6481 * 6482 * @event add: Event 6483 * Fired after the layer is added to a map 6484 * 6485 * @event remove: Event 6486 * Fired after the layer is removed from a map 6487 */ 6488 6489 6490 var Layer = Evented.extend({ 6491 6492 // Classes extending `L.Layer` will inherit the following options: 6493 options: { 6494 // @option pane: String = 'overlayPane' 6495 // 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. 6496 pane: 'overlayPane', 6497 6498 // @option attribution: String = null 6499 // 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. 6500 attribution: null, 6501 6502 bubblingMouseEvents: true 6503 }, 6504 6505 /* @section 6506 * Classes extending `L.Layer` will inherit the following methods: 6507 * 6508 * @method addTo(map: Map|LayerGroup): this 6509 * Adds the layer to the given map or layer group. 6510 */ 6511 addTo: function (map) { 6512 map.addLayer(this); 6513 return this; 6514 }, 6515 6516 // @method remove: this 6517 // Removes the layer from the map it is currently active on. 6518 remove: function () { 6519 return this.removeFrom(this._map || this._mapToAdd); 6520 }, 6521 6522 // @method removeFrom(map: Map): this 6523 // Removes the layer from the given map 6524 // 6525 // @alternative 6526 // @method removeFrom(group: LayerGroup): this 6527 // Removes the layer from the given `LayerGroup` 6528 removeFrom: function (obj) { 6529 if (obj) { 6530 obj.removeLayer(this); 6531 } 6532 return this; 6533 }, 6534 6535 // @method getPane(name? : String): HTMLElement 6536 // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer. 6537 getPane: function (name) { 6538 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane); 6539 }, 6540 6541 addInteractiveTarget: function (targetEl) { 6542 this._map._targets[stamp(targetEl)] = this; 6543 return this; 6544 }, 6545 6546 removeInteractiveTarget: function (targetEl) { 6547 delete this._map._targets[stamp(targetEl)]; 6548 return this; 6549 }, 6550 6551 // @method getAttribution: String 6552 // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution). 6553 getAttribution: function () { 6554 return this.options.attribution; 6555 }, 6556 6557 _layerAdd: function (e) { 6558 var map = e.target; 6559 6560 // check in case layer gets added and then removed before the map is ready 6561 if (!map.hasLayer(this)) { return; } 6562 6563 this._map = map; 6564 this._zoomAnimated = map._zoomAnimated; 6565 6566 if (this.getEvents) { 6567 var events = this.getEvents(); 6568 map.on(events, this); 6569 this.once('remove', function () { 6570 map.off(events, this); 6571 }, this); 6572 } 6573 6574 this.onAdd(map); 6575 6576
6576if (this.getAttribution && map.attributionControl) { 6577 map.attributionControl.addAttribution(this.getAttribution()); 6578 } 6579 6580 this.fire('add'); 6581 map.fire('layeradd', {layer: this}); 6582 } 6583 }); 6584 6585 /* @section Extension methods 6586 * @uninheritable 6587 * 6588 * Every layer should extend from `L.Layer` and (re-)implement the following methods. 6589 * 6590 * @method onAdd(map: Map): this 6591 * 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). 6592 * 6593 * @method onRemove(map: Map): this 6594 * 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). 6595 * 6596 * @method getEvents(): Object 6597 * 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. 6598 * 6599 * @method getAttribution(): String 6600 * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible. 6601 * 6602 * @method beforeAdd(map: Map): this 6603 * 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. 6604 */ 6605 6606 6607 /* @namespace Map 6608 * @section Layer events 6609 * 6610 * @event layeradd: LayerEvent 6611 * Fired when a new layer is added to the map. 6612 * 6613 * @event layerremove: LayerEvent 6614 * Fired when some layer is removed from the map 6615 * 6616 * @section Methods for Layers and Controls 6617 */ 6618 Map.include({ 6619 // @method addLayer(layer: Layer): this 6620 // Adds the given layer to the map 6621 addLayer: function (layer) { 6622 if (!layer._layerAdd) { 6623 throw new Error('The provided object is not a Layer.'); 6624 } 6625 6626 var id = stamp(layer); 6627 if (this._layers[id]) { return this; } 6628 this._layers[id] = layer; 6629 6630 layer._mapToAdd = this; 6631 6632 if (layer.beforeAdd) { 6633 layer.beforeAdd(this); 6634 } 6635 6636 this.whenReady(layer._layerAdd, layer); 6637 6638 return this; 6639 }, 6640 6641 // @method removeLayer(layer: Layer): this 6642 // Removes the given layer from the map. 6643 removeLayer: function (layer) { 6644 var id = stamp(layer); 6645 6646 if (!this._layers[id]) { return this; } 6647 6648 if (this._loaded) { 6649 layer.onRemove(this); 6650 } 6651 6652 if (layer.getAttribution && this.attributionControl) { 6653 this.attributionControl.removeAttribution(layer.getAttribution()); 6654 } 6655 6656 delete this._layers[id]; 6657 6658 if (this._loaded) { 6659 this.fire('layerremove', {layer: layer}); 6660 layer.fire('remove'); 6661 } 6662 6663 layer._map = layer._mapToAdd = null; 6664 6665 return this; 6666 }, 6667 6668 // @method hasLayer(layer: Layer): Boolean 6669 // Returns `true` if the given layer is currently added to the map 6670 hasLayer: function (layer) { 6671 return !!layer && (stamp(layer) in this._layers); 6672 }, 6673 6674 /* @method eachLayer(fn: Function, context?: Object): this 6675 * Iterates over the layers of the map, optionally specifying context of the iterator function. 6676 * ``` 6677 * map.eachLayer(function(layer){ 6678 * layer.bindPopup('Hello'); 6679 * }); 6680 * ``` 6681 */ 6682 eachLayer: function (method, context) { 6683 for (var i in this._layers) { 6684 method.call(context, this._layers[i]); 6685 } 6686 return this; 6687 }, 6688 6689 _addLayers: function (layers) { 6690 layers = layers ? (isArray(layers) ? layers : [layers]) : []; 6691 6692 for (var i = 0, len = layers.length; i < len; i++) { 6693 this.addLayer(layers[i]); 6694 } 6695 }, 6696 6697 _addZoomLimit: function (layer) { 6698 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) { 6699 this._zoomBoundLayers[stamp(layer)] = layer; 6700 this._updateZoomLevels(); 6701 } 6702 }, 6703 6704 _removeZoomLimit: function (layer) { 6705 var id = stamp(layer); 6706 6707 if (this._zoomBoundLayers[id]) { 6708 delete this._zoomBoundLayers[id]; 6709 this._updateZoomLevels(); 6710 } 6711 }, 6712 6713 _updateZoomLevels: function () { 6714 var minZoom = Infinity, 6715 maxZoom = -Infinity, 6716 oldZoomSpan = this._getZoomSpan(); 6717 6718 for (var i in this._zoomBoundLayers) { 6719 var options = this._zoomBoundLayers[i].options; 6720
vendor: 2,756 bytes, lines 6721-6816
6721 minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom); 6722 maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom); 6723 } 6724 6725 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom; 6726 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom; 6727 6728 // @section Map state change events 6729 // @event zoomlevelschange: Event 6730 // Fired when the number of zoomlevels on the map is changed due 6731 // to adding or removing a layer. 6732 if (oldZoomSpan !== this._getZoomSpan()) { 6733 this.fire('zoomlevelschange'); 6734 } 6735 6736 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) { 6737 this.setZoom(this._layersMaxZoom); 6738 } 6739 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) { 6740 this.setZoom(this._layersMinZoom); 6741 } 6742 } 6743 }); 6744 6745 /* 6746 * @class LayerGroup 6747 * @aka L.LayerGroup 6748 * @inherits Layer 6749 * 6750 * Used to group several layers and handle them as one. If you add it to the map, 6751 * any layers added or removed from the group will be added/removed on the map as 6752 * well. Extends `Layer`. 6753 * 6754 * @example 6755 * 6756 * ```js 6757 * L.layerGroup([marker1, marker2]) 6758 * .addLayer(polyline) 6759 * .addTo(map); 6760 * ``` 6761 */ 6762 6763 var LayerGroup = Layer.extend({ 6764 6765 initialize: function (layers, options) { 6766 setOptions(this, options); 6767 6768 this._layers = {}; 6769 6770 var i, len; 6771 6772 if (layers) { 6773 for (i = 0, len = layers.length; i < len; i++) { 6774 this.addLayer(layers[i]); 6775 } 6776 } 6777 }, 6778 6779 // @method addLayer(layer: Layer): this 6780 // Adds the given layer to the group. 6781 addLayer: function (layer) { 6782 var id = this.getLayerId(layer); 6783 6784 this._layers[id] = layer; 6785 6786 if (this._map) { 6787 this._map.addLayer(layer); 6788 } 6789 6790 return this; 6791 }, 6792 6793 // @method removeLayer(layer: Layer): this 6794 // Removes the given layer from the group. 6795 // @alternative 6796 // @method removeLayer(id: Number): this 6797 // Removes the layer with the given internal ID from the group. 6798 removeLayer: function (layer) { 6799 var id = layer in this._layers ? layer : this.getLayerId(layer); 6800 6801 if (this._map && this._layers[id]) { 6802 this._map.removeLayer(this._layers[id]); 6803 } 6804 6805 delete this._layers[id]; 6806 6807 return this; 6808 }, 6809 6810 // @method hasLayer(layer: Layer): Boolean 6811 // Returns `true` if the given layer is currently added to the group. 6812 // @alternative 6813 // @method hasLayer(id: Number): Boolean 6814 // Returns `true` if the given internal ID is currently added to the group. 6815 hasLayer: function (layer) { 6816 if (!layer) { return false; }
vendor: 7,845 bytes, lines 6816-7066
6816 6817 var layerId = typeof layer === 'number' ? layer : this.getLayerId(layer); 6818 return layerId in this._layers; 6819 }, 6820 6821 // @method clearLayers(): this 6822 // Removes all the layers from the group. 6823 clearLayers: function () { 6824 return this.eachLayer(this.removeLayer, this); 6825 }, 6826 6827 // @method invoke(methodName: String, â¦): this 6828 // Calls `methodName` on every layer contained in this group, passing any 6829 // additional parameters. Has no effect if the layers contained do not 6830 // implement `methodName`. 6831 invoke: function (methodName) { 6832 var args = Array.prototype.slice.call(arguments, 1), 6833 i, layer; 6834 6835 for (i in this._layers) { 6836 layer = this._layers[i]; 6837 6838 if (layer[methodName]) { 6839 layer[methodName].apply(layer, args); 6840 } 6841 } 6842 6843 return this; 6844 }, 6845 6846 onAdd: function (map) { 6847 this.eachLayer(map.addLayer, map); 6848 }, 6849 6850 onRemove: function (map) { 6851 this.eachLayer(map.removeLayer, map); 6852 }, 6853 6854 // @method eachLayer(fn: Function, context?: Object): this 6855 // Iterates over the layers of the group, optionally specifying context of the iterator function. 6856 // ```js 6857 // group.eachLayer(function (layer) { 6858 // layer.bindPopup('Hello'); 6859 // }); 6860 // ``` 6861 eachLayer: function (method, context) { 6862 for (var i in this._layers) { 6863 method.call(context, this._layers[i]); 6864 } 6865 return this; 6866 }, 6867 6868 // @method getLayer(id: Number): Layer 6869 // Returns the layer with the given internal ID. 6870 getLayer: function (id) { 6871 return this._layers[id]; 6872 }, 6873 6874 // @method getLayers(): Layer[] 6875 // Returns an array of all the layers added to the group. 6876 getLayers: function () { 6877 var layers = []; 6878 this.eachLayer(layers.push, layers); 6879 return layers; 6880 }, 6881 6882 // @method setZIndex(zIndex: Number): this 6883 // Calls `setZIndex` on every layer contained in this group, passing the z-index. 6884 setZIndex: function (zIndex) { 6885 return this.invoke('setZIndex', zIndex); 6886 }, 6887 6888 // @method getLayerId(layer: Layer): Number 6889 // Returns the internal ID for a layer 6890 getLayerId: function (layer) { 6891 return stamp(layer); 6892 } 6893 }); 6894 6895 6896 // @factory L.layerGroup(layers?: Layer[], options?: Object) 6897 // Create a layer group, optionally given an initial set of layers and an `options` object. 6898 var layerGroup = function (layers, options) { 6899 return new LayerGroup(layers, options); 6900 }; 6901 6902 /* 6903 * @class FeatureGroup 6904 * @aka L.FeatureGroup 6905 * @inherits LayerGroup 6906 * 6907 * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers: 6908 * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip)) 6909 * * Events are propagated to the `FeatureGroup`, so if the group has an event 6910 * handler, it will handle events from any of the layers. This includes mouse events 6911 * and custom events. 6912 * * Has `layeradd` and `layerremove` events 6913 * 6914 * @example 6915 * 6916 * ```js 6917 * L.featureGroup([marker1, marker2, polyline]) 6918 * .bindPopup('Hello world!') 6919 * .on('click', function() { alert('Clicked on a member of the group!'); }) 6920 * .addTo(map); 6921 * ``` 6922 */ 6923 6924 var FeatureGroup = LayerGroup.extend({ 6925 6926 addLayer: function (layer) { 6927 if (this.hasLayer(layer)) { 6928 return this; 6929 } 6930 6931 layer.addEventParent(this); 6932 6933 LayerGroup.prototype.addLayer.call(this, layer); 6934 6935 // @event layeradd: LayerEvent 6936 // Fired when a layer is added to this `FeatureGroup` 6937 return this.fire('layeradd', {layer: layer}); 6938 }, 6939 6940 removeLayer: function (layer) { 6941 if (!this.hasLayer(layer)) { 6942 return this; 6943 } 6944 if (layer in this._layers) { 6945 layer = this._layers[layer]; 6946 } 6947 6948 layer.removeEventParent(this); 6949 6950 LayerGroup.prototype.removeLayer.call(this, layer); 6951 6952 // @event layerremove: LayerEvent 6953 // Fired when a layer is removed from this `FeatureGroup` 6954 return this.fire('layerremove', {layer: layer}); 6955 }, 6956 6957 // @method setStyle(style: Path options): this 6958 // Sets the given path options to each layer of the group that has a `setStyle` method. 6959 setStyle: function (style) { 6960 return this.invoke('setStyle', style); 6961 }, 6962 6963 // @method bringToFront(): this 6964 // Brings the layer group to the top of all other layers 6965 bringToFront: function () { 6966 return this.invoke('bringToFront'); 6967 }, 6968 6969 // @method bringToBack(): this 6970 // Brings the layer group to the back of all other layers 6971 bringToBack: function () { 6972 return this.invoke('bringToBack'); 6973 }, 6974 6975 // @method getBounds(): LatLngBounds 6976 // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children). 6977 getBounds: function () { 6978 var bounds = new LatLngBounds(); 6979 6980 for (var id in this._layers) { 6981 var layer = this._layers[id]; 6982 bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng()); 6983 } 6984 return bounds; 6985 } 6986 }); 6987 6988 // @factory L.featureGroup(layers?: Layer[], options?: Object) 6989 // Create a feature group, optionally given an initial set of layers and an `options` object. 6990 var featureGroup = function (layers, options) { 6991 return new FeatureGroup(layers, options); 6992 }; 6993 6994 /* 6995 * @class Icon 6996 * @aka L.Icon 6997 * 6998 * Represents an icon to provide when creating a marker. 6999 * 7000 * @example 7001 * 7002 * ```js 7003 * var myIcon = L.icon({ 7004 * iconUrl: 'my-icon.png', 7005 * iconRetinaUrl: '[email protected]', 7006 * iconSize: [38, 95], 7007 * iconAnchor: [22, 94], 7008 * popupAnchor: [-3, -76], 7009 * shadowUrl: 'my-icon-shadow.png', 7010 * shadowRetinaUrl: '[email protected]', 7011 * shadowSize: [68, 95], 7012 * shadowAnchor: [22, 94] 7013 * }); 7014 * 7015 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); 7016 * ``` 7017 * 7018 * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default. 7019 * 7020 */ 7021 7022 var Icon = Class.extend({ 7023 7024 /* @section 7025 * @aka Icon options 7026 * 7027 * @option iconUrl: String = null 7028 * **(required)** The URL to the icon image (absolute or relative to your script path). 7029 * 7030 * @option iconRetinaUrl: String = null 7031 * The URL to a retina sized version of the icon image (absolute or relative to your 7032 * script path). Used for Retina screen devices. 7033 * 7034 * @option iconSize: Point = null 7035 * Size of the icon image in pixels. 7036 * 7037 * @option iconAnchor: Point = null 7038 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon 7039 * will be aligned so that this point is at the marker's geographical location. Centered 7040 * by default if size is specified, also can be set in CSS with negative margins. 7041 * 7042 * @option popupAnchor: Point = [0, 0] 7043 * The coordinates of the point from which popups will "open", relative to the icon anchor. 7044 * 7045 * @option tooltipAnchor: Point = [0, 0] 7046 * The coordinates of the point from which tooltips will "open", relative to the icon anchor. 7047 * 7048 * @option shadowUrl: String = null 7049 * The URL to the icon shadow image. If not specified, no shadow image will be created. 7050 * 7051 * @option shadowRetinaUrl: String = null 7052 * 7053 * @option shadowSize: Point = null 7054 * Size of the shadow image in pixels. 7055 * 7056 * @option shadowAnchor: Point = null 7057 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same 7058 * as iconAnchor if not specified). 7059 * 7060 * @option className: String = '' 7061 * A custom class name to assign to both icon and shadow images. Empty by default. 7062 */ 7063 7064 options: { 7065 popupAnchor: [0, 0], 7066 tooltipAnchor: [0, 0]
vendor: 22,921 bytes, lines 7066-7879
7066 7067 }, 7068 7069 initialize: function (options) { 7070 setOptions(this, options); 7071 }, 7072 7073 // @method createIcon(oldIcon?: HTMLElement): HTMLElement 7074 // Called internally when the icon has to be shown, returns a `<img>` HTML element 7075 // styled according to the options. 7076 createIcon: function (oldIcon) { 7077 return this._createIcon('icon', oldIcon); 7078 }, 7079 7080 // @method createShadow(oldIcon?: HTMLElement): HTMLElement 7081 // As `createIcon`, but for the shadow beneath it. 7082 createShadow: function (oldIcon) { 7083 return this._createIcon('shadow', oldIcon); 7084 }, 7085 7086 _createIcon: function (name, oldIcon) { 7087 var src = this._getIconUrl(name); 7088 7089 if (!src) { 7090 if (name === 'icon') { 7091 throw new Error('iconUrl not set in Icon options (see the docs).'); 7092 } 7093 return null; 7094 } 7095 7096 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null); 7097 this._setIconStyles(img, name); 7098 7099 return img; 7100 }, 7101 7102 _setIconStyles: function (img, name) { 7103 var options = this.options; 7104 var sizeOption = options[name + 'Size']; 7105 7106 if (typeof sizeOption === 'number') { 7107 sizeOption = [sizeOption, sizeOption]; 7108 } 7109 7110 var size = toPoint(sizeOption), 7111 anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor || 7112 size && size.divideBy(2, true)); 7113 7114 img.className = 'leaflet-marker-' + name + ' ' + (options.className || ''); 7115 7116 if (anchor) { 7117 img.style.marginLeft = (-anchor.x) + 'px'; 7118 img.style.marginTop = (-anchor.y) + 'px'; 7119 } 7120 7121 if (size) { 7122 img.style.width = size.x + 'px'; 7123 img.style.height = size.y + 'px'; 7124 } 7125 }, 7126 7127 _createImg: function (src, el) { 7128 el = el || document.createElement('img'); 7129 el.src = src; 7130 return el; 7131 }, 7132 7133 _getIconUrl: function (name) { 7134 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url']; 7135 } 7136 }); 7137 7138 7139 // @factory L.icon(options: Icon options) 7140 // Creates an icon instance with the given options. 7141 function icon(options) { 7142 return new Icon(options); 7143 } 7144 7145 /* 7146 * @miniclass Icon.Default (Icon) 7147 * @aka L.Icon.Default 7148 * @section 7149 * 7150 * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when 7151 * no icon is specified. Points to the blue marker image distributed with Leaflet 7152 * releases. 7153 * 7154 * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options` 7155 * (which is a set of `Icon options`). 7156 * 7157 * If you want to _completely_ replace the default icon, override the 7158 * `L.Marker.prototype.options.icon` with your own icon instead. 7159 */ 7160 7161 var IconDefault = Icon.extend({ 7162 7163 options: { 7164 iconUrl: 'marker-icon.png', 7165 iconRetinaUrl: 'marker-icon-2x.png', 7166 shadowUrl: 'marker-shadow.png', 7167 iconSize: [25, 41], 7168 iconAnchor: [12, 41], 7169 popupAnchor: [1, -34], 7170 tooltipAnchor: [16, -28], 7171 shadowSize: [41, 41] 7172 }, 7173 7174 _getIconUrl: function (name) { 7175 if (!IconDefault.imagePath) { // Deprecated, backwards-compatibility only 7176 IconDefault.imagePath = this._detectIconPath(); 7177 } 7178 7179 // @option imagePath: String 7180 // `Icon.Default` will try to auto-detect the location of the 7181 // blue icon images. If you are placing these images in a non-standard 7182 // way, set this option to point to the right path. 7183 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name); 7184 }, 7185 7186 _detectIconPath: function () { 7187 var el = create$1('div', 'leaflet-default-icon-path', document.body); 7188 var path = getStyle(el, 'background-image') || 7189 getStyle(el, 'backgroundImage'); // IE8 7190 7191 document.body.removeChild(el); 7192 7193 if (path === null || path.indexOf('url') !== 0) { 7194 path = ''; 7195 } else { 7196 path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, ''); 7197 } 7198 7199 return path; 7200 } 7201 }); 7202 7203 /* 7204 * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. 7205 */ 7206 7207 7208 /* @namespace Marker 7209 * @section Interaction handlers 7210 * 7211 * 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: 7212 * 7213 * ```js 7214 * marker.dragging.disable(); 7215 * ``` 7216 * 7217 * @property dragging: Handler 7218 * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)). 7219 */ 7220 7221 var MarkerDrag = Handler.extend({ 7222 initialize: function (marker) { 7223 this._marker = marker; 7224 }, 7225 7226 addHooks: function () { 7227 var icon = this._marker._icon; 7228 7229 if (!this._draggable) { 7230 this._draggable = new Draggable(icon, icon, true); 7231 } 7232 7233 this._draggable.on({ 7234 dragstart: this._onDragStart, 7235 predrag: this._onPreDrag, 7236 drag: this._onDrag, 7237 dragend: this._onDragEnd 7238 }, this).enable(); 7239 7240 addClass(icon, 'leaflet-marker-draggable'); 7241 }, 7242 7243 removeHooks: function () { 7244 this._draggable.off({ 7245 dragstart: this._onDragStart, 7246 predrag: this._onPreDrag, 7247 drag: this._onDrag, 7248 dragend: this._onDragEnd 7249 }, this).disable(); 7250 7251 if (this._marker._icon) { 7252 removeClass(this._marker._icon, 'leaflet-marker-draggable'); 7253 } 7254 }, 7255 7256 moved: function () { 7257 return this._draggable && this._draggable._moved; 7258 }, 7259 7260 _adjustPan: function (e) { 7261 var marker = this._marker, 7262 map = marker._map, 7263 speed = this._marker.options.autoPanSpeed, 7264 padding = this._marker.options.autoPanPadding, 7265 iconPos = getPosition(marker._icon), 7266 bounds = map.getPixelBounds(), 7267 origin = map.getPixelOrigin(); 7268 7269 var panBounds = toBounds( 7270 bounds.min._subtract(origin).add(padding), 7271 bounds.max._subtract(origin).subtract(padding) 7272 ); 7273 7274 if (!panBounds.contains(iconPos)) { 7275 // Compute incremental movement 7276 var movement = toPoint( 7277 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) - 7278 (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x), 7279 7280 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) - 7281 (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y) 7282 ).multiplyBy(speed); 7283 7284 map.panBy(movement, {animate: false}); 7285 7286 this._draggable._newPos._add(movement); 7287 this._draggable._startPos._add(movement); 7288 7289 setPosition(marker._icon, this._draggable._newPos); 7290 this._onDrag(e); 7291 7292 this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); 7293 } 7294 }, 7295 7296 _onDragStart: function () { 7297 // @section Dragging events 7298 // @event dragstart: Event 7299 // Fired when the user starts dragging the marker. 7300 7301 // @event movestart: Event 7302 // Fired when the marker starts moving (because of dragging). 7303 7304 this._oldLatLng = this._marker.getLatLng(); 7305 7306 // When using ES6 imports it could not be set when `Popup` was not imported as well 7307 this._marker.closePopup && this._marker.closePopup(); 7308 7309 this._marker 7310 .fire('movestart') 7311 .fire('dragstart'); 7312 }, 7313 7314 _onPreDrag: function (e) { 7315 if (this._marker.options.autoPan) { 7316 cancelAnimFrame(this._panRequest); 7317 this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e)); 7318 } 7319 }, 7320 7321 _onDrag: function (e) { 7322 var marker = this._marker, 7323 shadow = marker._shadow, 7324 iconPos = getPosition(marker._icon), 7325 latlng = marker._map.layerPointToLatLng(iconPos); 7326 7327 // update shadow position 7328 if (shadow) { 7329 setPosition(shadow, iconPos); 7330 } 7331 7332 marker._latlng = latlng; 7333 e.latlng = latlng; 7334 e.oldLatLng = this._oldLatLng; 7335 7336 // @event drag: Event 7337 // Fired repeatedly while the user drags the marker. 7338 marker 7339 .fire('move', e) 7340 .fire('drag', e); 7341 }, 7342 7343 _onDragEnd: function (e) { 7344 // @event dragend: DragEndEvent 7345 // Fired when the user stops dragging the marker. 7346 7347 cancelAnimFrame(this._panRequest); 7348 7349 // @event moveend: Event 7350 // Fired when the marker stops moving (because of dragging). 7351 delete this._oldLatLng; 7352 this._marker 7353 .fire('moveend') 7354 .fire('dragend', e); 7355 } 7356 }); 7357 7358 /* 7359 * @class Marker 7360 * @inherits Interactive layer 7361 * @aka L.Marker 7362 * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`. 7363 * 7364 * @example 7365 * 7366 * ```js 7367 * L.marker([50.5, 30.5]).addTo(map); 7368 * ``` 7369 */ 7370 7371 var Marker = Layer.extend({ 7372 7373 // @section 7374 // @aka Marker options 7375 options: { 7376 // @option icon: Icon = * 7377 // Icon instance to use for rendering the marker. 7378 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon. 7379 // If not specified, a common instance of `L.Icon.Default` is used. 7380 icon: new IconDefault(), 7381 7382 // Option inherited from "Interactive layer" abstract class 7383 interactive: true, 7384 7385 // @option keyboard: Boolean = true 7386 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter. 7387 keyboard: true, 7388 7389 // @option title: String = '' 7390 // Text for the browser tooltip that appear on marker hover (no tooltip by default). 7391 title: '', 7392 7393 // @option alt: String = '' 7394 // Text for the `alt` attribute of the icon image (useful for accessibility). 7395 alt: '', 7396 7397 // @option zIndexOffset: Number = 0 7398 // 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). 7399 zIndexOffset: 0, 7400 7401 // @option opacity: Number = 1.0 7402 // The opacity of the marker. 7403 opacity: 1, 7404 7405 // @option riseOnHover: Boolean = false 7406 // If `true`, the marker will get on top of others when you hover the mouse over it. 7407 riseOnHover: false, 7408 7409 // @option riseOffset: Number = 250 7410 // The z-index offset used for the `riseOnHover` feature. 7411 riseOffset: 250, 7412 7413 // @option pane: String = 'markerPane' 7414 // `Map pane` where the markers icon will be added. 7415 pane: 'markerPane', 7416 7417 // @option shadowPane: String = 'shadowPane' 7418 // `Map pane` where the markers shadow will be added. 7419 shadowPane: 'shadowPane', 7420 7421 // @option bubblingMouseEvents: Boolean = false 7422 // When `true`, a mouse event on this marker will trigger the same event on the map 7423 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). 7424 bubblingMouseEvents: false, 7425 7426 // @section Draggable marker options 7427 // @option draggable: Boolean = false 7428 // Whether the marker is draggable with mouse/touch or not. 7429 draggable: false, 7430 7431 // @option autoPan: Boolean = false 7432 // Whether to pan the map when dragging this marker near its edge or not. 7433 autoPan: false, 7434 7435 // @option autoPanPadding: Point = Point(50, 50) 7436 // Distance (in pixels to the left/right and to the top/bottom) of the 7437 // map edge to start panning the map. 7438 autoPanPadding: [50, 50], 7439 7440 // @option autoPanSpeed: Number = 10 7441 // Number of pixels the map should pan by. 7442 autoPanSpeed: 10 7443 }, 7444 7445 /* @section 7446 * 7447 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods: 7448 */ 7449 7450 initialize: function (latlng, options) { 7451 setOptions(this, options); 7452 this._latlng = toLatLng(latlng); 7453 }, 7454 7455 onAdd: function (map) { 7456 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation; 7457 7458 if (this._zoomAnimated) { 7459 map.on('zoomanim', this._animateZoom, this); 7460 } 7461 7462 this._initIcon(); 7463 this.update(); 7464 }, 7465 7466 onRemove: function (map) { 7467 if (this.dragging && this.dragging.enabled()) { 7468 this.options.draggable = true; 7469 this.dragging.removeHooks(); 7470 } 7471 delete this.dragging; 7472 7473 if (this._zoomAnimated) { 7474 map.off('zoomanim', this._animateZoom, this); 7475 } 7476 7477 this._removeIcon(); 7478 this._removeShadow(); 7479 }, 7480 7481 getEvents: function () { 7482 return { 7483 zoom: this.update, 7484 viewreset: this.update 7485 }; 7486 }, 7487 7488 // @method getLatLng: LatLng 7489 // Returns the current geographical position of the marker. 7490 getLatLng: function () { 7491 return this._latlng; 7492 }, 7493 7494 // @method setLatLng(latlng: LatLng): this 7495 // Changes the marker position to the given point. 7496 setLatLng: function (latlng) { 7497 var oldLatLng = this._latlng; 7498 this._latlng = toLatLng(latlng); 7499 this.update(); 7500 7501 // @event move: Event 7502 // 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`. 7503 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); 7504 }, 7505 7506 // @method setZIndexOffset(offset: Number): this 7507 // Changes the [zIndex offset](#marker-zindexoffset) of the marker. 7508 setZIndexOffset: function (offset) { 7509 this.options.zIndexOffset = offset; 7510 return this.update(); 7511 }, 7512 7513 // @method getIcon: Icon 7514 // Returns the current icon used by the marker 7515 getIcon: function () { 7516 return this.options.icon; 7517 }, 7518 7519 // @method setIcon(icon: Icon): this 7520 // Changes the marker icon. 7521 setIcon: function (icon) { 7522 7523 this.options.icon = icon; 7524 7525 if (this._map) { 7526 this._initIcon(); 7527 this.update(); 7528 } 7529 7530 if (this._popup) { 7531 this.bindPopup(this._popup, this._popup.options); 7532 } 7533 7534 return this; 7535 }, 7536 7537 getElement: function () { 7538 return this._icon; 7539 }, 7540 7541 update: function () { 7542 7543 if (this._icon && this._map) { 7544 var pos = this._map.latLngToLayerPoint(this._latlng).round(); 7545 this._setPos(pos); 7546 } 7547 7548 return this; 7549 }, 7550 7551 _initIcon: function () { 7552 var options = this.options, 7553 classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); 7554 7555 var icon = options.icon.createIcon(this._icon), 7556 addIcon = false; 7557 7558 // if we're not reusing the icon, remove the old one and init new one 7559 if (icon !== this._icon) { 7560 if (this._icon) { 7561 this._removeIcon(); 7562 } 7563 addIcon = true; 7564 7565 if (options.title) { 7566 icon.title = options.title; 7567 } 7568 7569 if (icon.tagName === 'IMG') { 7570 icon.alt = options.alt || ''; 7571 } 7572 } 7573 7574 addClass(icon, classToAdd); 7575 7576 if (options.keyboard) { 7577 icon.tabIndex = '0'; 7578 } 7579 7580 this._icon = icon; 7581 7582 if (options.riseOnHover) { 7583 this.on({ 7584 mouseover: this._bringToFront, 7585 mouseout: this._resetZIndex 7586 }); 7587 } 7588 7589 var newShadow = options.icon.createShadow(this._shadow), 7590 addShadow = false; 7591 7592 if (newShadow !== this._shadow) { 7593 this._removeShadow(); 7594 addShadow = true; 7595 } 7596 7597 if (newShadow) { 7598 addClass(newShadow, classToAdd); 7599 newShadow.alt = ''; 7600 } 7601 this._shadow = newShadow; 7602 7603 7604 if (options.opacity < 1) { 7605 this._updateOpacity(); 7606 } 7607 7608 7609 if (addIcon) { 7610 this.getPane().appendChild(this._icon); 7611 } 7612 this._initInteraction(); 7613 if (newShadow && addShadow) { 7614 this.getPane(options.shadowPane).appendChild(this._shadow); 7615 } 7616 }, 7617 7618 _removeIcon: function () { 7619 if (this.options.riseOnHover) { 7620 this.off({ 7621 mouseover: this._bringToFront, 7622 mouseout: this._resetZIndex 7623 }); 7624 } 7625 7626 remove(this._icon); 7627 this.removeInteractiveTarget(this._icon); 7628 7629 this._icon = null; 7630 }, 7631 7632 _removeShadow: function () { 7633 if (this._shadow) { 7634 remove(this._shadow); 7635 } 7636 this._shadow = null; 7637 }, 7638 7639 _setPos: function (pos) { 7640 7641 if (this._icon) { 7642 setPosition(this._icon, pos); 7643 } 7644 7645 if (this._shadow) { 7646 setPosition(this._shadow, pos); 7647 } 7648 7649 this._zIndex = pos.y + this.options.zIndexOffset; 7650 7651 this._resetZIndex(); 7652 }, 7653 7654 _updateZIndex: function (offset) { 7655 if (this._icon) { 7656 this._icon.style.zIndex = this._zIndex + offset; 7657 } 7658 }, 7659 7660 _animateZoom: function (opt) { 7661 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); 7662 7663 this._setPos(pos); 7664 }, 7665 7666 _initInteraction: function () { 7667 7668 if (!this.options.interactive) { return; } 7669 7670 addClass(this._icon, 'leaflet-interactive'); 7671 7672 this.addInteractiveTarget(this._icon); 7673 7674 if (MarkerDrag) { 7675 var draggable = this.options.draggable; 7676 if (this.dragging) { 7677 draggable = this.dragging.enabled(); 7678 this.dragging.disable(); 7679 } 7680 7681 this.dragging = new MarkerDrag(this); 7682 7683 if (draggable) { 7684 this.dragging.enable(); 7685 } 7686 } 7687 }, 7688 7689 // @method setOpacity(opacity: Number): this 7690 // Changes the opacity of the marker. 7691 setOpacity: function (opacity) { 7692 this.options.opacity = opacity; 7693 if (this._map) { 7694 this._updateOpacity(); 7695 } 7696 7697 return this; 7698 }, 7699 7700 _updateOpacity: function () { 7701 var opacity = this.options.opacity; 7702 7703 if (this._icon) { 7704 setOpacity(this._icon, opacity); 7705 } 7706 7707 if (this._shadow) { 7708 setOpacity(this._shadow, opacity); 7709 } 7710 }, 7711 7712 _bringToFront: function () { 7713 this._updateZIndex(this.options.riseOffset); 7714 }, 7715 7716 _resetZIndex: function () { 7717 this._updateZIndex(0); 7718 }, 7719 7720 _getPopupAnchor: function () { 7721 return this.options.icon.options.popupAnchor; 7722 }, 7723 7724 _getTooltipAnchor: function () { 7725 return this.options.icon.options.tooltipAnchor; 7726 } 7727 }); 7728 7729 7730 // factory L.marker(latlng: LatLng, options? : Marker options) 7731 7732 // @factory L.marker(latlng: LatLng, options? : Marker options) 7733 // Instantiates a Marker object given a geographical point and optionally an options object. 7734 function marker(latlng, options) { 7735 return new Marker(latlng, options); 7736 } 7737 7738 /* 7739 * @class Path 7740 * @aka L.Path 7741 * @inherits Interactive layer 7742 * 7743 * An abstract class that contains options and constants shared between vector 7744 * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`. 7745 */ 7746 7747 var Path = Layer.extend({ 7748 7749 // @section 7750 // @aka Path options 7751 options: { 7752 // @option stroke: Boolean = true 7753 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles. 7754 stroke: true, 7755 7756 // @option color: String = '#3388ff' 7757 // Stroke color 7758 color: '#3388ff', 7759 7760 // @option weight: Number = 3 7761 // Stroke width in pixels 7762 weight: 3, 7763 7764 // @option opacity: Number = 1.0 7765 // Stroke opacity 7766 opacity: 1, 7767 7768 // @option lineCap: String= 'round' 7769 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke. 7770 lineCap: 'round', 7771 7772 // @option lineJoin: String = 'round' 7773 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke. 7774 lineJoin: 'round', 7775 7776 // @option dashArray: String = null 7777 // 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). 7778 dashArray: null, 7779 7780 // @option dashOffset: String = null 7781 // 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). 7782 dashOffset: null, 7783 7784 // @option fill: Boolean = depends 7785 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles. 7786 fill: false, 7787 7788 // @option fillColor: String = * 7789 // Fill color. Defaults to the value of the [`color`](#path-color) option 7790 fillColor: null, 7791 7792 // @option fillOpacity: Number = 0.2 7793 // Fill opacity. 7794 fillOpacity: 0.2, 7795 7796 // @option fillRule: String = 'evenodd' 7797 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined. 7798 fillRule: 'evenodd', 7799 7800 // className: '', 7801 7802 // Option inherited from "Interactive layer" abstract class 7803 interactive: true, 7804 7805 // @option bubblingMouseEvents: Boolean = true 7806 // When `true`, a mouse event on this path will trigger the same event on the map 7807 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used). 7808 bubblingMouseEvents: true 7809 }, 7810 7811 beforeAdd: function (map) { 7812 // Renderer is set here because we need to call renderer.getEvents 7813 // before this.getEvents. 7814 this._renderer = map.getRenderer(this); 7815 }, 7816 7817 onAdd: function () { 7818 this._renderer._initPath(this); 7819 this._reset(); 7820 this._renderer._addPath(this); 7821 }, 7822 7823 onRemove: function () { 7824 this._renderer._removePath(this); 7825 }, 7826 7827 // @method redraw(): this 7828 // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses. 7829 redraw: function () { 7830 if (this._map) { 7831 this._renderer._updatePath(this); 7832 } 7833 return this; 7834 }, 7835 7836 // @method setStyle(style: Path options): this 7837 // Changes the appearance of a Path based on the options in the `Path options` object. 7838 setStyle: function (style) { 7839 setOptions(this, style); 7840 if (this._renderer) { 7841 this._renderer._updateStyle(this); 7842 if (this.options.stroke && style && Object.prototype.hasOwnProperty.call(style, 'weight')) { 7843 this._updateBounds(); 7844 } 7845 } 7846 return this; 7847 }, 7848 7849 // @method bringToFront(): this 7850 // Brings the layer to the top of all path layers. 7851 bringToFront: function () { 7852 if (this._renderer) { 7853 this._renderer._bringToFront(this); 7854 } 7855 return this; 7856 }, 7857 7858 // @method bringToBack(): this 7859 // Brings the layer to the bottom of all path layers. 7860 bringToBack: function () { 7861 if (this._renderer) { 7862 this._renderer._bringToBack(this); 7863 } 7864 return this; 7865 }, 7866 7867 getElement: function () { 7868 return this._path; 7869 }, 7870 7871 _reset: function () { 7872 // defined in child classes 7873 this._project(); 7874 this._update(); 7875 }, 7876 7877 _clickTolerance: function () { 7878 // used when doing hit detection for Canvas layers 7879 return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance
vendor: 9,092 bytes, lines 7879-8201
7879; 7880 } 7881 }); 7882 7883 /* 7884 * @class CircleMarker 7885 * @aka L.CircleMarker 7886 * @inherits Path 7887 * 7888 * A circle of a fixed size with radius specified in pixels. Extends `Path`. 7889 */ 7890 7891 var CircleMarker = Path.extend({ 7892 7893 // @section 7894 // @aka CircleMarker options 7895 options: { 7896 fill: true, 7897 7898 // @option radius: Number = 10 7899 // Radius of the circle marker, in pixels 7900 radius: 10 7901 }, 7902 7903 initialize: function (latlng, options) { 7904 setOptions(this, options); 7905 this._latlng = toLatLng(latlng); 7906 this._radius = this.options.radius; 7907 }, 7908 7909 // @method setLatLng(latLng: LatLng): this 7910 // Sets the position of a circle marker to a new location. 7911 setLatLng: function (latlng) { 7912 var oldLatLng = this._latlng; 7913 this._latlng = toLatLng(latlng); 7914 this.redraw(); 7915 7916 // @event move: Event 7917 // Fired when the marker is moved via [`setLatLng`](#circlemarker-setlatlng). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`. 7918 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); 7919 }, 7920 7921 // @method getLatLng(): LatLng 7922 // Returns the current geographical position of the circle marker 7923 getLatLng: function () { 7924 return this._latlng; 7925 }, 7926 7927 // @method setRadius(radius: Number): this 7928 // Sets the radius of a circle marker. Units are in pixels. 7929 setRadius: function (radius) { 7930 this.options.radius = this._radius = radius; 7931 return this.redraw(); 7932 }, 7933 7934 // @method getRadius(): Number 7935 // Returns the current radius of the circle 7936 getRadius: function () { 7937 return this._radius; 7938 }, 7939 7940 setStyle : function (options) { 7941 var radius = options && options.radius || this._radius; 7942 Path.prototype.setStyle.call(this, options); 7943 this.setRadius(radius); 7944 return this; 7945 }, 7946 7947 _project: function () { 7948 this._point = this._map.latLngToLayerPoint(this._latlng); 7949 this._updateBounds(); 7950 }, 7951 7952 _updateBounds: function () { 7953 var r = this._radius, 7954 r2 = this._radiusY || r, 7955 w = this._clickTolerance(), 7956 p = [r + w, r2 + w]; 7957 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p)); 7958 }, 7959 7960 _update: function () { 7961 if (this._map) { 7962 this._updatePath(); 7963 } 7964 }, 7965 7966 _updatePath: function () { 7967 this._renderer._updateCircle(this); 7968 }, 7969 7970 _empty: function () { 7971 return this._radius && !this._renderer._bounds.intersects(this._pxBounds); 7972 }, 7973 7974 // Needed by the `Canvas` renderer for interactivity 7975 _containsPoint: function (p) { 7976 return p.distanceTo(this._point) <= this._radius + this._clickTolerance(); 7977 } 7978 }); 7979 7980 7981 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options) 7982 // Instantiates a circle marker object given a geographical point, and an optional options object. 7983 function circleMarker(latlng, options) { 7984 return new CircleMarker(latlng, options); 7985 } 7986 7987 /* 7988 * @class Circle 7989 * @aka L.Circle 7990 * @inherits CircleMarker 7991 * 7992 * A class for drawing circle overlays on a map. Extends `CircleMarker`. 7993 * 7994 * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion). 7995 * 7996 * @example 7997 * 7998 * ```js 7999 * L.circle([50.5, 30.5], {radius: 200}).addTo(map); 8000 * ``` 8001 */ 8002 8003 var Circle = CircleMarker.extend({ 8004 8005 initialize: function (latlng, options, legacyOptions) { 8006 if (typeof options === 'number') { 8007 // Backwards compatibility with 0.7.x factory (latlng, radius, options?) 8008 options = extend({}, legacyOptions, {radius: options}); 8009 } 8010 setOptions(this, options); 8011 this._latlng = toLatLng(latlng); 8012 8013 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); } 8014 8015 // @section 8016 // @aka Circle options 8017 // @option radius: Number; Radius of the circle, in meters. 8018 this._mRadius = this.options.radius; 8019 }, 8020 8021 // @method setRadius(radius: Number): this 8022 // Sets the radius of a circle. Units are in meters. 8023 setRadius: function (radius) { 8024 this._mRadius = radius; 8025 return this.redraw(); 8026 }, 8027 8028 // @method getRadius(): Number 8029 // Returns the current radius of a circle. Units are in meters. 8030 getRadius: function () { 8031 return this._mRadius; 8032 }, 8033 8034 // @method getBounds(): LatLngBounds 8035 // Returns the `LatLngBounds` of the path. 8036 getBounds: function () { 8037 var half = [this._radius, this._radiusY || this._radius]; 8038 8039 return new LatLngBounds( 8040 this._map.layerPointToLatLng(this._point.subtract(half)), 8041 this._map.layerPointToLatLng(this._point.add(half))); 8042 }, 8043 8044 setStyle: Path.prototype.setStyle, 8045 8046 _project: function () { 8047 8048 var lng = this._latlng.lng, 8049 lat = this._latlng.lat, 8050 map = this._map, 8051 crs = map.options.crs; 8052 8053 if (crs.distance === Earth.distance) { 8054 var d = Math.PI / 180, 8055 latR = (this._mRadius / Earth.R) / d, 8056 top = map.project([lat + latR, lng]), 8057 bottom = map.project([lat - latR, lng]), 8058 p = top.add(bottom).divideBy(2), 8059 lat2 = map.unproject(p).lat, 8060 lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) / 8061 (Math.cos(lat * d) * Math.cos(lat2 * d))) / d; 8062 8063 if (isNaN(lngR) || lngR === 0) { 8064 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425 8065 } 8066 8067 this._point = p.subtract(map.getPixelOrigin()); 8068 this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x; 8069 this._radiusY = p.y - top.y; 8070 8071 } else { 8072 var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0])); 8073 8074 this._point = map.latLngToLayerPoint(this._latlng); 8075 this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x; 8076 } 8077 8078 this._updateBounds(); 8079 } 8080 }); 8081 8082 // @factory L.circle(latlng: LatLng, options?: Circle options) 8083 // Instantiates a circle object given a geographical point, and an options object 8084 // which contains the circle radius. 8085 // @alternative 8086 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options) 8087 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code. 8088 // Do not use in new applications or plugins. 8089 function circle(latlng, options, legacyOptions) { 8090 return new Circle(latlng, options, legacyOptions); 8091 } 8092 8093 /* 8094 * @class Polyline 8095 * @aka L.Polyline 8096 * @inherits Path 8097 * 8098 * A class for drawing polyline overlays on a map. Extends `Path`. 8099 * 8100 * @example 8101 * 8102 * ```js 8103 * // create a red polyline from an array of LatLng points 8104 * var latlngs = [ 8105 * [45.51, -122.68], 8106 * [37.77, -122.43], 8107 * [34.04, -118.2] 8108 * ]; 8109 * 8110 * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map); 8111 * 8112 * // zoom the map to the polyline 8113 * map.fitBounds(polyline.getBounds()); 8114 * ``` 8115 * 8116 * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape: 8117 * 8118 * ```js 8119 * // create a red polyline from an array of arrays of LatLng points 8120 * var latlngs = [ 8121 * [[45.51, -122.68], 8122 * [37.77, -122.43], 8123 * [34.04, -118.2]], 8124 * [[40.78, -73.91], 8125 * [41.83, -87.62], 8126 * [32.76, -96.72]] 8127 * ]; 8128 * ``` 8129 */ 8130 8131 8132 var Polyline = Path.extend({ 8133 8134 // @section 8135 // @aka Polyline options 8136 options: { 8137 // @option smoothFactor: Number = 1.0 8138 // How much to simplify the polyline on each zoom level. More means 8139 // better performance and smoother look, and less means more accurate representation. 8140 smoothFactor: 1.0, 8141 8142 // @option noClip: Boolean = false 8143 // Disable polyline clipping. 8144 noClip: false 8145 }, 8146 8147 initialize: function (latlngs, options) { 8148 setOptions(this, options); 8149 this._setLatLngs(latlngs); 8150 }, 8151 8152 // @method getLatLngs(): LatLng[] 8153 // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline. 8154 getLatLngs: function () { 8155 return this._latlngs; 8156 }, 8157 8158 // @method setLatLngs(latlngs: LatLng[]): this 8159 // Replaces all the points in the polyline with the given array of geographical points. 8160 setLatLngs: function (latlngs) { 8161 this._setLatLngs(latlngs); 8162 return this.redraw(); 8163 }, 8164 8165 // @method isEmpty(): Boolean 8166 // Returns `true` if the Polyline has no LatLngs. 8167 isEmpty: function () { 8168 return !this._latlngs.length; 8169 }, 8170 8171 // @method closestLayerPoint(p: Point): Point 8172 // Returns the point closest to `p` on the Polyline. 8173 closestLayerPoint: function (p) { 8174 var minDistance = Infinity, 8175 minPoint = null, 8176 closest = _sqClosestPointOnSegment, 8177 p1, p2; 8178 8179 for (var j = 0, jLen = this._parts.length; j < jLen; j++) { 8180 var points = this._parts[j]; 8181 8182 for (var i = 1, len = points.length; i < len; i++) { 8183 p1 = points[i - 1]; 8184 p2 = points[i]; 8185 8186 var sqDist = closest(p, p1, p2, true); 8187 8188 if (sqDist < minDistance) { 8189 minDistance = sqDist; 8190 minPoint = closest(p, p1, p2); 8191 } 8192 } 8193 } 8194 if (minPoint) { 8195 minPoint.distance = Math.sqrt(minDistance); 8196 } 8197 return minPoint; 8198 }, 8199 8200 // @method getCenter(): LatLng 8201 // Returns the center ([centroid](http
8201://en.wikipedia.org/wiki/Centroid)) of the polyline. 8202 getCenter: function () { 8203 // throws error when not yet added to map as this center calculation requires projected coordinates 8204 if (!this._map) { 8205 throw new Error('Must add layer to map before using getCenter()'); 8206 } 8207 8208 var i, halfDist, segDist, dist, p1, p2, ratio, 8209 points = this._rings[0], 8210 len = points.length; 8211 8212 if (!len) { return null; } 8213 8214 // polyline centroid algorithm; only uses the first ring if there are multiple 8215 8216 for (i = 0, halfDist = 0; i < len - 1; i++) { 8217 halfDist += points[i].distanceTo(points[i + 1]) / 2; 8218 } 8219 8220 // The line is so small in the current view that all points are on the same pixel. 8221 if (halfDist === 0) { 8222 return this._map.layerPointToLatLng(points[0]); 8223 } 8224 8225 for (i = 0, dist = 0; i < len - 1; i++) { 8226 p1 = points[i]; 8227 p2 = points[i + 1]; 8228 segDist = p1.distanceTo(p2); 8229 dist += segDist; 8230 8231 if (dist > halfDist) { 8232 ratio = (dist - halfDist) / segDist; 8233 return this._map.layerPointToLatLng([ 8234 p2.x - ratio * (p2.x - p1.x), 8235 p2.y - ratio * (p2.y - p1.y) 8236 ]); 8237 } 8238 } 8239 }, 8240 8241 // @method getBounds(): LatLngBounds 8242 // Returns the `LatLngBounds` of the path.
vendor: 7,582 bytes, lines 8243-8507
8243 getBounds: function () { 8244 return this._bounds; 8245 }, 8246 8247 // @method addLatLng(latlng: LatLng, latlngs?: LatLng[]): this 8248 // Adds a given point to the polyline. By default, adds to the first ring of 8249 // the polyline in case of a multi-polyline, but can be overridden by passing 8250 // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)). 8251 addLatLng: function (latlng, latlngs) { 8252 latlngs = latlngs || this._defaultShape(); 8253 latlng = toLatLng(latlng); 8254 latlngs.push(latlng); 8255 this._bounds.extend(latlng); 8256 return this.redraw(); 8257 }, 8258 8259 _setLatLngs: function (latlngs) { 8260 this._bounds = new LatLngBounds(); 8261 this._latlngs = this._convertLatLngs(latlngs); 8262 }, 8263 8264 _defaultShape: function () { 8265 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0]; 8266 }, 8267 8268 // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way 8269 _convertLatLngs: function (latlngs) { 8270 var result = [], 8271 flat = isFlat(latlngs); 8272 8273 for (var i = 0, len = latlngs.length; i < len; i++) { 8274 if (flat) { 8275 result[i] = toLatLng(latlngs[i]); 8276 this._bounds.extend(result[i]); 8277 } else { 8278 result[i] = this._convertLatLngs(latlngs[i]); 8279 } 8280 } 8281 8282 return result; 8283 }, 8284 8285 _project: function () { 8286 var pxBounds = new Bounds(); 8287 this._rings = []; 8288 this._projectLatlngs(this._latlngs, this._rings, pxBounds); 8289 8290 if (this._bounds.isValid() && pxBounds.isValid()) { 8291 this._rawPxBounds = pxBounds; 8292 this._updateBounds(); 8293 } 8294 }, 8295 8296 _updateBounds: function () { 8297 var w = this._clickTolerance(), 8298 p = new Point(w, w); 8299 this._pxBounds = new Bounds([ 8300 this._rawPxBounds.min.subtract(p), 8301 this._rawPxBounds.max.add(p) 8302 ]); 8303 }, 8304 8305 // recursively turns latlngs into a set of rings with projected coordinates 8306 _projectLatlngs: function (latlngs, result, projectedBounds) { 8307 var flat = latlngs[0] instanceof LatLng, 8308 len = latlngs.length, 8309 i, ring; 8310 8311 if (flat) { 8312 ring = []; 8313 for (i = 0; i < len; i++) { 8314 ring[i] = this._map.latLngToLayerPoint(latlngs[i]); 8315 projectedBounds.extend(ring[i]); 8316 } 8317 result.push(ring); 8318 } else { 8319 for (i = 0; i < len; i++) { 8320 this._projectLatlngs(latlngs[i], result, projectedBounds); 8321 } 8322 } 8323 }, 8324 8325 // clip polyline by renderer bounds so that we have less to render for performance 8326 _clipPoints: function () { 8327 var bounds = this._renderer._bounds; 8328 8329 this._parts = []; 8330 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { 8331 return; 8332 } 8333 8334 if (this.options.noClip) { 8335 this._parts = this._rings; 8336 return; 8337 } 8338 8339 var parts = this._parts, 8340 i, j, k, len, len2, segment, points; 8341 8342 for (i = 0, k = 0, len = this._rings.length; i < len; i++) { 8343 points = this._rings[i]; 8344 8345 for (j = 0, len2 = points.length; j < len2 - 1; j++) { 8346 segment = clipSegment(points[j], points[j + 1], bounds, j, true); 8347 8348 if (!segment) { continue; } 8349 8350 parts[k] = parts[k] || []; 8351 parts[k].push(segment[0]); 8352 8353 // if segment goes out of screen, or it's the last one, it's the end of the line part 8354 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) { 8355 parts[k].push(segment[1]); 8356 k++; 8357 } 8358 } 8359 } 8360 }, 8361 8362 // simplify each clipped part of the polyline for performance 8363 _simplifyPoints: function () { 8364 var parts = this._parts, 8365 tolerance = this.options.smoothFactor; 8366 8367 for (var i = 0, len = parts.length; i < len; i++) { 8368 parts[i] = simplify(parts[i], tolerance); 8369 } 8370 }, 8371 8372 _update: function () { 8373 if (!this._map) { return; } 8374 8375 this._clipPoints(); 8376 this._simplifyPoints(); 8377 this._updatePath(); 8378 }, 8379 8380 _updatePath: function () { 8381 this._renderer._updatePoly(this); 8382 }, 8383 8384 // Needed by the `Canvas` renderer for interactivity 8385 _containsPoint: function (p, closed) { 8386 var i, j, k, len, len2, part, 8387 w = this._clickTolerance(); 8388 8389 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; } 8390 8391 // hit detection for polylines 8392 for (i = 0, len = this._parts.length; i < len; i++) { 8393 part = this._parts[i]; 8394 8395 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { 8396 if (!closed && (j === 0)) { continue; } 8397 8398 if (pointToSegmentDistance(p, part[k], part[j]) <= w) { 8399 return true; 8400 } 8401 } 8402 } 8403 return false; 8404 } 8405 }); 8406 8407 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options) 8408 // Instantiates a polyline object given an array of geographical points and 8409 // optionally an options object. You can create a `Polyline` object with 8410 // multiple separate lines (`MultiPolyline`) by passing an array of arrays 8411 // of geographic points. 8412 function polyline(latlngs, options) { 8413 return new Polyline(latlngs, options); 8414 } 8415 8416 // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1. 8417 Polyline._flat = _flat; 8418 8419 /* 8420 * @class Polygon 8421 * @aka L.Polygon 8422 * @inherits Polyline 8423 * 8424 * A class for drawing polygon overlays on a map. Extends `Polyline`. 8425 * 8426 * 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. 8427 * 8428 * 8429 * @example 8430 * 8431 * ```js 8432 * // create a red polygon from an array of LatLng points 8433 * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]]; 8434 * 8435 * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map); 8436 * 8437 * // zoom the map to the polygon 8438 * map.fitBounds(polygon.getBounds()); 8439 * ``` 8440 * 8441 * 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: 8442 * 8443 * ```js 8444 * var latlngs = [ 8445 * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring 8446 * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole 8447 * ]; 8448 * ``` 8449 * 8450 * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape. 8451 * 8452 * ```js 8453 * var latlngs = [ 8454 * [ // first polygon 8455 * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring 8456 * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole 8457 * ], 8458 * [ // second polygon 8459 * [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]] 8460 * ] 8461 * ]; 8462 * ``` 8463 */ 8464 8465 var Polygon = Polyline.extend({ 8466 8467 options: { 8468 fill: true 8469 }, 8470 8471 isEmpty: function () { 8472 return !this._latlngs.length || !this._latlngs[0].length; 8473 }, 8474 8475 getCenter: function () { 8476 // throws error when not yet added to map as this center calculation requires projected coordinates 8477 if (!this._map) { 8478 throw new Error('Must add layer to map before using getCenter()'); 8479 } 8480 8481 var i, j, p1, p2, f, area, x, y, center, 8482 points = this._rings[0], 8483 len = points.length; 8484 8485 if (!len) { return null; } 8486 8487 // polygon centroid algorithm; only uses the first ring if there are multiple 8488 8489 area = x = y = 0; 8490 8491 for (i = 0, j = len - 1; i < len; j = i++) { 8492 p1 = points[i]; 8493 p2 = points[j]; 8494 8495 f = p1.y * p2.x - p2.y * p1.x; 8496 x += (p1.x + p2.x) * f; 8497 y += (p1.y + p2.y) * f; 8498 area += f * 3; 8499 } 8500 8501 if (area === 0) { 8502 // Polygon is so small that all points are on same pixel. 8503 center = points[0]; 8504 } else { 8505 center = [x / area, y / area]; 8506 } 8507 return this._map.layerPointToLatLng(center
vendor: 8,784 bytes, lines 8507-8792
8507); 8508 }, 8509 8510 _convertLatLngs: function (latlngs) { 8511 var result = Polyline.prototype._convertLatLngs.call(this, latlngs), 8512 len = result.length; 8513 8514 // remove last point if it equals first one 8515 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) { 8516 result.pop(); 8517 } 8518 return result; 8519 }, 8520 8521 _setLatLngs: function (latlngs) { 8522 Polyline.prototype._setLatLngs.call(this, latlngs); 8523 if (isFlat(this._latlngs)) { 8524 this._latlngs = [this._latlngs]; 8525 } 8526 }, 8527 8528 _defaultShape: function () { 8529 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0]; 8530 }, 8531 8532 _clipPoints: function () { 8533 // polygons need a different clipping algorithm so we redefine that 8534 8535 var bounds = this._renderer._bounds, 8536 w = this.options.weight, 8537 p = new Point(w, w); 8538 8539 // increase clip padding by stroke width to avoid stroke on clip edges 8540 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p)); 8541 8542 this._parts = []; 8543 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) { 8544 return; 8545 } 8546 8547 if (this.options.noClip) { 8548 this._parts = this._rings; 8549 return; 8550 } 8551 8552 for (var i = 0, len = this._rings.length, clipped; i < len; i++) { 8553 clipped = clipPolygon(this._rings[i], bounds, true); 8554 if (clipped.length) { 8555 this._parts.push(clipped); 8556 } 8557 } 8558 }, 8559 8560 _updatePath: function () { 8561 this._renderer._updatePoly(this, true); 8562 }, 8563 8564 // Needed by the `Canvas` renderer for interactivity 8565 _containsPoint: function (p) { 8566 var inside = false, 8567 part, p1, p2, i, j, k, len, len2; 8568 8569 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; } 8570 8571 // ray casting algorithm for detecting if point is in polygon 8572 for (i = 0, len = this._parts.length; i < len; i++) { 8573 part = this._parts[i]; 8574 8575 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { 8576 p1 = part[j]; 8577 p2 = part[k]; 8578 8579 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)) { 8580 inside = !inside; 8581 } 8582 } 8583 } 8584 8585 // also check if it's on polygon stroke 8586 return inside || Polyline.prototype._containsPoint.call(this, p, true); 8587 } 8588 8589 }); 8590 8591 8592 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options) 8593 function polygon(latlngs, options) { 8594 return new Polygon(latlngs, options); 8595 } 8596 8597 /* 8598 * @class GeoJSON 8599 * @aka L.GeoJSON 8600 * @inherits FeatureGroup 8601 * 8602 * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse 8603 * GeoJSON data and display it on the map. Extends `FeatureGroup`. 8604 * 8605 * @example 8606 * 8607 * ```js 8608 * L.geoJSON(data, { 8609 * style: function (feature) { 8610 * return {color: feature.properties.color}; 8611 * } 8612 * }).bindPopup(function (layer) { 8613 * return layer.feature.properties.description; 8614 * }).addTo(map); 8615 * ``` 8616 */ 8617 8618 var GeoJSON = FeatureGroup.extend({ 8619 8620 /* @section 8621 * @aka GeoJSON options 8622 * 8623 * @option pointToLayer: Function = * 8624 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally 8625 * called when data is added, passing the GeoJSON point feature and its `LatLng`. 8626 * The default is to spawn a default `Marker`: 8627 * ```js 8628 * function(geoJsonPoint, latlng) { 8629 * return L.marker(latlng); 8630 * } 8631 * ``` 8632 * 8633 * @option style: Function = * 8634 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons, 8635 * called internally when data is added. 8636 * The default value is to not override any defaults: 8637 * ```js 8638 * function (geoJsonFeature) { 8639 * return {} 8640 * } 8641 * ``` 8642 * 8643 * @option onEachFeature: Function = * 8644 * A `Function` that will be called once for each created `Feature`, after it has 8645 * been created and styled. Useful for attaching events and popups to features. 8646 * The default is to do nothing with the newly created layers: 8647 * ```js 8648 * function (feature, layer) {} 8649 * ``` 8650 * 8651 * @option filter: Function = * 8652 * A `Function` that will be used to decide whether to include a feature or not. 8653 * The default is to include all features: 8654 * ```js 8655 * function (geoJsonFeature) { 8656 * return true; 8657 * } 8658 * ``` 8659 * Note: dynamically changing the `filter` option will have effect only on newly 8660 * added data. It will _not_ re-evaluate already included features. 8661 * 8662 * @option coordsToLatLng: Function = * 8663 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s. 8664 * The default is the `coordsToLatLng` static method. 8665 * 8666 * @option markersInheritOptions: Boolean = false 8667 * Whether default Markers for "Point" type Features inherit from group options. 8668 */ 8669 8670 initialize: function (geojson, options) { 8671 setOptions(this, options); 8672 8673 this._layers = {}; 8674 8675 if (geojson) { 8676 this.addData(geojson); 8677 } 8678 }, 8679 8680 // @method addData( <GeoJSON> data ): this 8681 // Adds a GeoJSON object to the layer. 8682 addData: function (geojson) { 8683 var features = isArray(geojson) ? geojson : geojson.features, 8684 i, len, feature; 8685 8686 if (features) { 8687 for (i = 0, len = features.length; i < len; i++) { 8688 // only add this if geometry or geometries are set and not null 8689 feature = features[i]; 8690 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) { 8691 this.addData(feature); 8692 } 8693 } 8694 return this; 8695 } 8696 8697 var options = this.options; 8698 8699 if (options.filter && !options.filter(geojson)) { return this; } 8700 8701 var layer = geometryToLayer(geojson, options); 8702 if (!layer) { 8703 return this; 8704 } 8705 layer.feature = asFeature(geojson); 8706 8707 layer.defaultOptions = layer.options; 8708 this.resetStyle(layer); 8709 8710 if (options.onEachFeature) { 8711 options.onEachFeature(geojson, layer); 8712 } 8713 8714 return this.addLayer(layer); 8715 }, 8716 8717 // @method resetStyle( <Path> layer? ): this 8718 // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events. 8719 // If `layer` is omitted, the style of all features in the current layer is reset. 8720 resetStyle: function (layer) { 8721 if (layer === undefined) { 8722 return this.eachLayer(this.resetStyle, this); 8723 } 8724 // reset any custom styles 8725 layer.options = extend({}, layer.defaultOptions); 8726 this._setLayerStyle(layer, this.options.style); 8727 return this; 8728 }, 8729 8730 // @method setStyle( <Function> style ): this 8731 // Changes styles of GeoJSON vector layers with the given style function. 8732 setStyle: function (style) { 8733 return this.eachLayer(function (layer) { 8734 this._setLayerStyle(layer, style); 8735 }, this); 8736 }, 8737 8738 _setLayerStyle: function (layer, style) { 8739 if (layer.setStyle) { 8740 if (typeof style === 'function') { 8741 style = style(layer.feature); 8742 } 8743 layer.setStyle(style); 8744 } 8745 } 8746 }); 8747 8748 // @section 8749 // There are several static functions which can be called without instantiating L.GeoJSON: 8750 8751 // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer 8752 // Creates a `Layer` from a given GeoJSON feature. Can use a custom 8753 // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng) 8754 // functions if provided as options. 8755 function geometryToLayer(geojson, options) { 8756 8757 var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson, 8758 coords = geometry ? geometry.coordinates : null, 8759 layers = [], 8760 pointToLayer = options && options.pointToLayer, 8761 _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng, 8762 latlng, latlngs, i, len; 8763 8764 if (!coords && !geometry) { 8765 return null; 8766 } 8767 8768 switch (geometry.type) { 8769 case 'Point': 8770 latlng = _coordsToLatLng(coords); 8771 return _pointToLayer(pointToLayer, geojson, latlng, options); 8772 8773 case 'MultiPoint': 8774 for (i = 0, len = coords.length; i < len; i++) { 8775 latlng = _coordsToLatLng(coords[i]); 8776 layers.push(_pointToLayer(pointToLayer, geojson, latlng, options)); 8777 } 8778 return new FeatureGroup(layers); 8779 8780 case 'LineString': 8781 case 'MultiLineString': 8782 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng); 8783 return new Polyline(latlngs, options); 8784 8785 case 'Polygon': 8786 case 'MultiPolygon': 8787 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng); 8788 return new Polygon(latlngs, options); 8789 8790 case 'GeometryCollection': 8791 for (i = 0, len = geometry.geometries.length; i < len; i++) { 8792 var
8792layer = geometryToLayer({ 8793 geometry: geometry.geometries[i], 8794 type: 'Feature', 8795 properties: geojson.properties 8796 }, options); 8797 8798 if (layer) { 8799 layers.push(layer); 8800 } 8801 } 8802 return new FeatureGroup(layers); 8803 8804 default: 8805 throw new Error('Invalid GeoJSON object.'); 8806 } 8807 } 8808 8809 function _pointToLayer(pointToLayerFn, geojson, latlng, options) { 8810 return pointToLayerFn ? 8811 pointToLayerFn(geojson, latlng) : 8812 new Marker(latlng, options && options.markersInheritOptions && options); 8813 } 8814 8815 // @function coordsToLatLng(coords: Array): LatLng 8816 // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude) 8817 // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points. 8818 function coordsToLatLng(coords) { 8819 return new LatLng(coords[1], coords[0], coords[2]); 8820 } 8821 8822 // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array 8823 // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array. 8824 // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default). 8825 // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function. 8826 function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) { 8827 var latlngs = []; 8828 8829 for (var i = 0, len = coords.length, latlng; i < len; i++) { 8830 latlng = levelsDeep ? 8831 coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) : 8832 (_coordsToLatLng || coordsToLatLng)(coords[i]); 8833 8834 latlngs.push(latlng); 8835 } 8836 8837 return latlngs; 8838 } 8839 8840 // @function latLngToCoords(latlng: LatLng, precision?: Number): Array 8841 // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng) 8842 function latLngToCoords(latlng, precision) { 8843 precision = typeof precision === 'number' ? precision : 6; 8844 return latlng.alt !== undefined ? 8845 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] : 8846 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)]; 8847 } 8848 8849 // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number,
vendor: 32,065 bytes, lines 8849-9898
8849closed?: Boolean): Array 8850 // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs) 8851 // `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. 8852 function latLngsToCoords(latlngs, levelsDeep, closed, precision) { 8853 var coords = []; 8854 8855 for (var i = 0, len = latlngs.length; i < len; i++) { 8856 coords.push(levelsDeep ? 8857 latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) : 8858 latLngToCoords(latlngs[i], precision)); 8859 } 8860 8861 if (!levelsDeep && closed) { 8862 coords.push(coords[0]); 8863 } 8864 8865 return coords; 8866 } 8867 8868 function getFeature(layer, newGeometry) { 8869 return layer.feature ? 8870 extend({}, layer.feature, {geometry: newGeometry}) : 8871 asFeature(newGeometry); 8872 } 8873 8874 // @function asFeature(geojson: Object): Object 8875 // Normalize GeoJSON geometries/features into GeoJSON features. 8876 function asFeature(geojson) { 8877 if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') { 8878 return geojson; 8879 } 8880 8881 return { 8882 type: 'Feature', 8883 properties: {}, 8884 geometry: geojson 8885 }; 8886 } 8887 8888 var PointToGeoJSON = { 8889 toGeoJSON: function (precision) { 8890 return getFeature(this, { 8891 type: 'Point', 8892 coordinates: latLngToCoords(this.getLatLng(), precision) 8893 }); 8894 } 8895 }; 8896 8897 // @namespace Marker 8898 // @section Other methods 8899 // @method toGeoJSON(precision?: Number): Object 8900 // `precision` is the number of decimal places for coordinates. 8901 // The default value is 6 places. 8902 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature). 8903 Marker.include(PointToGeoJSON); 8904 8905 // @namespace CircleMarker 8906 // @method toGeoJSON(precision?: Number): Object 8907 // `precision` is the number of decimal places for coordinates. 8908 // The default value is 6 places. 8909 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature). 8910 Circle.include(PointToGeoJSON); 8911 CircleMarker.include(PointToGeoJSON); 8912 8913 8914 // @namespace Polyline 8915 // @method toGeoJSON(precision?: Number): Object 8916 // `precision` is the number of decimal places for coordinates. 8917 // The default value is 6 places. 8918 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature). 8919 Polyline.include({ 8920 toGeoJSON: function (precision) { 8921 var multi = !isFlat(this._latlngs); 8922 8923 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision); 8924 8925 return getFeature(this, { 8926 type: (multi ? 'Multi' : '') + 'LineString', 8927 coordinates: coords 8928 }); 8929 } 8930 }); 8931 8932 // @namespace Polygon 8933 // @method toGeoJSON(precision?: Number): Object 8934 // `precision` is the number of decimal places for coordinates. 8935 // The default value is 6 places. 8936 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature). 8937 Polygon.include({ 8938 toGeoJSON: function (precision) { 8939 var holes = !isFlat(this._latlngs), 8940 multi = holes && !isFlat(this._latlngs[0]); 8941 8942 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision); 8943 8944 if (!holes) { 8945 coords = [coords]; 8946 } 8947 8948 return getFeature(this, { 8949 type: (multi ? 'Multi' : '') + 'Polygon', 8950 coordinates: coords 8951 }); 8952 } 8953 }); 8954 8955 8956 // @namespace LayerGroup 8957 LayerGroup.include({ 8958 toMultiPoint: function (precision) { 8959 var coords = []; 8960 8961 this.eachLayer(function (layer) { 8962 coords.push(layer.toGeoJSON(precision).geometry.coordinates); 8963 }); 8964 8965 return getFeature(this, { 8966 type: 'MultiPoint', 8967 coordinates: coords 8968 }); 8969 }, 8970 8971 // @method toGeoJSON(precision?: Number): Object 8972 // `precision` is the number of decimal places for coordinates. 8973 // The default value is 6 places. 8974 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`). 8975 toGeoJSON: function (precision) { 8976 8977 var type = this.feature && this.feature.geometry && this.feature.geometry.type; 8978 8979 if (type === 'MultiPoint') { 8980 return this.toMultiPoint(precision); 8981 } 8982 8983 var isGeometryCollection = type === 'GeometryCollection', 8984 jsons = []; 8985 8986 this.eachLayer(function (layer) { 8987 if (layer.toGeoJSON) { 8988 var json = layer.toGeoJSON(precision); 8989 if (isGeometryCollection) { 8990 jsons.push(json.geometry); 8991 } else { 8992 var feature = asFeature(json); 8993 // Squash nested feature collections 8994 if (feature.type === 'FeatureCollection') { 8995 jsons.push.apply(jsons, feature.features); 8996 } else { 8997 jsons.push(feature); 8998 } 8999 } 9000 } 9001 }); 9002 9003 if (isGeometryCollection) { 9004 return getFeature(this, { 9005 geometries: jsons, 9006 type: 'GeometryCollection' 9007 }); 9008 } 9009 9010 return { 9011 type: 'FeatureCollection', 9012 features: jsons 9013 }; 9014 } 9015 }); 9016 9017 // @namespace GeoJSON 9018 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options) 9019 // Creates a GeoJSON layer. Optionally accepts an object in 9020 // [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map 9021 // (you can alternatively add it later with `addData` method) and an `options` object. 9022 function geoJSON(geojson, options) { 9023 return new GeoJSON(geojson, options); 9024 } 9025 9026 // Backward compatibility. 9027 var geoJson = geoJSON; 9028 9029 /* 9030 * @class ImageOverlay 9031 * @aka L.ImageOverlay 9032 * @inherits Interactive layer 9033 * 9034 * Used to load and display a single image over specific bounds of the map. Extends `Layer`. 9035 * 9036 * @example 9037 * 9038 * ```js 9039 * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg', 9040 * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]]; 9041 * L.imageOverlay(imageUrl, imageBounds).addTo(map); 9042 * ``` 9043 */ 9044 9045 var ImageOverlay = Layer.extend({ 9046 9047 // @section 9048 // @aka ImageOverlay options 9049 options: { 9050 // @option opacity: Number = 1.0 9051 // The opacity of the image overlay. 9052 opacity: 1, 9053 9054 // @option alt: String = '' 9055 // Text for the `alt` attribute of the image (useful for accessibility). 9056 alt: '', 9057 9058 // @option interactive: Boolean = false 9059 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered. 9060 interactive: false, 9061 9062 // @option crossOrigin: Boolean|String = false 9063 // Whether the crossOrigin attribute will be added to the image. 9064 // 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. 9065 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values. 9066 crossOrigin: false, 9067 9068 // @option errorOverlayUrl: String = '' 9069 // URL to the overlay image to show in place of the overlay that failed to load. 9070 errorOverlayUrl: '', 9071 9072 // @option zIndex: Number = 1 9073 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer. 9074 zIndex: 1, 9075 9076 // @option className: String = '' 9077 // A custom class name to assign to the image. Empty by default. 9078 className: '' 9079 }, 9080 9081 initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) 9082 this._url = url; 9083 this._bounds = toLatLngBounds(bounds); 9084 9085 setOptions(this, options); 9086 }, 9087 9088 onAdd: function () { 9089 if (!this._image) { 9090 this._initImage(); 9091 9092 if (this.options.opacity < 1) { 9093 this._updateOpacity(); 9094 } 9095 } 9096 9097 if (this.options.interactive) { 9098 addClass(this._image, 'leaflet-interactive'); 9099 this.addInteractiveTarget(this._image); 9100 } 9101 9102 this.getPane().appendChild(this._image); 9103 this._reset(); 9104 }, 9105 9106 onRemove: function () { 9107 remove(this._image); 9108 if (this.options.interactive) { 9109 this.removeInteractiveTarget(this._image); 9110 } 9111 }, 9112 9113 // @method setOpacity(opacity: Number): this 9114 // Sets the opacity of the overlay. 9115 setOpacity: function (opacity) { 9116 this.options.opacity = opacity; 9117 9118 if (this._image) { 9119 this._updateOpacity(); 9120 } 9121 return this; 9122 }, 9123 9124 setStyle: function (styleOpts) { 9125 if (styleOpts.opacity) { 9126 this.setOpacity(styleOpts.opacity); 9127 } 9128 return this; 9129 }, 9130 9131 // @method bringToFront(): this 9132 // Brings the layer to the top of all overlays. 9133 bringToFront: function () { 9134 if (this._map) { 9135 toFront(this._image); 9136 } 9137 return this; 9138 }, 9139 9140 // @method bringToBack(): this 9141 // Brings the layer to the bottom of all overlays. 9142 bringToBack: function () { 9143 if (this._map) { 9144 toBack(this._image); 9145 } 9146 return this; 9147 }, 9148 9149 // @method setUrl(url: String): this 9150 // Changes the URL of the image. 9151 setUrl: function (url) { 9152 this._url = url; 9153 9154 if (this._image) { 9155 this._image.src = url; 9156 } 9157 return this; 9158 }, 9159 9160 // @method setBounds(bounds: LatLngBounds): this 9161 // Update the bounds that this ImageOverlay covers 9162 setBounds: function (bounds) { 9163 this._bounds = toLatLngBounds(bounds); 9164 9165 if (this._map) { 9166 this._reset(); 9167 } 9168 return this; 9169 }, 9170 9171 getEvents: function () { 9172 var events = { 9173 zoom: this._reset, 9174 viewreset: this._reset 9175 }; 9176 9177 if (this._zoomAnimated) { 9178 events.zoomanim = this._animateZoom; 9179 } 9180 9181 return events; 9182 }, 9183 9184 // @method setZIndex(value: Number): this 9185 // Changes the [zIndex](#imageoverlay-zindex) of the image overlay. 9186 setZIndex: function (value) { 9187 this.options.zIndex = value; 9188 this._updateZIndex(); 9189 return this; 9190 }, 9191 9192 // @method getBounds(): LatLngBounds 9193 // Get the bounds that this ImageOverlay covers 9194 getBounds: function () { 9195 return this._bounds; 9196 }, 9197 9198 // @method getElement(): HTMLElement 9199 // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement) 9200 // used by this overlay. 9201 getElement: function () { 9202 return this._image; 9203 }, 9204 9205 _initImage: function () { 9206 var wasElementSupplied = this._url.tagName === 'IMG'; 9207 var img = this._image = wasElementSupplied ? this._url : create$1('img'); 9208 9209 addClass(img, 'leaflet-image-layer'); 9210 if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); } 9211 if (this.options.className) { addClass(img, this.options.className); } 9212 9213 img.onselectstart = falseFn; 9214 img.onmousemove = falseFn; 9215 9216 // @event load: Event 9217 // Fired when the ImageOverlay layer has loaded its image 9218 img.onload = bind(this.fire, this, 'load'); 9219 img.onerror = bind(this._overlayOnError, this, 'error'); 9220 9221 if (this.options.crossOrigin || this.options.crossOrigin === '') { 9222 img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin; 9223 } 9224 9225 if (this.options.zIndex) { 9226 this._updateZIndex(); 9227 } 9228 9229 if (wasElementSupplied) { 9230 this._url = img.src; 9231 return; 9232 } 9233 9234 img.src = this._url; 9235 img.alt = this.options.alt; 9236 }, 9237 9238 _animateZoom: function (e) { 9239 var scale = this._map.getZoomScale(e.zoom), 9240 offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min; 9241 9242 setTransform(this._image, offset, scale); 9243 }, 9244 9245 _reset: function () { 9246 var image = this._image, 9247 bounds = new Bounds( 9248 this._map.latLngToLayerPoint(this._bounds.getNorthWest()), 9249 this._map.latLngToLayerPoint(this._bounds.getSouthEast())), 9250 size = bounds.getSize(); 9251 9252 setPosition(image, bounds.min); 9253 9254 image.style.width = size.x + 'px'; 9255 image.style.height = size.y + 'px'; 9256 }, 9257 9258 _updateOpacity: function () { 9259 setOpacity(this._image, this.options.opacity); 9260 }, 9261 9262 _updateZIndex: function () { 9263 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) { 9264 this._image.style.zIndex = this.options.zIndex; 9265 } 9266 }, 9267 9268 _overlayOnError: function () { 9269 // @event error: Event 9270 // Fired when the ImageOverlay layer fails to load its image 9271 this.fire('error'); 9272 9273 var errorUrl = this.options.errorOverlayUrl; 9274 if (errorUrl && this._url !== errorUrl) { 9275 this._url = errorUrl; 9276 this._image.src = errorUrl; 9277 } 9278 } 9279 }); 9280 9281 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options) 9282 // Instantiates an image overlay object given the URL of the image and the 9283 // geographical bounds it is tied to. 9284 var imageOverlay = function (url, bounds, options) { 9285 return new ImageOverlay(url, bounds, options); 9286 }; 9287 9288 /* 9289 * @class VideoOverlay 9290 * @aka L.VideoOverlay 9291 * @inherits ImageOverlay 9292 * 9293 * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`. 9294 * 9295 * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video) 9296 * HTML5 element. 9297 * 9298 * @example 9299 * 9300 * ```js 9301 * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm', 9302 * videoBounds = [[ 32, -130], [ 13, -100]]; 9303 * L.videoOverlay(videoUrl, videoBounds ).addTo(map); 9304 * ``` 9305 */ 9306 9307 var VideoOverlay = ImageOverlay.extend({ 9308 9309 // @section 9310 // @aka VideoOverlay options 9311 options: { 9312 // @option autoplay: Boolean = true 9313 // Whether the video starts playing automatically when loaded. 9314 autoplay: true, 9315 9316 // @option loop: Boolean = true 9317 // Whether the video will loop back to the beginning when played. 9318 loop: true, 9319 9320 // @option keepAspectRatio: Boolean = true 9321 // Whether the video will save aspect ratio after the projection. 9322 // Relevant for supported browsers. Browser compatibility- https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit 9323 keepAspectRatio: true, 9324 9325 // @option muted: Boolean = false 9326 // Whether the video starts on mute when loaded. 9327 muted: false 9328 }, 9329 9330 _initImage: function () { 9331 var wasElementSupplied = this._url.tagName === 'VIDEO'; 9332 var vid = this._image = wasElementSupplied ? this._url : create$1('video'); 9333 9334 addClass(vid, 'leaflet-image-layer'); 9335 if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); } 9336 if (this.options.className) { addClass(vid, this.options.className); } 9337 9338 vid.onselectstart = falseFn; 9339 vid.onmousemove = falseFn; 9340 9341 // @event load: Event 9342 // Fired when the video has finished loading the first frame 9343 vid.onloadeddata = bind(this.fire, this, 'load'); 9344 9345 if (wasElementSupplied) { 9346 var sourceElements = vid.getElementsByTagName('source'); 9347 var sources = []; 9348 for (var j = 0; j < sourceElements.length; j++) { 9349 sources.push(sourceElements[j].src); 9350 } 9351 9352 this._url = (sourceElements.length > 0) ? sources : [vid.src]; 9353 return; 9354 } 9355 9356 if (!isArray(this._url)) { this._url = [this._url]; } 9357 9358 if (!this.options.keepAspectRatio && Object.prototype.hasOwnProperty.call(vid.style, 'objectFit')) { 9359 vid.style['objectFit'] = 'fill'; 9360 } 9361 vid.autoplay = !!this.options.autoplay; 9362 vid.loop = !!this.options.loop; 9363 vid.muted = !!this.options.muted; 9364 for (var i = 0; i < this._url.length; i++) { 9365 var source = create$1('source'); 9366 source.src = this._url[i]; 9367 vid.appendChild(source); 9368 } 9369 } 9370 9371 // @method getElement(): HTMLVideoElement 9372 // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement) 9373 // used by this overlay. 9374 }); 9375 9376 9377 // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options) 9378 // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the 9379 // geographical bounds it is tied to. 9380 9381 function videoOverlay(video, bounds, options) { 9382 return new VideoOverlay(video, bounds, options); 9383 } 9384 9385 /* 9386 * @class SVGOverlay 9387 * @aka L.SVGOverlay 9388 * @inherits ImageOverlay 9389 * 9390 * Used to load, display and provide DOM access to an SVG file over specific bounds of the map. Extends `ImageOverlay`. 9391 * 9392 * An SVG overlay uses the [`<svg>`](https://developer.mozilla.org/docs/Web/SVG/Element/svg) element. 9393 * 9394 * @example 9395 * 9396 * ```js 9397 * var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg"); 9398 * svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg"); 9399 * svgElement.setAttribute('viewBox', "0 0 200 200"); 9400 * svgElement.innerHTML = '<rect width="200" height="200"/><rect x="75" y="23" width="50" height="50" style="fill:red"/><rect x="75" y="123" width="50" height="50" style="fill:#0013ff"/>'; 9401 * var svgElementBounds = [ [ 32, -130 ], [ 13, -100 ] ]; 9402 * L.svgOverlay(svgElement, svgElementBounds).addTo(map); 9403 * ``` 9404 */ 9405 9406 var SVGOverlay = ImageOverlay.extend({ 9407 _initImage: function () { 9408 var el = this._image = this._url; 9409 9410 addClass(el, 'leaflet-image-layer'); 9411 if (this._zoomAnimated) { addClass(el, 'leaflet-zoom-animated'); } 9412 if (this.options.className) { addClass(el, this.options.className); } 9413 9414 el.onselectstart = falseFn; 9415 el.onmousemove = falseFn; 9416 } 9417 9418 // @method getElement(): SVGElement 9419 // Returns the instance of [`SVGElement`](https://developer.mozilla.org/docs/Web/API/SVGElement) 9420 // used by this overlay. 9421 }); 9422 9423 9424 // @factory L.svgOverlay(svg: String|SVGElement, bounds: LatLngBounds, options?: SVGOverlay options) 9425 // Instantiates an image overlay object given an SVG element and the geographical bounds it is tied to. 9426 // A viewBox attribute is required on the SVG element to zoom in and out properly. 9427 9428 function svgOverlay(el, bounds, options) { 9429 return new SVGOverlay(el, bounds, options); 9430 } 9431 9432 /* 9433 * @class DivOverlay 9434 * @inherits Layer 9435 * @aka L.DivOverlay 9436 * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins. 9437 */ 9438 9439 // @namespace DivOverlay 9440 var DivOverlay = Layer.extend({ 9441 9442 // @section 9443 // @aka DivOverlay options 9444 options: { 9445 // @option offset: Point = Point(0, 7) 9446 // The offset of the popup position. Useful to control the anchor 9447 // of the popup when opening it on some overlays. 9448 offset: [0, 7], 9449 9450 // @option className: String = '' 9451 // A custom CSS class name to assign to the popup. 9452 className: '', 9453 9454 // @option pane: String = 'popupPane' 9455 // `Map pane` where the popup will be added. 9456 pane: 'popupPane' 9457 }, 9458 9459 initialize: function (options, source) { 9460 setOptions(this, options); 9461 9462 this._source = source; 9463 }, 9464 9465 onAdd: function (map) { 9466 this._zoomAnimated = map._zoomAnimated; 9467 9468 if (!this._container) { 9469 this._initLayout(); 9470 } 9471 9472 if (map._fadeAnimated) { 9473 setOpacity(this._container, 0); 9474 } 9475 9476 clearTimeout(this._removeTimeout); 9477 this.getPane().appendChild(this._container); 9478 this.update(); 9479 9480 if (map._fadeAnimated) { 9481 setOpacity(this._container, 1); 9482 } 9483 9484 this.bringToFront(); 9485 }, 9486 9487 onRemove: function (map) { 9488 if (map._fadeAnimated) { 9489 setOpacity(this._container, 0); 9490 this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200); 9491 } else { 9492 remove(this._container); 9493 } 9494 }, 9495 9496 // @namespace Popup 9497 // @method getLatLng: LatLng 9498 // Returns the geographical point of popup. 9499 getLatLng: function () { 9500 return this._latlng; 9501 }, 9502 9503 // @method setLatLng(latlng: LatLng): this 9504 // Sets the geographical point where the popup will open. 9505 setLatLng: function (latlng) { 9506 this._latlng = toLatLng(latlng); 9507 if (this._map) { 9508 this._updatePosition(); 9509 this._adjustPan(); 9510 } 9511 return this; 9512 }, 9513 9514 // @method getContent: String|HTMLElement 9515 // Returns the content of the popup. 9516 getContent: function () { 9517 return this._content; 9518 }, 9519 9520 // @method setContent(htmlContent: String|HTMLElement|Function): this 9521 // 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. 9522 setContent: function (content) { 9523 this._content = content; 9524 this.update(); 9525 return this; 9526 }, 9527 9528 // @method getElement: String|HTMLElement 9529 // Returns the HTML container of the popup. 9530 getElement: function () { 9531 return this._container; 9532 }, 9533 9534 // @method update: null 9535 // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded. 9536 update: function () { 9537 if (!this._map) { return; } 9538 9539 this._container.style.visibility = 'hidden'; 9540 9541 this._updateContent(); 9542 this._updateLayout(); 9543 this._updatePosition(); 9544 9545 this._container.style.visibility = ''; 9546 9547 this._adjustPan(); 9548 }, 9549 9550 getEvents: function () { 9551 var events = { 9552 zoom: this._updatePosition, 9553 viewreset: this._updatePosition 9554 }; 9555 9556 if (this._zoomAnimated) { 9557 events.zoomanim = this._animateZoom; 9558 } 9559 return events; 9560 }, 9561 9562 // @method isOpen: Boolean 9563 // Returns `true` when the popup is visible on the map. 9564 isOpen: function () { 9565 return !!this._map && this._map.hasLayer(this); 9566 }, 9567 9568 // @method bringToFront: this 9569 // Brings this popup in front of other popups (in the same map pane). 9570 bringToFront: function () { 9571 if (this._map) { 9572 toFront(this._container); 9573 } 9574 return this; 9575 }, 9576 9577 // @method bringToBack: this 9578 // Brings this popup to the back of other popups (in the same map pane). 9579 bringToBack: function () { 9580 if (this._map) { 9581 toBack(this._container); 9582 } 9583 return this; 9584 }, 9585 9586 _prepareOpen: function (parent, layer, latlng) { 9587 if (!(layer instanceof Layer)) { 9588 latlng = layer; 9589 layer = parent; 9590 } 9591 9592 if (layer instanceof FeatureGroup) { 9593 for (var id in parent._layers) { 9594 layer = parent._layers[id]; 9595 break; 9596 } 9597 } 9598 9599 if (!latlng) { 9600 if (layer.getCenter) { 9601 latlng = layer.getCenter(); 9602 } else if (layer.getLatLng) { 9603 latlng = layer.getLatLng(); 9604 } else { 9605 throw new Error('Unable to get source layer LatLng.'); 9606 } 9607 } 9608 9609 // set overlay source to this layer 9610 this._source = layer; 9611 9612 // update the overlay (content, layout, ect...) 9613 this.update(); 9614 9615 return latlng; 9616 }, 9617 9618 _updateContent: function () { 9619 if (!this._content) { return; } 9620 9621 var node = this._contentNode; 9622 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content; 9623 9624 if (typeof content === 'string') { 9625 node.innerHTML = content; 9626 } else { 9627 while (node.hasChildNodes()) { 9628 node.removeChild(node.firstChild); 9629 } 9630 node.appendChild(content); 9631 } 9632 this.fire('contentupdate'); 9633 }, 9634 9635 _updatePosition: function () { 9636 if (!this._map) { return; } 9637 9638 var pos = this._map.latLngToLayerPoint(this._latlng), 9639 offset = toPoint(this.options.offset), 9640 anchor = this._getAnchor(); 9641 9642 if (this._zoomAnimated) { 9643 setPosition(this._container, pos.add(anchor)); 9644 } else { 9645 offset = offset.add(pos).add(anchor); 9646 } 9647 9648 var bottom = this._containerBottom = -offset.y, 9649 left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x; 9650 9651 // bottom position the popup in case the height of the popup changes (images loading etc) 9652 this._container.style.bottom = bottom + 'px'; 9653 this._container.style.left = left + 'px'; 9654 }, 9655 9656 _getAnchor: function () { 9657 return [0, 0]; 9658 } 9659 9660 }); 9661 9662 /* 9663 * @class Popup 9664 * @inherits DivOverlay 9665 * @aka L.Popup 9666 * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to 9667 * open popups while making sure that only one popup is open at one time 9668 * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want. 9669 * 9670 * @example 9671 * 9672 * If you want to just bind a popup to marker click and then open it, it's really easy: 9673 * 9674 * ```js 9675 * marker.bindPopup(popupContent).openPopup(); 9676 * ``` 9677 * Path overlays like polylines also have a `bindPopup` method. 9678 * Here's a more complicated way to open a popup on a map: 9679 * 9680 * ```js 9681 * var popup = L.popup() 9682 * .setLatLng(latlng) 9683 * .setContent('<p>Hello world!<br />This is a nice popup.</p>') 9684 * .openOn(map); 9685 * ``` 9686 */ 9687 9688 9689 // @namespace Popup 9690 var Popup = DivOverlay.extend({ 9691 9692 // @section 9693 // @aka Popup options 9694 options: { 9695 // @option maxWidth: Number = 300 9696 // Max width of the popup, in pixels. 9697 maxWidth: 300, 9698 9699 // @option minWidth: Number = 50 9700 // Min width of the popup, in pixels. 9701 minWidth: 50, 9702 9703 // @option maxHeight: Number = null 9704 // If set, creates a scrollable container of the given height 9705 // inside a popup if its content exceeds it. 9706 maxHeight: null, 9707 9708 // @option autoPan: Boolean = true 9709 // Set it to `false` if you don't want the map to do panning animation 9710 // to fit the opened popup. 9711 autoPan: true, 9712 9713 // @option autoPanPaddingTopLeft: Point = null 9714 // The margin between the popup and the top left corner of the map 9715 // view after autopanning was performed. 9716 autoPanPaddingTopLeft: null, 9717 9718 // @option autoPanPaddingBottomRight: Point = null 9719 // The margin between the popup and the bottom right corner of the map 9720 // view after autopanning was performed. 9721 autoPanPaddingBottomRight: null, 9722 9723 // @option autoPanPadding: Point = Point(5, 5) 9724 // Equivalent of setting both top left and bottom right autopan padding to the same value. 9725 autoPanPadding: [5, 5], 9726 9727 // @option keepInView: Boolean = false 9728 // Set it to `true` if you want to prevent users from panning the popup 9729 // off of the screen while it is open. 9730 keepInView: false, 9731 9732 // @option closeButton: Boolean = true 9733 // Controls the presence of a close button in the popup. 9734 closeButton: true, 9735 9736 // @option autoClose: Boolean = true 9737 // Set it to `false` if you want to override the default behavior of 9738 // the popup closing when another popup is opened. 9739 autoClose: true, 9740 9741 // @option closeOnEscapeKey: Boolean = true 9742 // Set it to `false` if you want to override the default behavior of 9743 // the ESC key for closing of the popup. 9744 closeOnEscapeKey: true, 9745 9746 // @option closeOnClick: Boolean = * 9747 // Set it if you want to override the default behavior of the popup closing when user clicks 9748 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option. 9749 9750 // @option className: String = '' 9751 // A custom CSS class name to assign to the popup. 9752 className: '' 9753 }, 9754 9755 // @namespace Popup 9756 // @method openOn(map: Map): this 9757 // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`. 9758 openOn: function (map) { 9759 map.openPopup(this); 9760 return this; 9761 }, 9762 9763 onAdd: function (map) { 9764 DivOverlay.prototype.onAdd.call(this, map); 9765 9766 // @namespace Map 9767 // @section Popup events 9768 // @event popupopen: PopupEvent 9769 // Fired when a popup is opened in the map 9770 map.fire('popupopen', {popup: this}); 9771 9772 if (this._source) { 9773 // @namespace Layer 9774 // @section Popup events 9775 // @event popupopen: PopupEvent 9776 // Fired when a popup bound to this layer is opened 9777 this._source.fire('popupopen', {popup: this}, true); 9778 // For non-path layers, we toggle the popup when clicking 9779 // again the layer, so prevent the map to reopen it. 9780 if (!(this._source instanceof Path)) { 9781 this._source.on('preclick', stopPropagation); 9782 } 9783 } 9784 }, 9785 9786 onRemove: function (map) { 9787 DivOverlay.prototype.onRemove.call(this, map); 9788 9789 // @namespace Map 9790 // @section Popup events 9791 // @event popupclose: PopupEvent 9792 // Fired when a popup in the map is closed 9793 map.fire('popupclose', {popup: this}); 9794 9795 if (this._source) { 9796 // @namespace Layer 9797 // @section Popup events 9798 // @event popupclose: PopupEvent 9799 // Fired when a popup bound to this layer is closed 9800 this._source.fire('popupclose', {popup: this}, true); 9801 if (!(this._source instanceof Path)) { 9802 this._source.off('preclick', stopPropagation); 9803 } 9804 } 9805 }, 9806 9807 getEvents: function () { 9808 var events = DivOverlay.prototype.getEvents.call(this); 9809 9810 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) { 9811 events.preclick = this._close; 9812 } 9813 9814 if (this.options.keepInView) { 9815 events.moveend = this._adjustPan; 9816 } 9817 9818 return events; 9819 }, 9820 9821 _close: function () { 9822 if (this._map) { 9823 this._map.closePopup(this); 9824 } 9825 }, 9826 9827 _initLayout: function () { 9828 var prefix = 'leaflet-popup', 9829 container = this._container = create$1('div', 9830 prefix + ' ' + (this.options.className || '') + 9831 ' leaflet-zoom-animated'); 9832 9833 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container); 9834 this._contentNode = create$1('div', prefix + '-content', wrapper); 9835 9836 disableClickPropagation(container); 9837 disableScrollPropagation(this._contentNode); 9838 on(container, 'contextmenu', stopPropagation); 9839 9840 this._tipContainer = create$1('div', prefix + '-tip-container', container); 9841 this._tip = create$1('div', prefix + '-tip', this._tipContainer); 9842 9843 if (this.options.closeButton) { 9844 var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container); 9845 closeButton.href = '#close'; 9846 closeButton.innerHTML = '×'; 9847 9848 on(closeButton, 'click', this._onCloseButtonClick, this); 9849 } 9850 }, 9851 9852 _updateLayout: function () { 9853 var container = this._contentNode, 9854 style = container.style; 9855 9856 style.width = ''; 9857 style.whiteSpace = 'nowrap'; 9858 9859 var width = container.offsetWidth; 9860 width = Math.min(width, this.options.maxWidth); 9861 width = Math.max(width, this.options.minWidth); 9862 9863 style.width = (width + 1) + 'px'; 9864 style.whiteSpace = ''; 9865 9866 style.height = ''; 9867 9868 var height = container.offsetHeight, 9869 maxHeight = this.options.maxHeight, 9870 scrolledClass = 'leaflet-popup-scrolled'; 9871 9872 if (maxHeight && height > maxHeight) { 9873 style.height = maxHeight + 'px'; 9874 addClass(container, scrolledClass); 9875 } else { 9876 removeClass(container, scrolledClass); 9877 } 9878 9879 this._containerWidth = this._container.offsetWidth; 9880 }, 9881 9882 _animateZoom: function (e) { 9883 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center), 9884 anchor = this._getAnchor(); 9885 setPosition(this._container, pos.add(anchor)); 9886 }, 9887 9888 _adjustPan: function () { 9889 if (!this.options.autoPan) { return; } 9890 if (this._map._panAnim) { this._map._panAnim.stop(); } 9891 9892 var map = this._map, 9893 marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0, 9894 containerHeight = this._container.offsetHeight + marginBottom, 9895 containerWidth = this._containerWidth, 9896 layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom); 9897 9898 layerPos._add(getPosition(this._container));
9899 9900 var containerPos = map.layerPointToContainerPoint(layerPos), 9901 padding = toPoint(this.options.autoPanPadding), 9902 paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding), 9903 paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding), 9904 size = map.getSize(), 9905 dx = 0, 9906 dy = 0; 9907 9908 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right 9909 dx = containerPos.x + containerWidth - size.x + paddingBR.x; 9910 } 9911 if (containerPos.x - dx - paddingTL.x < 0) { // left 9912 dx = containerPos.x - paddingTL.x; 9913 } 9914 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom 9915 dy = containerPos.y + containerHeight - size.y + paddingBR.y; 9916 } 9917 if (containerPos.y - dy - paddingTL.y < 0) { // top 9918 dy = containerPos.y - paddingTL.y; 9919 } 9920 9921 // @namespace Map 9922 // @section Popup events 9923 // @event autopanstart: Event 9924 // Fired when the map starts autopanning when opening a popup. 9925 if (dx || dy) { 9926 map 9927 .fire('autopanstart') 9928 .panBy([dx, dy]); 9929 } 9930 }, 9931 9932 _onCloseButtonClick: function (e) { 9933 this._close(); 9934 stop(e); 9935 }, 9936 9937 _getAnchor: function () { 9938 // Where should we anchor the popup on the source layer? 9939 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]); 9940 } 9941 9942 }); 9943 9944 // @namespace Popup 9945 // @factory L.popup(options?: Popup options, source?: Layer) 9946 // 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. 9947 var popup = function (options, source) { 9948 return new Popup(options, source); 9949 }; 9950 9951 9952 /* @namespace Map 9953 * @section Interaction Options 9954 * @option closePopupOnClick: Boolean = true 9955 * Set it to `false` if you don't want popups to close when user clicks the map. 9956 */ 9957 Map.mergeOptions({ 9958 closePopupOnClick: true 9959 }); 9960 9961 9962 // @namespace Map 9963 // @section Methods for Layers and Controls 9964 Map.include({ 9965 // @method openPopup(popup: Popup): this 9966 // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability). 9967 // @alternative 9968 // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this 9969 // Creates a popup with the specified content and options and opens it in the given point on a map. 9970 openPopup: function (popup, latlng, options) { 9971 if (!(popup instanceof Popup)) { 9972 popup = new Popup(options).setContent(popup); 9973 } 9974 9975 if (latlng) { 9976 popup.setLatLng(latlng); 9977 } 9978 9979 if (this.hasLayer(popup)) { 9980 return this; 9981 } 9982 9983 if (this._popup && this._popup.options.autoClose) { 9984 this.closePopup(); 9985 } 9986 9987 this._popup = popup; 9988 return this.addLayer(popup); 9989 }, 9990 9991 // @method closePopup(popup?: Popup): this 9992 // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one). 9993 closePopup: function (popup) { 9994 if (!popup || popup === this._popup) { 9995 popup = this._popup; 9996 this._popup = null; 9997 } 9998 if (popup) { 9999 this.removeLayer(popup); 10000 } 10001 return this; 10002 } 10003 }); 10004 10005 /* 10006 * @namespace Layer 10007 * @section Popup methods example 10008 * 10009 * All layers share a set of methods convenient for binding popups to it. 10010 * 10011 * ```js 10012 * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map); 10013 * layer.openPopup(); 10014 * layer.closePopup(); 10015 * ``` 10016 * 10017 * 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. 10018 */ 10019 10020 // @section Popup methods 10021 Layer.include({ 10022 10023 // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this 10024 // Binds a popup to the layer with the passed `content` and sets up the 10025 // necessary event listeners. If a `Function` is passed it will receive 10026 // the layer as the first argument and should return a `String` or `HTMLElement`. 10027 bindPopup: function (content, options) { 10028 10029 if (content instanceof Popup) { 10030 setOptions(content, options); 10031 this._popup = content; 10032 content._source = this; 10033 } else { 10034 if (!this._popup || options) { 10035 this._popup = new Popup(options, this); 10036 } 10037 this._popup.setContent(content); 10038 } 10039 10040 if (!this._popupHandlersAdded) { 10041 this.on({ 10042 click: this._openPopup, 10043 keypress: this._onKeyPress, 10044 remove: this.closePopup, 10045 move: this._movePopup 10046 });
10047 this._popupHandlersAdded = true; 10048 } 10049 10050 return this; 10051 }, 10052 10053 // @method unbindPopup(): this 10054 // Removes the popup previously bound with `bindPopup`. 10055 unbindPopup: function () { 10056 if (this._popup) { 10057 this.off({ 10058 click: this._openPopup, 10059 keypress: this._onKeyPress, 10060 remove: this.closePopup, 10061 move: this._movePopup 10062 }); 10063 this._popupHandlersAdded = false; 10064 this._popup = null; 10065 } 10066 return this; 10067 }, 10068 10069 // @method openPopup(latlng?: LatLng): this 10070 // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed. 10071 openPopup: function (layer, latlng) { 10072 if (this._popup && this._map) { 10073 latlng = this._popup._prepareOpen(this, layer, latlng); 10074 10075 // open the popup on the map 10076 this._map.openPopup(this._popup, latlng); 10077 } 10078 10079 return this; 10080 }, 10081 10082 // @method closePopup(): this 10083 // Closes the popup bound to this layer if it is open. 10084 closePopup: function () { 10085 if (this._popup) { 10086 this._popup._close(); 10087 } 10088 return this; 10089 }, 10090 10091 // @method togglePopup(): this 10092 // Opens or closes the popup bound to this layer depending on its current state. 10093 togglePopup: function (target) { 10094 if (this._popup) { 10095 if (this._popup._map) { 10096 this.closePopup(); 10097 } else { 10098 this.openPopup(target); 10099 } 10100 } 10101 return this; 10102 }, 10103 10104 // @method isPopupOpen(): boolean 10105 // Returns `true` if the popup bound to this layer is currently open. 10106 isPopupOpen: function () { 10107 return (this._popup ? this._popup.isOpen() : false); 10108 }, 10109 10110 // @method setPopupContent(content: String|HTMLElement|Popup): this 10111 // Sets the content of the popup bound to this layer. 10112 setPopupContent: function (content) { 10113 if (this._popup) { 10114 this._popup.setContent(content); 10115 } 10116 return this; 10117 }, 10118 10119 // @method getPopup(): Popup 10120 // Returns the popup bound to this layer. 10121 getPopup: function () { 10122 return this._popup; 10123 }, 10124 10125 _openPopup: function (e) { 10126 var layer = e.layer || e.target; 10127 10128 if (!this._popup) { 10129 return; 10130 } 10131 10132 if (!this._map) { 10133 return; 10134 } 10135 10136 // prevent map click 10137 stop(e); 10138 10139 // if this inherits from Path its a vector and we can just 10140 // open the popup at the new location 10141 if (layer instanceof Path) { 10142 this.openPopup(e.layer || e.target, e.latlng); 10143 return; 10144 } 10145 10146 // otherwise treat it like a marker and figure out 10147 // if we should toggle it open/closed 10148 if (this._map.hasLayer(this._popup) && this._popup._source === layer) { 10149 this.closePopup(); 10150 } else { 10151 this.openPopup(layer, e.latlng); 10152 } 10153 }, 10154 10155 _movePopup: function (e) { 10156 this._popup.setLatLng(e.latlng); 10157 }, 10158 10159 _onKeyPress: function (e) { 10160 if (e.originalEvent.keyCode === 13) { 10161 this._openPopup(e); 10162 } 10163 } 10164 }); 10165 10166 /* 10167 * @class Tooltip 10168 * @inherits DivOverlay 10169 * @aka L.Tooltip 10170 * Used to display small texts on top of map layers. 10171 * 10172 * @example 10173 * 10174 * ```js 10175 * marker.bindTooltip("my tooltip text").openTooltip(); 10176 * ``` 10177 * Note about tooltip offset. Leaflet takes two options in consideration 10178 * for computing tooltip offsetting: 10179 * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip. 10180 * Add a positive x offset to move the tooltip to the right, and a positive y offset to 10181 * move it to the bottom. Negatives will move to the left and top. 10182 * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You 10183 * should adapt this value if you use a custom icon. 10184 */ 10185 10186 10187 // @namespace Tooltip 10188 var Tooltip = DivOverlay.extend({ 10189 10190 // @section 10191 // @aka Tooltip options 10192 options: { 10193 // @option pane: String = 'tooltipPane' 10194 // `Map pane` where the tooltip will be added. 10195 pane: 'tooltipPane', 10196 10197 // @option offset: Point = Point(0, 0) 10198 // Optional offset of the tooltip position. 10199 offset: [0, 0], 10200 10201 // @option direction: String = 'auto' 10202 // Direction where to open the tooltip. Possible values are: `right`, `left`, 10203 // `top`, `bottom`, `center`, `auto`. 10204 // `auto` will dynamically switch between `right` and `left` according to the tooltip 10205 // position on the map. 10206 direction: 'auto', 10207 10208 // @option permanent: Boolean = false 10209 // Whether to open the tooltip permanently or only on mouseover. 10210 permanent: false, 10211 10212 // @option sticky: Boolean = false 10213 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
vendor: 13,806 bytes, lines 10214-10678
10214 sticky: false, 10215 10216 // @option interactive: Boolean = false 10217 // If true, the tooltip will listen to the feature events. 10218 interactive: false, 10219 10220 // @option opacity: Number = 0.9 10221 // Tooltip container opacity. 10222 opacity: 0.9 10223 }, 10224 10225 onAdd: function (map) { 10226 DivOverlay.prototype.onAdd.call(this, map); 10227 this.setOpacity(this.options.opacity); 10228 10229 // @namespace Map 10230 // @section Tooltip events 10231 // @event tooltipopen: TooltipEvent 10232 // Fired when a tooltip is opened in the map. 10233 map.fire('tooltipopen', {tooltip: this}); 10234 10235 if (this._source) { 10236 // @namespace Layer 10237 // @section Tooltip events 10238 // @event tooltipopen: TooltipEvent 10239 // Fired when a tooltip bound to this layer is opened. 10240 this._source.fire('tooltipopen', {tooltip: this}, true); 10241 } 10242 }, 10243 10244 onRemove: function (map) { 10245 DivOverlay.prototype.onRemove.call(this, map); 10246 10247 // @namespace Map 10248 // @section Tooltip events 10249 // @event tooltipclose: TooltipEvent 10250 // Fired when a tooltip in the map is closed. 10251 map.fire('tooltipclose', {tooltip: this}); 10252 10253 if (this._source) { 10254 // @namespace Layer 10255 // @section Tooltip events 10256 // @event tooltipclose: TooltipEvent 10257 // Fired when a tooltip bound to this layer is closed. 10258 this._source.fire('tooltipclose', {tooltip: this}, true); 10259 } 10260 }, 10261 10262 getEvents: function () { 10263 var events = DivOverlay.prototype.getEvents.call(this); 10264 10265 if (touch && !this.options.permanent) { 10266 events.preclick = this._close; 10267 } 10268 10269 return events; 10270 }, 10271 10272 _close: function () { 10273 if (this._map) { 10274 this._map.closeTooltip(this); 10275 } 10276 }, 10277 10278 _initLayout: function () { 10279 var prefix = 'leaflet-tooltip', 10280 className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide'); 10281 10282 this._contentNode = this._container = create$1('div', className); 10283 }, 10284 10285 _updateLayout: function () {}, 10286 10287 _adjustPan: function () {}, 10288 10289 _setPosition: function (pos) { 10290 var subX, subY, 10291 map = this._map, 10292 container = this._container, 10293 centerPoint = map.latLngToContainerPoint(map.getCenter()), 10294 tooltipPoint = map.layerPointToContainerPoint(pos), 10295 direction = this.options.direction, 10296 tooltipWidth = container.offsetWidth, 10297 tooltipHeight = container.offsetHeight, 10298 offset = toPoint(this.options.offset), 10299 anchor = this._getAnchor(); 10300 10301 if (direction === 'top') { 10302 subX = tooltipWidth / 2; 10303 subY = tooltipHeight; 10304 } else if (direction === 'bottom') { 10305 subX = tooltipWidth / 2; 10306 subY = 0; 10307 } else if (direction === 'center') { 10308 subX = tooltipWidth / 2; 10309 subY = tooltipHeight / 2; 10310 } else if (direction === 'right') { 10311 subX = 0; 10312 subY = tooltipHeight / 2; 10313 } else if (direction === 'left') { 10314 subX = tooltipWidth; 10315 subY = tooltipHeight / 2; 10316 } else if (tooltipPoint.x < centerPoint.x) { 10317 direction = 'right'; 10318 subX = 0; 10319 subY = tooltipHeight / 2; 10320 } else { 10321 direction = 'left'; 10322 subX = tooltipWidth + (offset.x + anchor.x) * 2; 10323 subY = tooltipHeight / 2; 10324 } 10325 10326 pos = pos.subtract(toPoint(subX, subY, true)).add(offset).add(anchor); 10327 10328 removeClass(container, 'leaflet-tooltip-right'); 10329 removeClass(container, 'leaflet-tooltip-left'); 10330 removeClass(container, 'leaflet-tooltip-top'); 10331 removeClass(container, 'leaflet-tooltip-bottom'); 10332 addClass(container, 'leaflet-tooltip-' + direction); 10333 setPosition(container, pos); 10334 }, 10335 10336 _updatePosition: function () { 10337 var pos = this._map.latLngToLayerPoint(this._latlng); 10338 this._setPosition(pos); 10339 }, 10340 10341 setOpacity: function (opacity) { 10342 this.options.opacity = opacity; 10343 10344 if (this._container) { 10345 setOpacity(this._container, opacity); 10346 } 10347 }, 10348 10349 _animateZoom: function (e) { 10350 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center); 10351 this._setPosition(pos); 10352 }, 10353 10354 _getAnchor: function () { 10355 // Where should we anchor the tooltip on the source layer? 10356 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]); 10357 } 10358 10359 }); 10360 10361 // @namespace Tooltip 10362 // @factory L.tooltip(options?: Tooltip options, source?: Layer) 10363 // 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. 10364 var tooltip = function (options, source) { 10365 return new Tooltip(options, source); 10366 }; 10367 10368 // @namespace Map 10369 // @section Methods for Layers and Controls 10370 Map.include({ 10371 10372 // @method openTooltip(tooltip: Tooltip): this 10373 // Opens the specified tooltip. 10374 // @alternative 10375 // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this 10376 // Creates a tooltip with the specified content and options and open it. 10377 openTooltip: function (tooltip, latlng, options) { 10378 if (!(tooltip instanceof Tooltip)) { 10379 tooltip = new Tooltip(options).setContent(tooltip); 10380 } 10381 10382 if (latlng) { 10383 tooltip.setLatLng(latlng); 10384 } 10385 10386 if (this.hasLayer(tooltip)) { 10387 return this; 10388 } 10389 10390 return this.addLayer(tooltip); 10391 }, 10392 10393 // @method closeTooltip(tooltip?: Tooltip): this 10394 // Closes the tooltip given as parameter. 10395 closeTooltip: function (tooltip) { 10396 if (tooltip) { 10397 this.removeLayer(tooltip); 10398 } 10399 return this; 10400 } 10401 10402 }); 10403 10404 /* 10405 * @namespace Layer 10406 * @section Tooltip methods example 10407 * 10408 * All layers share a set of methods convenient for binding tooltips to it. 10409 * 10410 * ```js 10411 * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map); 10412 * layer.openTooltip(); 10413 * layer.closeTooltip(); 10414 * ``` 10415 */ 10416 10417 // @section Tooltip methods 10418 Layer.include({ 10419 10420 // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this 10421 // Binds a tooltip to the layer with the passed `content` and sets up the 10422 // necessary event listeners. If a `Function` is passed it will receive 10423 // the layer as the first argument and should return a `String` or `HTMLElement`. 10424 bindTooltip: function (content, options) { 10425 10426 if (content instanceof Tooltip) { 10427 setOptions(content, options); 10428 this._tooltip = content; 10429 content._source = this; 10430 } else { 10431 if (!this._tooltip || options) { 10432 this._tooltip = new Tooltip(options, this); 10433 } 10434 this._tooltip.setContent(content); 10435 10436 } 10437 10438 this._initTooltipInteractions(); 10439 10440 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) { 10441 this.openTooltip(); 10442 } 10443 10444 return this; 10445 }, 10446 10447 // @method unbindTooltip(): this 10448 // Removes the tooltip previously bound with `bindTooltip`. 10449 unbindTooltip: function () { 10450 if (this._tooltip) { 10451 this._initTooltipInteractions(true); 10452 this.closeTooltip(); 10453 this._tooltip = null; 10454 } 10455 return this; 10456 }, 10457 10458 _initTooltipInteractions: function (remove) { 10459 if (!remove && this._tooltipHandlersAdded) { return; } 10460 var onOff = remove ? 'off' : 'on', 10461 events = { 10462 remove: this.closeTooltip, 10463 move: this._moveTooltip 10464 }; 10465 if (!this._tooltip.options.permanent) { 10466 events.mouseover = this._openTooltip; 10467 events.mouseout = this.closeTooltip; 10468 if (this._tooltip.options.sticky) { 10469 events.mousemove = this._moveTooltip; 10470 } 10471 if (touch) { 10472 events.click = this._openTooltip; 10473 } 10474 } else { 10475 events.add = this._openTooltip; 10476 } 10477 this[onOff](events); 10478 this._tooltipHandlersAdded = !remove; 10479 }, 10480 10481 // @method openTooltip(latlng?: LatLng): this 10482 // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed. 10483 openTooltip: function (layer, latlng) { 10484 if (this._tooltip && this._map) { 10485 latlng = this._tooltip._prepareOpen(this, layer, latlng); 10486 10487 // open the tooltip on the map 10488 this._map.openTooltip(this._tooltip, latlng); 10489 10490 // Tooltip container may not be defined if not permanent and never 10491 // opened. 10492 if (this._tooltip.options.interactive && this._tooltip._container) { 10493 addClass(this._tooltip._container, 'leaflet-clickable'); 10494 this.addInteractiveTarget(this._tooltip._container); 10495 } 10496 } 10497 10498 return this; 10499 }, 10500 10501 // @method closeTooltip(): this 10502 // Closes the tooltip bound to this layer if it is open. 10503 closeTooltip: function () { 10504 if (this._tooltip) { 10505 this._tooltip._close(); 10506 if (this._tooltip.options.interactive && this._tooltip._container) { 10507 removeClass(this._tooltip._container, 'leaflet-clickable'); 10508 this.removeInteractiveTarget(this._tooltip._container); 10509 } 10510 } 10511 return this; 10512 }, 10513 10514 // @method toggleTooltip(): this 10515 // Opens or closes the tooltip bound to this layer depending on its current state. 10516 toggleTooltip: function (target) { 10517 if (this._tooltip) { 10518 if (this._tooltip._map) { 10519 this.closeTooltip(); 10520 } else { 10521 this.openTooltip(target); 10522 } 10523 } 10524 return this; 10525 }, 10526 10527 // @method isTooltipOpen(): boolean 10528 // Returns `true` if the tooltip bound to this layer is currently open. 10529 isTooltipOpen: function () { 10530 return this._tooltip.isOpen(); 10531 }, 10532 10533 // @method setTooltipContent(content: String|HTMLElement|Tooltip): this 10534 // Sets the content of the tooltip bound to this layer. 10535 setTooltipContent: function (content) { 10536 if (this._tooltip) { 10537 this._tooltip.setContent(content); 10538 } 10539 return this; 10540 }, 10541 10542 // @method getTooltip(): Tooltip 10543 // Returns the tooltip bound to this layer. 10544 getTooltip: function () { 10545 return this._tooltip; 10546 }, 10547 10548 _openTooltip: function (e) { 10549 var layer = e.layer || e.target; 10550 10551 if (!this._tooltip || !this._map) { 10552 return; 10553 } 10554 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined); 10555 }, 10556 10557 _moveTooltip: function (e) { 10558 var latlng = e.latlng, containerPoint, layerPoint; 10559 if (this._tooltip.options.sticky && e.originalEvent) { 10560 containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent); 10561 layerPoint = this._map.containerPointToLayerPoint(containerPoint); 10562 latlng = this._map.layerPointToLatLng(layerPoint); 10563 } 10564 this._tooltip.setLatLng(latlng); 10565 } 10566 }); 10567 10568 /* 10569 * @class DivIcon 10570 * @aka L.DivIcon 10571 * @inherits Icon 10572 * 10573 * Represents a lightweight icon for markers that uses a simple `<div>` 10574 * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options. 10575 * 10576 * @example 10577 * ```js 10578 * var myIcon = L.divIcon({className: 'my-div-icon'}); 10579 * // you can set .my-div-icon styles in CSS 10580 * 10581 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map); 10582 * ``` 10583 * 10584 * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow. 10585 */ 10586 10587 var DivIcon = Icon.extend({ 10588 options: { 10589 // @section 10590 // @aka DivIcon options 10591 iconSize: [12, 12], // also can be set through CSS 10592 10593 // iconAnchor: (Point), 10594 // popupAnchor: (Point), 10595 10596 // @option html: String|HTMLElement = '' 10597 // Custom HTML code to put inside the div element, empty by default. Alternatively, 10598 // an instance of `HTMLElement`. 10599 html: false, 10600 10601 // @option bgPos: Point = [0, 0] 10602 // Optional relative position of the background, in pixels 10603 bgPos: null, 10604 10605 className: 'leaflet-div-icon' 10606 }, 10607 10608 createIcon: function (oldIcon) { 10609 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'), 10610 options = this.options; 10611 10612 if (options.html instanceof Element) { 10613 empty(div); 10614 div.appendChild(options.html); 10615 } else { 10616 div.innerHTML = options.html !== false ? options.html : ''; 10617 } 10618 10619 if (options.bgPos) { 10620 var bgPos = toPoint(options.bgPos); 10621 div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px'; 10622 } 10623 this._setIconStyles(div, 'icon'); 10624 10625 return div; 10626 }, 10627 10628 createShadow: function () { 10629 return null; 10630 } 10631 }); 10632 10633 // @factory L.divIcon(options: DivIcon options) 10634 // Creates a `DivIcon` instance with the given options. 10635 function divIcon(options) { 10636 return new DivIcon(options); 10637 } 10638 10639 Icon.Default = IconDefault; 10640 10641 /* 10642 * @class GridLayer 10643 * @inherits Layer 10644 * @aka L.GridLayer 10645 * 10646 * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`. 10647 * 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. 10648 * 10649 * 10650 * @section Synchronous usage 10651 * @example 10652 * 10653 * 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. 10654 * 10655 * ```js 10656 * var CanvasLayer = L.GridLayer.extend({ 10657 * createTile: function(coords){ 10658 * // create a <canvas> element for drawing 10659 * var tile = L.DomUtil.create('canvas', 'leaflet-tile'); 10660 * 10661 * // setup tile width and height according to the options 10662 * var size = this.getTileSize(); 10663 * tile.width = size.x; 10664 * tile.height = size.y; 10665 * 10666 * // get a canvas context and draw something on it using coords.x, coords.y and coords.z 10667 * var ctx = tile.getContext('2d'); 10668 * 10669 * // return the tile so it can be rendered on screen 10670 * return tile; 10671 * } 10672 * }); 10673 * ``` 10674 * 10675 * @section Asynchronous usage 10676 * @example 10677 * 10678 * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. O
10678nce the tile is finished drawing it can be passed to the `done()` callback. 10679 * 10680 * ```js 10681 * var CanvasLayer = L.GridLayer.extend({ 10682 * createTile: function(coords, done){ 10683 * var error; 10684 * 10685 * // create a <canvas> element for drawing 10686 * var tile = L.DomUtil.create('canvas', 'leaflet-tile'); 10687 * 10688 * // setup tile width and height according to the options 10689 * var size = this.getTileSize(); 10690 * tile.width = size.x; 10691 * tile.height = size.y; 10692 * 10693 * // draw something asynchronously and pass the tile to the done() callback 10694 * setTimeout(function() { 10695 * done(error, tile); 10696 * }, 1000); 10697 * 10698 * return tile; 10699 * } 10700 * }); 10701 * ``` 10702 * 10703 * @section 10704 */ 10705 10706 10707 var GridLayer = Layer.extend({ 10708 10709 // @section 10710 // @aka GridLayer options 10711 options: { 10712 // @option tileSize: Number|Point = 256 10713 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise. 10714 tileSize: 256, 10715 10716 // @option opacity: Number = 1.0 10717 // Opacity of the tiles. Can be used in the `createTile()` function. 10718 opacity: 1, 10719 10720 // @option updateWhenIdle: Boolean = (depends) 10721 // Load new tiles only when panning ends. 10722 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation. 10723 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the 10724 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers. 10725 updateWhenIdle: mobile, 10726 10727 // @option updateWhenZooming: Boolean = true 10728 // 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. 10729 updateWhenZooming: true, 10730 10731 // @option updateInterval: Number = 200 10732 // Tiles will not update more than once every `updateInterval` milliseconds when panning. 10733 updateInterval: 200, 10734 10735 // @option zIndex: Number = 1 10736 // The explicit zIndex of the tile layer. 10737 zIndex: 1, 10738 10739 // @option bounds: LatLngBounds = undefined 10740 // If set, tiles will only be loaded inside the set `LatLngBounds`. 10741 bounds: null, 10742 10743 // @option minZoom: Number = 0 10744 // The minimum zoom level down to which this layer will be displayed (inclusive). 10745 minZoom: 0, 10746 10747 // @option maxZoom: Number = undefined 10748 // The maximum zoom level up to which this layer will be displayed (inclusive). 10749 maxZoom: undefined, 10750 10751 // @option maxNativeZoom: Number = undefined 10752 // Maximum zoom number the tile source has available. If it is specified, 10753 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded 10754 // from `maxNativeZoom` level and auto-scaled. 10755 maxNativeZoom: undefined, 10756 10757 // @option minNativeZoom: Number = undefined 10758 // Minimum zoom number the tile source has available. If it is specified, 10759 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded 10760 // from `minNativeZoom` level and auto-scaled. 10761 minNativeZoom: undefined, 10762 10763 // @option noWrap: Boolean = false 10764 // Whether the layer is wrapped around the antimeridian. If `true`, the 10765 // GridLayer will only be displayed once at low zoom levels. Has no 10766 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used 10767 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting 10768 // tiles outside the CRS limits. 10769 noWrap: false, 10770 10771 // @option pane: String = 'tilePane' 10772 // `Map pane` where the grid layer will be added. 10773 pane: 'tilePane', 10774 10775 // @option className: String = '' 10776 // A custom class name to assign to the tile layer. Empty by default. 10777 className: '', 10778 10779 // @option keepBuffer: Number = 2 10780 // When panning the map, keep this many rows and columns of tiles before unloading them. 10781 keepBuffer: 2 10782 }, 10783 10784 initialize: function (options) { 10785 setOptions(this, options); 10786 }, 10787 10788 onAdd: function () { 10789 this._initContainer(); 10790 10791 this._levels = {}; 10792 this._tiles = {}; 10793 10794 this._resetView(); 10795 this._update(); 10796 }, 10797 10798 beforeAdd: function (map) {
vendor: 4,091 bytes, lines 10799-10942
10799 map._addZoomLimit(this); 10800 }, 10801 10802 onRemove: function (map) { 10803 this._removeAllTiles(); 10804 remove(this._container); 10805 map._removeZoomLimit(this); 10806 this._container = null; 10807 this._tileZoom = undefined; 10808 }, 10809 10810 // @method bringToFront: this 10811 // Brings the tile layer to the top of all tile layers. 10812 bringToFront: function () { 10813 if (this._map) { 10814 toFront(this._container); 10815 this._setAutoZIndex(Math.max); 10816 } 10817 return this; 10818 }, 10819 10820 // @method bringToBack: this 10821 // Brings the tile layer to the bottom of all tile layers. 10822 bringToBack: function () { 10823 if (this._map) { 10824 toBack(this._container); 10825 this._setAutoZIndex(Math.min); 10826 } 10827 return this; 10828 }, 10829 10830 // @method getContainer: HTMLElement 10831 // Returns the HTML element that contains the tiles for this layer. 10832 getContainer: function () { 10833 return this._container; 10834 }, 10835 10836 // @method setOpacity(opacity: Number): this 10837 // Changes the [opacity](#gridlayer-opacity) of the grid layer. 10838 setOpacity: function (opacity) { 10839 this.options.opacity = opacity; 10840 this._updateOpacity(); 10841 return this; 10842 }, 10843 10844 // @method setZIndex(zIndex: Number): this 10845 // Changes the [zIndex](#gridlayer-zindex) of the grid layer. 10846 setZIndex: function (zIndex) { 10847 this.options.zIndex = zIndex; 10848 this._updateZIndex(); 10849 10850 return this; 10851 }, 10852 10853 // @method isLoading: Boolean 10854 // Returns `true` if any tile in the grid layer has not finished loading. 10855 isLoading: function () { 10856 return this._loading; 10857 }, 10858 10859 // @method redraw: this 10860 // Causes the layer to clear all the tiles and request them again. 10861 redraw: function () { 10862 if (this._map) { 10863 this._removeAllTiles(); 10864 this._update(); 10865 } 10866 return this; 10867 }, 10868 10869 getEvents: function () { 10870 var events = { 10871 viewprereset: this._invalidateAll, 10872 viewreset: this._resetView, 10873 zoom: this._resetView, 10874 moveend: this._onMoveEnd 10875 }; 10876 10877 if (!this.options.updateWhenIdle) { 10878 // update tiles on move, but not more often than once per given interval 10879 if (!this._onMove) { 10880 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this); 10881 } 10882 10883 events.move = this._onMove; 10884 } 10885 10886 if (this._zoomAnimated) { 10887 events.zoomanim = this._animateZoom; 10888 } 10889 10890 return events; 10891 }, 10892 10893 // @section Extension methods 10894 // Layers extending `GridLayer` shall reimplement the following method. 10895 // @method createTile(coords: Object, done?: Function): HTMLElement 10896 // Called only internally, must be overridden by classes extending `GridLayer`. 10897 // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback 10898 // is specified, it must be called when the tile has finished loading and drawing. 10899 createTile: function () { 10900 return document.createElement('div'); 10901 }, 10902 10903 // @section 10904 // @method getTileSize: Point 10905 // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method. 10906 getTileSize: function () { 10907 var s = this.options.tileSize; 10908 return s instanceof Point ? s : new Point(s, s); 10909 }, 10910 10911 _updateZIndex: function () { 10912 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) { 10913 this._container.style.zIndex = this.options.zIndex; 10914 } 10915 }, 10916 10917 _setAutoZIndex: function (compare) { 10918 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back) 10919 10920 var layers = this.getPane().children, 10921 edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min 10922 10923 for (var i = 0, len = layers.length, zIndex; i < len; i++) { 10924 10925 zIndex = layers[i].style.zIndex; 10926 10927 if (layers[i] !== this._container && zIndex) { 10928 edgeZIndex = compare(edgeZIndex, +zIndex); 10929 } 10930 } 10931 10932 if (isFinite(edgeZIndex)) { 10933 this.options.zIndex = edgeZIndex + compare(-1, 1); 10934 this._updateZIndex(); 10935 } 10936 }, 10937 10938 _updateOpacity: function () { 10939 if (!this._map) { return; } 10940 10941 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles 10942 if (
vendor: 6,720 bytes, lines 10942-11228
10942ielt9) { return; } 10943 10944 setOpacity(this._container, this.options.opacity); 10945 10946 var now = +new Date(), 10947 nextFrame = false, 10948 willPrune = false; 10949 10950 for (var key in this._tiles) { 10951 var tile = this._tiles[key]; 10952 if (!tile.current || !tile.loaded) { continue; } 10953 10954 var fade = Math.min(1, (now - tile.loaded) / 200); 10955 10956 setOpacity(tile.el, fade); 10957 if (fade < 1) { 10958 nextFrame = true; 10959 } else { 10960 if (tile.active) { 10961 willPrune = true; 10962 } else { 10963 this._onOpaqueTile(tile); 10964 } 10965 tile.active = true; 10966 } 10967 } 10968 10969 if (willPrune && !this._noPrune) { this._pruneTiles(); } 10970 10971 if (nextFrame) { 10972 cancelAnimFrame(this._fadeFrame); 10973 this._fadeFrame = requestAnimFrame(this._updateOpacity, this); 10974 } 10975 }, 10976 10977 _onOpaqueTile: falseFn, 10978 10979 _initContainer: function () { 10980 if (this._container) { return; } 10981 10982 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || '')); 10983 this._updateZIndex(); 10984 10985 if (this.options.opacity < 1) { 10986 this._updateOpacity(); 10987 } 10988 10989 this.getPane().appendChild(this._container); 10990 }, 10991 10992 _updateLevels: function () { 10993 10994 var zoom = this._tileZoom, 10995 maxZoom = this.options.maxZoom; 10996 10997 if (zoom === undefined) { return undefined; } 10998 10999 for (var z in this._levels) { 11000 z = Number(z); 11001 if (this._levels[z].el.children.length || z === zoom) { 11002 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z); 11003 this._onUpdateLevel(z); 11004 } else { 11005 remove(this._levels[z].el); 11006 this._removeTilesAtZoom(z); 11007 this._onRemoveLevel(z); 11008 delete this._levels[z]; 11009 } 11010 } 11011 11012 var level = this._levels[zoom], 11013 map = this._map; 11014 11015 if (!level) { 11016 level = this._levels[zoom] = {}; 11017 11018 level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container); 11019 level.el.style.zIndex = maxZoom; 11020 11021 level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round(); 11022 level.zoom = zoom; 11023 11024 this._setZoomTransform(level, map.getCenter(), map.getZoom()); 11025 11026 // force the browser to consider the newly added element for transition 11027 falseFn(level.el.offsetWidth); 11028 11029 this._onCreateLevel(level); 11030 } 11031 11032 this._level = level; 11033 11034 return level; 11035 }, 11036 11037 _onUpdateLevel: falseFn, 11038 11039 _onRemoveLevel: falseFn, 11040 11041 _onCreateLevel: falseFn, 11042 11043 _pruneTiles: function () { 11044 if (!this._map) { 11045 return; 11046 } 11047 11048 var key, tile; 11049 11050 var zoom = this._map.getZoom(); 11051 if (zoom > this.options.maxZoom || 11052 zoom < this.options.minZoom) { 11053 this._removeAllTiles(); 11054 return; 11055 } 11056 11057 for (key in this._tiles) { 11058 tile = this._tiles[key]; 11059 tile.retain = tile.current; 11060 } 11061 11062 for (key in this._tiles) { 11063 tile = this._tiles[key]; 11064 if (tile.current && !tile.active) { 11065 var coords = tile.coords; 11066 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) { 11067 this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2); 11068 } 11069 } 11070 } 11071 11072 for (key in this._tiles) { 11073 if (!this._tiles[key].retain) { 11074 this._removeTile(key); 11075 } 11076 } 11077 }, 11078 11079 _removeTilesAtZoom: function (zoom) { 11080 for (var key in this._tiles) { 11081 if (this._tiles[key].coords.z !== zoom) { 11082 continue; 11083 } 11084 this._removeTile(key); 11085 } 11086 }, 11087 11088 _removeAllTiles: function () { 11089 for (var key in this._tiles) { 11090 this._removeTile(key); 11091 } 11092 }, 11093 11094 _invalidateAll: function () { 11095 for (var z in this._levels) { 11096 remove(this._levels[z].el); 11097 this._onRemoveLevel(Number(z)); 11098 delete this._levels[z]; 11099 } 11100 this._removeAllTiles(); 11101 11102 this._tileZoom = undefined; 11103 }, 11104 11105 _retainParent: function (x, y, z, minZoom) { 11106 var x2 = Math.floor(x / 2), 11107 y2 = Math.floor(y / 2), 11108 z2 = z - 1, 11109 coords2 = new Point(+x2, +y2); 11110 coords2.z = +z2; 11111 11112 var key = this._tileCoordsToKey(coords2), 11113 tile = this._tiles[key]; 11114 11115 if (tile && tile.active) { 11116 tile.retain = true; 11117 return true; 11118 11119 } else if (tile && tile.loaded) { 11120 tile.retain = true; 11121 } 11122 11123 if (z2 > minZoom) { 11124 return this._retainParent(x2, y2, z2, minZoom); 11125 } 11126 11127 return false; 11128 }, 11129 11130 _retainChildren: function (x, y, z, maxZoom) { 11131 11132 for (var i = 2 * x; i < 2 * x + 2; i++) { 11133 for (var j = 2 * y; j < 2 * y + 2; j++) { 11134 11135 var coords = new Point(i, j); 11136 coords.z = z + 1; 11137 11138 var key = this._tileCoordsToKey(coords), 11139 tile = this._tiles[key]; 11140 11141 if (tile && tile.active) { 11142 tile.retain = true; 11143 continue; 11144 11145 } else if (tile && tile.loaded) { 11146 tile.retain = true; 11147 } 11148 11149 if (z + 1 < maxZoom) { 11150 this._retainChildren(i, j, z + 1, maxZoom); 11151 } 11152 } 11153 } 11154 }, 11155 11156 _resetView: function (e) { 11157 var animating = e && (e.pinch || e.flyTo); 11158 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating); 11159 }, 11160 11161 _animateZoom: function (e) { 11162 this._setView(e.center, e.zoom, true, e.noUpdate); 11163 }, 11164 11165 _clampZoom: function (zoom) { 11166 var options = this.options; 11167 11168 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) { 11169 return options.minNativeZoom; 11170 } 11171 11172 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) { 11173 return options.maxNativeZoom; 11174 } 11175 11176 return zoom; 11177 }, 11178 11179 _setView: function (center, zoom, noPrune, noUpdate) { 11180 var tileZoom = Math.round(zoom); 11181 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) || 11182 (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) { 11183 tileZoom = undefined; 11184 } else { 11185 tileZoom = this._clampZoom(tileZoom); 11186 } 11187 11188 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom); 11189 11190 if (!noUpdate || tileZoomChanged) { 11191 11192 this._tileZoom = tileZoom; 11193 11194 if (this._abortLoading) { 11195 this._abortLoading(); 11196 } 11197 11198 this._updateLevels(); 11199 this._resetGrid(); 11200 11201 if (tileZoom !== undefined) { 11202 this._update(center); 11203 } 11204 11205 if (!noPrune) { 11206 this._pruneTiles(); 11207 } 11208 11209 // Flag to prevent _updateOpacity from pruning tiles during 11210 // a zoom anim or a pinch gesture 11211 this._noPrune = !!noPrune; 11212 } 11213 11214 this._setZoomTransforms(center, zoom); 11215 }, 11216 11217 _setZoomTransforms: function (center, zoom) { 11218 for (var i in this._levels) { 11219 this._setZoomTransform(this._levels[i], center, zoom); 11220 } 11221 }, 11222 11223 _setZoomTransform: function (level, center, zoom) { 11224 var scale = this._map.getZoomScale(zoom, level.zoom), 11225 translate = level.origin.multiplyBy(scale) 11226 .subtract(this._map._getNewPixelOrigin(center, zoom)).round(); 11227 11228 if (
vendor: 12,019 bytes, lines 11228-11604
11228any3d) { 11229 setTransform(level.el, translate, scale); 11230 } else { 11231 setPosition(level.el, translate); 11232 } 11233 }, 11234 11235 _resetGrid: function () { 11236 var map = this._map, 11237 crs = map.options.crs, 11238 tileSize = this._tileSize = this.getTileSize(), 11239 tileZoom = this._tileZoom; 11240 11241 var bounds = this._map.getPixelWorldBounds(this._tileZoom); 11242 if (bounds) { 11243 this._globalTileRange = this._pxBoundsToTileRange(bounds); 11244 } 11245 11246 this._wrapX = crs.wrapLng && !this.options.noWrap && [ 11247 Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x), 11248 Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y) 11249 ]; 11250 this._wrapY = crs.wrapLat && !this.options.noWrap && [ 11251 Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x), 11252 Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y) 11253 ]; 11254 }, 11255 11256 _onMoveEnd: function () { 11257 if (!this._map || this._map._animatingZoom) { return; } 11258 11259 this._update(); 11260 }, 11261 11262 _getTiledPixelBounds: function (center) { 11263 var map = this._map, 11264 mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(), 11265 scale = map.getZoomScale(mapZoom, this._tileZoom), 11266 pixelCenter = map.project(center, this._tileZoom).floor(), 11267 halfSize = map.getSize().divideBy(scale * 2); 11268 11269 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize)); 11270 }, 11271 11272 // Private method to load tiles in the grid's active zoom level according to map bounds 11273 _update: function (center) { 11274 var map = this._map; 11275 if (!map) { return; } 11276 var zoom = this._clampZoom(map.getZoom()); 11277 11278 if (center === undefined) { center = map.getCenter(); } 11279 if (this._tileZoom === undefined) { return; } // if out of minzoom/maxzoom 11280 11281 var pixelBounds = this._getTiledPixelBounds(center), 11282 tileRange = this._pxBoundsToTileRange(pixelBounds), 11283 tileCenter = tileRange.getCenter(), 11284 queue = [], 11285 margin = this.options.keepBuffer, 11286 noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]), 11287 tileRange.getTopRight().add([margin, -margin])); 11288 11289 // Sanity check: panic if the tile range contains Infinity somewhere. 11290 if (!(isFinite(tileRange.min.x) && 11291 isFinite(tileRange.min.y) && 11292 isFinite(tileRange.max.x) && 11293 isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); } 11294 11295 for (var key in this._tiles) { 11296 var c = this._tiles[key].coords; 11297 if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) { 11298 this._tiles[key].current = false; 11299 } 11300 } 11301 11302 // _update just loads more tiles. If the tile zoom level differs too much 11303 // from the map's, let _setView reset levels and prune old tiles. 11304 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; } 11305 11306 // create a queue of coordinates to load tiles from 11307 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) { 11308 for (var i = tileRange.min.x; i <= tileRange.max.x; i++) { 11309 var coords = new Point(i, j); 11310 coords.z = this._tileZoom; 11311 11312 if (!this._isValidTile(coords)) { continue; } 11313 11314 var tile = this._tiles[this._tileCoordsToKey(coords)]; 11315 if (tile) { 11316 tile.current = true; 11317 } else { 11318 queue.push(coords); 11319 } 11320 } 11321 } 11322 11323 // sort tile queue to load tiles in order of their distance to center 11324 queue.sort(function (a, b) { 11325 return a.distanceTo(tileCenter) - b.distanceTo(tileCenter); 11326 }); 11327 11328 if (queue.length !== 0) { 11329 // if it's the first batch of tiles to load 11330 if (!this._loading) { 11331 this._loading = true; 11332 // @event loading: Event 11333 // Fired when the grid layer starts loading tiles. 11334 this.fire('loading'); 11335 } 11336 11337 // create DOM fragment to append tiles in one batch 11338 var fragment = document.createDocumentFragment(); 11339 11340 for (i = 0; i < queue.length; i++) { 11341 this._addTile(queue[i], fragment); 11342 } 11343 11344 this._level.el.appendChild(fragment); 11345 } 11346 }, 11347 11348 _isValidTile: function (coords) { 11349 var crs = this._map.options.crs; 11350 11351 if (!crs.infinite) { 11352 // don't load tile if it's out of bounds and not wrapped 11353 var bounds = this._globalTileRange; 11354 if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) || 11355 (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; } 11356 } 11357 11358 if (!this.options.bounds) { return true; } 11359 11360 // don't load tile if it doesn't intersect the bounds in options 11361 var tileBounds = this._tileCoordsToBounds(coords); 11362 return toLatLngBounds(this.options.bounds).overlaps(tileBounds); 11363 }, 11364 11365 _keyToBounds: function (key) { 11366 return this._tileCoordsToBounds(this._keyToTileCoords(key)); 11367 }, 11368 11369 _tileCoordsToNwSe: function (coords) { 11370 var map = this._map, 11371 tileSize = this.getTileSize(), 11372 nwPoint = coords.scaleBy(tileSize), 11373 sePoint = nwPoint.add(tileSize), 11374 nw = map.unproject(nwPoint, coords.z), 11375 se = map.unproject(sePoint, coords.z); 11376 return [nw, se]; 11377 }, 11378 11379 // converts tile coordinates to its geographical bounds 11380 _tileCoordsToBounds: function (coords) { 11381 var bp = this._tileCoordsToNwSe(coords), 11382 bounds = new LatLngBounds(bp[0], bp[1]); 11383 11384 if (!this.options.noWrap) { 11385 bounds = this._map.wrapLatLngBounds(bounds); 11386 } 11387 return bounds; 11388 }, 11389 // converts tile coordinates to key for the tile cache 11390 _tileCoordsToKey: function (coords) { 11391 return coords.x + ':' + coords.y + ':' + coords.z; 11392 }, 11393 11394 // converts tile cache key to coordinates 11395 _keyToTileCoords: function (key) { 11396 var k = key.split(':'), 11397 coords = new Point(+k[0], +k[1]); 11398 coords.z = +k[2]; 11399 return coords; 11400 }, 11401 11402 _removeTile: function (key) { 11403 var tile = this._tiles[key]; 11404 if (!tile) { return; } 11405 11406 remove(tile.el); 11407 11408 delete this._tiles[key]; 11409 11410 // @event tileunload: TileEvent 11411 // Fired when a tile is removed (e.g. when a tile goes off the screen). 11412 this.fire('tileunload', { 11413 tile: tile.el, 11414 coords: this._keyToTileCoords(key) 11415 }); 11416 }, 11417 11418 _initTile: function (tile) { 11419 addClass(tile, 'leaflet-tile'); 11420 11421 var tileSize = this.getTileSize(); 11422 tile.style.width = tileSize.x + 'px'; 11423 tile.style.height = tileSize.y + 'px'; 11424 11425 tile.onselectstart = falseFn; 11426 tile.onmousemove = falseFn; 11427 11428 // update opacity on tiles in IE7-8 because of filter inheritance problems 11429 if (ielt9 && this.options.opacity < 1) { 11430 setOpacity(tile, this.options.opacity); 11431 } 11432 11433 // without this hack, tiles disappear after zoom on Chrome for Android 11434 // https://github.com/Leaflet/Leaflet/issues/2078 11435 if (android && !android23) { 11436 tile.style.WebkitBackfaceVisibility = 'hidden'; 11437 } 11438 }, 11439 11440 _addTile: function (coords, container) { 11441 var tilePos = this._getTilePos(coords), 11442 key = this._tileCoordsToKey(coords); 11443 11444 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords)); 11445 11446 this._initTile(tile); 11447 11448 // if createTile is defined with a second argument ("done" callback), 11449 // we know that tile is async and will be ready later; otherwise 11450 if (this.createTile.length < 2) { 11451 // mark tile as ready, but delay one frame for opacity animation to happen 11452 requestAnimFrame(bind(this._tileReady, this, coords, null, tile)); 11453 } 11454 11455 setPosition(tile, tilePos); 11456 11457 // save tile in cache 11458 this._tiles[key] = { 11459 el: tile, 11460 coords: coords, 11461 current: true 11462 }; 11463 11464 container.appendChild(tile); 11465 // @event tileloadstart: TileEvent 11466 // Fired when a tile is requested and starts loading. 11467 this.fire('tileloadstart', { 11468 tile: tile, 11469 coords: coords 11470 }); 11471 }, 11472 11473 _tileReady: function (coords, err, tile) { 11474 if (err) { 11475 // @event tileerror: TileErrorEvent 11476 // Fired when there is an error loading a tile. 11477 this.fire('tileerror', { 11478 error: err, 11479 tile: tile, 11480 coords: coords 11481 }); 11482 } 11483 11484 var key = this._tileCoordsToKey(coords); 11485 11486 tile = this._tiles[key]; 11487 if (!tile) { return; } 11488 11489 tile.loaded = +new Date(); 11490 if (this._map._fadeAnimated) { 11491 setOpacity(tile.el, 0); 11492 cancelAnimFrame(this._fadeFrame); 11493 this._fadeFrame = requestAnimFrame(this._updateOpacity, this); 11494 } else { 11495 tile.active = true; 11496 this._pruneTiles(); 11497 } 11498 11499 if (!err) { 11500 addClass(tile.el, 'leaflet-tile-loaded'); 11501 11502 // @event tileload: TileEvent 11503 // Fired when a tile loads. 11504 this.fire('tileload', { 11505 tile: tile.el, 11506 coords: coords 11507 }); 11508 } 11509 11510 if (this._noTilesToLoad()) { 11511 this._loading = false; 11512 // @event load: Event 11513 // Fired when the grid layer loaded all visible tiles. 11514 this.fire('load'); 11515 11516 if (ielt9 || !this._map._fadeAnimated) { 11517 requestAnimFrame(this._pruneTiles, this); 11518 } else { 11519 // Wait a bit more than 0.2 secs (the duration of the tile fade-in) 11520 // to trigger a pruning. 11521 setTimeout(bind(this._pruneTiles, this), 250); 11522 } 11523 } 11524 }, 11525 11526 _getTilePos: function (coords) { 11527 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin); 11528 }, 11529 11530 _wrapCoords: function (coords) { 11531 var newCoords = new Point( 11532 this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x, 11533 this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y); 11534 newCoords.z = coords.z; 11535 return newCoords; 11536 }, 11537 11538 _pxBoundsToTileRange: function (bounds) { 11539 var tileSize = this.getTileSize(); 11540 return new Bounds( 11541 bounds.min.unscaleBy(tileSize).floor(), 11542 bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1])); 11543 }, 11544 11545 _noTilesToLoad: function () { 11546 for (var key in this._tiles) { 11547 if (!this._tiles[key].loaded) { return false; } 11548 } 11549 return true; 11550 } 11551 }); 11552 11553 // @factory L.gridLayer(options?: GridLayer options) 11554 // Creates a new instance of GridLayer with the supplied options. 11555 function gridLayer(options) { 11556 return new GridLayer(options); 11557 } 11558 11559 /* 11560 * @class TileLayer 11561 * @inherits GridLayer 11562 * @aka L.TileLayer 11563 * 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`. 11564 * 11565 * @example 11566 * 11567 * ```js 11568 * 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); 11569 * ``` 11570 * 11571 * @section URL template 11572 * @example 11573 * 11574 * A string of the following form: 11575 * 11576 * ``` 11577 * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png' 11578 * ``` 11579 * 11580 * `{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. 11581 * 11582 * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this: 11583 * 11584 * ``` 11585 * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'}); 11586 * ``` 11587 */ 11588 11589 11590 var TileLayer = GridLayer.extend({ 11591 11592 // @section 11593 // @aka TileLayer options 11594 options: { 11595 // @option minZoom: Number = 0 11596 // The minimum zoom level down to which this layer will be displayed (inclusive). 11597 minZoom: 0, 11598 11599 // @option maxZoom: Number = 18 11600 // The maximum zoom level up to which this layer will be displayed (inclusive). 11601 maxZoom: 18, 11602 11603 // @option subdomains: String|String[] = 'abc' 11604 // 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 str
11604ings. 11605 subdomains: 'abc', 11606 11607 // @option errorTileUrl: String = '' 11608 // URL to the tile image to show in place of the tile that failed to load. 11609 errorTileUrl: '', 11610 11611 // @option zoomOffset: Number = 0 11612 // The zoom number used in tile URLs will be offset with this value. 11613 zoomOffset: 0, 11614 11615 // @option tms: Boolean = false 11616 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services). 11617 tms: false, 11618 11619 // @option zoomReverse: Boolean = false 11620 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`) 11621 zoomReverse: false, 11622 11623 // @option detectRetina: Boolean = false 11624 // 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. 11625 detectRetina: false, 11626 11627 // @option crossOrigin: Boolean|String = false 11628 // Whether the crossOrigin attribute will be added to the tiles. 11629 // 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. 11630 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values. 11631 crossOrigin: false 11632 }, 11633 11634 initialize: function (url, options) { 11635 11636 this._url = url; 11637 11638 options = setOptions(this, options); 11639 11640 // detecting retina displays, adjusting tileSize and zoom levels 11641 if (options.detectRetina && retina && options.maxZoom > 0) { 11642 11643 options.tileSize = Math.floor(options.tileSize / 2); 11644 11645 if (!options.zoomReverse) { 11646 options.zoomOffset++; 11647 options.maxZoom--; 11648 } else { 11649 options.zoomOffset--; 11650 options.minZoom++; 11651 } 11652 11653 options.minZoom = Math.max(0, options.minZoom); 11654 } 11655 11656 if (typeof options.subdomains === 'string') { 11657 options.subdomains = options.subdomains.split(''); 11658 } 11659 11660 // for https://github.com/Leaflet/Leaflet/issues/137 11661 if (!android) { 11662 this.on('tileunload', this._onTileRemove); 11663 } 11664 }, 11665 11666 // @method setUrl(url: String, noRedraw?: Boolean): this 11667 // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`). 11668 // If the URL does not change, the layer will not be redrawn unless 11669 // the noRedraw parameter is set to false. 11670 setUrl: function (url, noRedraw) { 11671 if (this._url === url && noRedraw === undefined) { 11672 noRedraw = true; 11673 } 11674 11675 this._url = url; 11676 11677 if (!noRedraw) { 11678 this.redraw(); 11679 } 11680 return this; 11681 }, 11682 11683 // @method createTile(coords: Object, done?: Function): HTMLElement 11684 // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile) 11685 // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done` 11686 // callback is called when the tile has been loaded. 11687 createTile: function (coords, done) { 11688 var tile = document.createElement('img'); 11689 11690 on(tile, 'load', bind(this._tileOnLoad, this, done, tile)); 11691 on(tile, 'error', bind(this._tileOnError, this, done, tile)); 11692 11693 if (this.options.crossOrigin || this.options.crossOrigin === '') { 11694 tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin; 11695 } 11696 11697 /* 11698 Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons 11699 http://www.w3.org/TR/WCAG20-TECHS/H67 11700 */ 11701 tile.alt = ''; 11702 11703 /* 11704 Set role="presentation" to force screen readers to ignore this 11705 https://www.w3.org/TR/wai-aria/roles#textalternativecomputation 11706 */ 11707 tile.setAttribute('role', 'presentation'); 11708 11709 tile.src = this.getTileUrl(coords); 11710 11711 return tile; 11712 }, 11713 11714 // @section Extension methods 11715 // @uninheritable 11716 // Layers extending `TileLayer` might reimplement the following method. 11717 // @method getTileUrl(coords: Object): String 11718 // Called only internally, returns the URL for a tile given its coordinates. 11719 // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes. 11720 getTileUrl: function (coords) { 11721 var data = { 11722 r: retina ? '@2x' : '', 11723 s: this._getSubdomain(coords), 11724 x: coords.x, 11725 y: coords.y, 11726 z: this._getZoomForUrl() 11727 }; 11728 if (this._map && !this._map.options.crs.infinite) { 11729 var invertedY = this._globalTileRange.max.y - coords.y; 11730 if (this.options.tms) { 11731 data['y'] = invertedY; 11732 }
vendor: 32,187 bytes, lines 11733-12925
11733 data['-y'] = invertedY; 11734 } 11735 11736 return template(this._url, extend(data, this.options)); 11737 }, 11738 11739 _tileOnLoad: function (done, tile) { 11740 // For https://github.com/Leaflet/Leaflet/issues/3332 11741 if (ielt9) { 11742 setTimeout(bind(done, this, null, tile), 0); 11743 } else { 11744 done(null, tile); 11745 } 11746 }, 11747 11748 _tileOnError: function (done, tile, e) { 11749 var errorUrl = this.options.errorTileUrl; 11750 if (errorUrl && tile.getAttribute('src') !== errorUrl) { 11751 tile.src = errorUrl; 11752 } 11753 done(e, tile); 11754 }, 11755 11756 _onTileRemove: function (e) { 11757 e.tile.onload = null; 11758 }, 11759 11760 _getZoomForUrl: function () { 11761 var zoom = this._tileZoom, 11762 maxZoom = this.options.maxZoom, 11763 zoomReverse = this.options.zoomReverse, 11764 zoomOffset = this.options.zoomOffset; 11765 11766 if (zoomReverse) { 11767 zoom = maxZoom - zoom; 11768 } 11769 11770 return zoom + zoomOffset; 11771 }, 11772 11773 _getSubdomain: function (tilePoint) { 11774 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length; 11775 return this.options.subdomains[index]; 11776 }, 11777 11778 // stops loading all tiles in the background layer 11779 _abortLoading: function () { 11780 var i, tile; 11781 for (i in this._tiles) { 11782 if (this._tiles[i].coords.z !== this._tileZoom) { 11783 tile = this._tiles[i].el; 11784 11785 tile.onload = falseFn; 11786 tile.onerror = falseFn; 11787 11788 if (!tile.complete) { 11789 tile.src = emptyImageUrl; 11790 remove(tile); 11791 delete this._tiles[i]; 11792 } 11793 } 11794 } 11795 }, 11796 11797 _removeTile: function (key) { 11798 var tile = this._tiles[key]; 11799 if (!tile) { return; } 11800 11801 // Cancels any pending http requests associated with the tile 11802 // unless we're on Android's stock browser, 11803 // see https://github.com/Leaflet/Leaflet/issues/137 11804 if (!androidStock) { 11805 tile.el.setAttribute('src', emptyImageUrl); 11806 } 11807 11808 return GridLayer.prototype._removeTile.call(this, key); 11809 }, 11810 11811 _tileReady: function (coords, err, tile) { 11812 if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) { 11813 return; 11814 } 11815 11816 return GridLayer.prototype._tileReady.call(this, coords, err, tile); 11817 } 11818 }); 11819 11820 11821 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options) 11822 // Instantiates a tile layer object given a `URL template` and optionally an options object. 11823 11824 function tileLayer(url, options) { 11825 return new TileLayer(url, options); 11826 } 11827 11828 /* 11829 * @class TileLayer.WMS 11830 * @inherits TileLayer 11831 * @aka L.TileLayer.WMS 11832 * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`. 11833 * 11834 * @example 11835 * 11836 * ```js 11837 * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", { 11838 * layers: 'nexrad-n0r-900913', 11839 * format: 'image/png', 11840 * transparent: true, 11841 * attribution: "Weather data © 2012 IEM Nexrad" 11842 * }); 11843 * ``` 11844 */ 11845 11846 var TileLayerWMS = TileLayer.extend({ 11847 11848 // @section 11849 // @aka TileLayer.WMS options 11850 // If any custom options not documented here are used, they will be sent to the 11851 // WMS server as extra parameters in each request URL. This can be useful for 11852 // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html). 11853 defaultWmsParams: { 11854 service: 'WMS', 11855 request: 'GetMap', 11856 11857 // @option layers: String = '' 11858 // **(required)** Comma-separated list of WMS layers to show. 11859 layers: '', 11860 11861 // @option styles: String = '' 11862 // Comma-separated list of WMS styles. 11863 styles: '', 11864 11865 // @option format: String = 'image/jpeg' 11866 // WMS image format (use `'image/png'` for layers with transparency). 11867 format: 'image/jpeg', 11868 11869 // @option transparent: Boolean = false 11870 // If `true`, the WMS service will return images with transparency. 11871 transparent: false, 11872 11873 // @option version: String = '1.1.1' 11874 // Version of the WMS service to use 11875 version: '1.1.1' 11876 }, 11877 11878 options: { 11879 // @option crs: CRS = null 11880 // Coordinate Reference System to use for the WMS requests, defaults to 11881 // map CRS. Don't change this if you're not sure what it means. 11882 crs: null, 11883 11884 // @option uppercase: Boolean = false 11885 // If `true`, WMS request parameter keys will be uppercase. 11886 uppercase: false 11887 }, 11888 11889 initialize: function (url, options) { 11890 11891 this._url = url; 11892 11893 var wmsParams = extend({}, this.defaultWmsParams); 11894 11895 // all keys that are not TileLayer options go to WMS params 11896 for (var i in options) { 11897 if (!(i in this.options)) { 11898 wmsParams[i] = options[i]; 11899 } 11900 } 11901 11902 options = setOptions(this, options); 11903 11904 var realRetina = options.detectRetina && retina ? 2 : 1; 11905 var tileSize = this.getTileSize(); 11906 wmsParams.width = tileSize.x * realRetina; 11907 wmsParams.height = tileSize.y * realRetina; 11908 11909 this.wmsParams = wmsParams; 11910 }, 11911 11912 onAdd: function (map) { 11913 11914 this._crs = this.options.crs || map.options.crs; 11915 this._wmsVersion = parseFloat(this.wmsParams.version); 11916 11917 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs'; 11918 this.wmsParams[projectionKey] = this._crs.code; 11919 11920 TileLayer.prototype.onAdd.call(this, map); 11921 }, 11922 11923 getTileUrl: function (coords) { 11924 11925 var tileBounds = this._tileCoordsToNwSe(coords), 11926 crs = this._crs, 11927 bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])), 11928 min = bounds.min, 11929 max = bounds.max, 11930 bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ? 11931 [min.y, min.x, max.y, max.x] : 11932 [min.x, min.y, max.x, max.y]).join(','), 11933 url = TileLayer.prototype.getTileUrl.call(this, coords); 11934 return url + 11935 getParamString(this.wmsParams, url, this.options.uppercase) + 11936 (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox; 11937 }, 11938 11939 // @method setParams(params: Object, noRedraw?: Boolean): this 11940 // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true). 11941 setParams: function (params, noRedraw) { 11942 11943 extend(this.wmsParams, params); 11944 11945 if (!noRedraw) { 11946 this.redraw(); 11947 } 11948 11949 return this; 11950 } 11951 }); 11952 11953 11954 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options) 11955 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object. 11956 function tileLayerWMS(url, options) { 11957 return new TileLayerWMS(url, options); 11958 } 11959 11960 TileLayer.WMS = TileLayerWMS; 11961 tileLayer.wms = tileLayerWMS; 11962 11963 /* 11964 * @class Renderer 11965 * @inherits Layer 11966 * @aka L.Renderer 11967 * 11968 * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the 11969 * DOM container of the renderer, its bounds, and its zoom animation. 11970 * 11971 * A `Renderer` works as an implicit layer group for all `Path`s - the renderer 11972 * itself can be added or removed to the map. All paths use a renderer, which can 11973 * be implicit (the map will decide the type of renderer and use it automatically) 11974 * or explicit (using the [`renderer`](#path-renderer) option of the path). 11975 * 11976 * Do not use this class directly, use `SVG` and `Canvas` instead. 11977 * 11978 * @event update: Event 11979 * Fired when the renderer updates its bounds, center and zoom, for example when 11980 * its map has moved 11981 */ 11982 11983 var Renderer = Layer.extend({ 11984 11985 // @section 11986 // @aka Renderer options 11987 options: { 11988 // @option padding: Number = 0.1 11989 // How much to extend the clip area around the map view (relative to its size) 11990 // e.g. 0.1 would be 10% of map view in each direction 11991 padding: 0.1, 11992 11993 // @option tolerance: Number = 0 11994 // How much to extend click tolerance round a path/object on the map 11995 tolerance : 0 11996 }, 11997 11998 initialize: function (options) { 11999 setOptions(this, options); 12000 stamp(this); 12001 this._layers = this._layers || {}; 12002 }, 12003 12004 onAdd: function () { 12005 if (!this._container) { 12006 this._initContainer(); // defined by renderer implementations 12007 12008 if (this._zoomAnimated) { 12009 addClass(this._container, 'leaflet-zoom-animated'); 12010 } 12011 } 12012 12013 this.getPane().appendChild(this._container); 12014 this._update(); 12015 this.on('update', this._updatePaths, this); 12016 }, 12017 12018 onRemove: function () { 12019 this.off('update', this._updatePaths, this); 12020 this._destroyContainer(); 12021 }, 12022 12023 getEvents: function () { 12024 var events = { 12025 viewreset: this._reset, 12026 zoom: this._onZoom, 12027 moveend: this._update, 12028 zoomend: this._onZoomEnd 12029 }; 12030 if (this._zoomAnimated) { 12031 events.zoomanim = this._onAnimZoom; 12032 } 12033 return events; 12034 }, 12035 12036 _onAnimZoom: function (ev) { 12037 this._updateTransform(ev.center, ev.zoom); 12038 }, 12039 12040 _onZoom: function () { 12041 this._updateTransform(this._map.getCenter(), this._map.getZoom()); 12042 }, 12043 12044 _updateTransform: function (center, zoom) { 12045 var scale = this._map.getZoomScale(zoom, this._zoom), 12046 position = getPosition(this._container), 12047 viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding), 12048 currentCenterPoint = this._map.project(this._center, zoom), 12049 destCenterPoint = this._map.project(center, zoom), 12050 centerOffset = destCenterPoint.subtract(currentCenterPoint), 12051 12052 topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset); 12053 12054 if (any3d) { 12055 setTransform(this._container, topLeftOffset, scale); 12056 } else { 12057 setPosition(this._container, topLeftOffset); 12058 } 12059 }, 12060 12061 _reset: function () { 12062 this._update(); 12063 this._updateTransform(this._center, this._zoom); 12064 12065 for (var id in this._layers) { 12066 this._layers[id]._reset(); 12067 } 12068 }, 12069 12070 _onZoomEnd: function () { 12071 for (var id in this._layers) { 12072 this._layers[id]._project(); 12073 } 12074 }, 12075 12076 _updatePaths: function () { 12077 for (var id in this._layers) { 12078 this._layers[id]._update(); 12079 } 12080 }, 12081 12082 _update: function () { 12083 // Update pixel bounds of renderer container (for positioning/sizing/clipping later) 12084 // Subclasses are responsible of firing the 'update' event. 12085 var p = this.options.padding, 12086 size = this._map.getSize(), 12087 min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round(); 12088 12089 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round()); 12090 12091 this._center = this._map.getCenter(); 12092 this._zoom = this._map.getZoom(); 12093 } 12094 }); 12095 12096 /* 12097 * @class Canvas 12098 * @inherits Renderer 12099 * @aka L.Canvas 12100 * 12101 * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API). 12102 * Inherits `Renderer`. 12103 * 12104 * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not 12105 * available in all web browsers, notably IE8, and overlapping geometries might 12106 * not display properly in some edge cases. 12107 * 12108 * @example 12109 * 12110 * Use Canvas by default for all paths in the map: 12111 * 12112 * ```js 12113 * var map = L.map('map', { 12114 * renderer: L.canvas() 12115 * }); 12116 * ``` 12117 * 12118 * Use a Canvas renderer with extra padding for specific vector geometries: 12119 * 12120 * ```js 12121 * var map = L.map('map'); 12122 * var myRenderer = L.canvas({ padding: 0.5 }); 12123 * var line = L.polyline( coordinates, { renderer: myRenderer } ); 12124 * var circle = L.circle( center, { renderer: myRenderer } ); 12125 * ``` 12126 */ 12127 12128 var Canvas = Renderer.extend({ 12129 getEvents: function () { 12130 var events = Renderer.prototype.getEvents.call(this); 12131 events.viewprereset = this._onViewPreReset; 12132 return events; 12133 }, 12134 12135 _onViewPreReset: function () { 12136 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once 12137 this._postponeUpdatePaths = true; 12138 }, 12139 12140 onAdd: function () { 12141 Renderer.prototype.onAdd.call(this); 12142 12143 // Redraw vectors since canvas is cleared upon removal, 12144 // in case of removing the renderer itself from the map. 12145 this._draw(); 12146 }, 12147 12148 _initContainer: function () { 12149 var container = this._container = document.createElement('canvas'); 12150 12151 on(container, 'mousemove', this._onMouseMove, this); 12152 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this); 12153 on(container, 'mouseout', this._handleMouseOut, this); 12154 12155 this._ctx = container.getContext('2d'); 12156 }, 12157 12158 _destroyContainer: function () { 12159 cancelAnimFrame(this._redrawRequest); 12160 delete this._ctx; 12161 remove(this._container); 12162 off(this._container); 12163 delete this._container; 12164 }, 12165 12166 _updatePaths: function () { 12167 if (this._postponeUpdatePaths) { return; } 12168 12169 var layer; 12170 this._redrawBounds = null; 12171 for (var id in this._layers) { 12172 layer = this._layers[id]; 12173 layer._update(); 12174 } 12175 this._redraw(); 12176 }, 12177 12178 _update: function () { 12179 if (this._map._animatingZoom && this._bounds) { return; } 12180 12181 Renderer.prototype._update.call(this); 12182 12183 var b = this._bounds, 12184 container = this._container, 12185 size = b.getSize(), 12186 m = retina ? 2 : 1; 12187 12188 setPosition(container, b.min); 12189 12190 // set canvas size (also clearing it); use double size on retina 12191 container.width = m * size.x; 12192 container.height = m * size.y; 12193 container.style.width = size.x + 'px'; 12194 container.style.height = size.y + 'px'; 12195 12196 if (retina) { 12197 this._ctx.scale(2, 2); 12198 } 12199 12200 // translate so we use the same path coordinates after canvas element moves 12201 this._ctx.translate(-b.min.x, -b.min.y); 12202 12203 // Tell paths to redraw themselves 12204 this.fire('update'); 12205 }, 12206 12207 _reset: function () { 12208 Renderer.prototype._reset.call(this); 12209 12210 if (this._postponeUpdatePaths) { 12211 this._postponeUpdatePaths = false; 12212 this._updatePaths(); 12213 } 12214 }, 12215 12216 _initPath: function (layer) { 12217 this._updateDashArray(layer); 12218 this._layers[stamp(layer)] = layer; 12219 12220 var order = layer._order = { 12221 layer: layer, 12222 prev: this._drawLast, 12223 next: null 12224 }; 12225 if (this._drawLast) { this._drawLast.next = order; } 12226 this._drawLast = order; 12227 this._drawFirst = this._drawFirst || this._drawLast; 12228 }, 12229 12230 _addPath: function (layer) { 12231 this._requestRedraw(layer); 12232 }, 12233 12234 _removePath: function (layer) { 12235 var order = layer._order; 12236 var next = order.next; 12237 var prev = order.prev; 12238 12239 if (next) { 12240 next.prev = prev; 12241 } else { 12242 this._drawLast = prev; 12243 } 12244 if (prev) { 12245 prev.next = next; 12246 } else { 12247 this._drawFirst = next; 12248 } 12249 12250 delete layer._order; 12251 12252 delete this._layers[stamp(layer)]; 12253 12254 this._requestRedraw(layer); 12255 }, 12256 12257 _updatePath: function (layer) { 12258 // Redraw the union of the layer's old pixel 12259 // bounds and the new pixel bounds. 12260 this._extendRedrawBounds(layer); 12261 layer._project(); 12262 layer._update(); 12263 // The redraw will extend the redraw bounds 12264 // with the new pixel bounds. 12265 this._requestRedraw(layer); 12266 }, 12267 12268 _updateStyle: function (layer) { 12269 this._updateDashArray(layer); 12270 this._requestRedraw(layer); 12271 }, 12272 12273 _updateDashArray: function (layer) { 12274 if (typeof layer.options.dashArray === 'string') { 12275 var parts = layer.options.dashArray.split(/[, ]+/), 12276 dashArray = [], 12277 dashValue, 12278 i; 12279 for (i = 0; i < parts.length; i++) { 12280 dashValue = Number(parts[i]); 12281 // Ignore dash array containing invalid lengths 12282 if (isNaN(dashValue)) { return; } 12283 dashArray.push(dashValue); 12284 } 12285 layer.options._dashArray = dashArray; 12286 } else { 12287 layer.options._dashArray = layer.options.dashArray; 12288 } 12289 }, 12290 12291 _requestRedraw: function (layer) { 12292 if (!this._map) { return; } 12293 12294 this._extendRedrawBounds(layer); 12295 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this); 12296 }, 12297 12298 _extendRedrawBounds: function (layer) { 12299 if (layer._pxBounds) { 12300 var padding = (layer.options.weight || 0) + 1; 12301 this._redrawBounds = this._redrawBounds || new Bounds(); 12302 this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding])); 12303 this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding])); 12304 } 12305 }, 12306 12307 _redraw: function () { 12308 this._redrawRequest = null; 12309 12310 if (this._redrawBounds) { 12311 this._redrawBounds.min._floor(); 12312 this._redrawBounds.max._ceil(); 12313 } 12314 12315 this._clear(); // clear layers in redraw bounds 12316 this._draw(); // draw layers 12317 12318 this._redrawBounds = null; 12319 }, 12320 12321 _clear: function () { 12322 var bounds = this._redrawBounds; 12323 if (bounds) { 12324 var size = bounds.getSize(); 12325 this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y); 12326 } else { 12327 this._ctx.save(); 12328 this._ctx.setTransform(1, 0, 0, 1, 0, 0); 12329 this._ctx.clearRect(0, 0, this._container.width, this._container.height); 12330 this._ctx.restore(); 12331 } 12332 }, 12333 12334 _draw: function () { 12335 var layer, bounds = this._redrawBounds; 12336 this._ctx.save(); 12337 if (bounds) { 12338 var size = bounds.getSize(); 12339 this._ctx.beginPath(); 12340 this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y); 12341 this._ctx.clip(); 12342 } 12343 12344 this._drawing = true; 12345 12346 for (var order = this._drawFirst; order; order = order.next) { 12347 layer = order.layer; 12348 if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) { 12349 layer._updatePath(); 12350 } 12351 } 12352 12353 this._drawing = false; 12354 12355 this._ctx.restore(); // Restore state before clipping. 12356 }, 12357 12358 _updatePoly: function (layer, closed) { 12359 if (!this._drawing) { return; } 12360 12361 var i, j, len2, p, 12362 parts = layer._parts, 12363 len = parts.length, 12364 ctx = this._ctx; 12365 12366 if (!len) { return; } 12367 12368 ctx.beginPath(); 12369 12370 for (i = 0; i < len; i++) { 12371 for (j = 0, len2 = parts[i].length; j < len2; j++) { 12372 p = parts[i][j]; 12373 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y); 12374 } 12375 if (closed) { 12376 ctx.closePath(); 12377 } 12378 } 12379 12380 this._fillStroke(ctx, layer); 12381 12382 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature 12383 }, 12384 12385 _updateCircle: function (layer) { 12386 12387 if (!this._drawing || layer._empty()) { return; } 12388 12389 var p = layer._point, 12390 ctx = this._ctx, 12391 r = Math.max(Math.round(layer._radius), 1), 12392 s = (Math.max(Math.round(layer._radiusY), 1) || r) / r; 12393 12394 if (s !== 1) { 12395 ctx.save(); 12396 ctx.scale(1, s); 12397 } 12398 12399 ctx.beginPath(); 12400 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false); 12401 12402 if (s !== 1) { 12403 ctx.restore(); 12404 } 12405 12406 this._fillStroke(ctx, layer); 12407 }, 12408 12409 _fillStroke: function (ctx, layer) { 12410 var options = layer.options; 12411 12412 if (options.fill) { 12413 ctx.globalAlpha = options.fillOpacity; 12414 ctx.fillStyle = options.fillColor || options.color; 12415 ctx.fill(options.fillRule || 'evenodd'); 12416 } 12417 12418 if (options.stroke && options.weight !== 0) { 12419 if (ctx.setLineDash) { 12420 ctx.setLineDash(layer.options && layer.options._dashArray || []); 12421 } 12422 ctx.globalAlpha = options.opacity; 12423 ctx.lineWidth = options.weight; 12424 ctx.strokeStyle = options.color; 12425 ctx.lineCap = options.lineCap; 12426 ctx.lineJoin = options.lineJoin; 12427 ctx.stroke(); 12428 } 12429 }, 12430 12431 // Canvas obviously doesn't have mouse events for individual drawn objects, 12432 // so we emulate that by calculating what's under the mouse on mousemove/click manually 12433 12434 _onClick: function (e) { 12435 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer; 12436 12437 for (var order = this._drawFirst; order; order = order.next) { 12438 layer = order.layer; 12439 if (layer.options.interactive && layer._containsPoint(point)) { 12440 if (!(e.type === 'click' || e.type !== 'preclick') || !this._map._draggableMoved(layer)) { 12441 clickedLayer = layer; 12442 } 12443 } 12444 } 12445 if (clickedLayer) { 12446 fakeStop(e); 12447 this._fireEvent([clickedLayer], e); 12448 } 12449 }, 12450 12451 _onMouseMove: function (e) { 12452 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; } 12453 12454 var point = this._map.mouseEventToLayerPoint(e); 12455 this._handleMouseHover(e, point); 12456 }, 12457 12458 12459 _handleMouseOut: function (e) { 12460 var layer = this._hoveredLayer; 12461 if (layer) { 12462 // if we're leaving the layer, fire mouseout 12463 removeClass(this._container, 'leaflet-interactive'); 12464 this._fireEvent([layer], e, 'mouseout'); 12465 this._hoveredLayer = null; 12466 this._mouseHoverThrottled = false; 12467 } 12468 }, 12469 12470 _handleMouseHover: function (e, point) { 12471 if (this._mouseHoverThrottled) { 12472 return; 12473 } 12474 12475 var layer, candidateHoveredLayer; 12476 12477 for (var order = this._drawFirst; order; order = order.next) { 12478 layer = order.layer; 12479 if (layer.options.interactive && layer._containsPoint(point)) { 12480 candidateHoveredLayer = layer; 12481 } 12482 } 12483 12484 if (candidateHoveredLayer !== this._hoveredLayer) { 12485 this._handleMouseOut(e); 12486 12487 if (candidateHoveredLayer) { 12488 addClass(this._container, 'leaflet-interactive'); // change cursor 12489 this._fireEvent([candidateHoveredLayer], e, 'mouseover'); 12490 this._hoveredLayer = candidateHoveredLayer; 12491 } 12492 } 12493 12494 if (this._hoveredLayer) { 12495 this._fireEvent([this._hoveredLayer], e); 12496 } 12497 12498 this._mouseHoverThrottled = true; 12499 setTimeout(bind(function () { 12500 this._mouseHoverThrottled = false; 12501 }, this), 32); 12502 }, 12503 12504 _fireEvent: function (layers, e, type) { 12505 this._map._fireDOMEvent(e, type || e.type, layers); 12506 }, 12507 12508 _bringToFront: function (layer) { 12509 var order = layer._order; 12510 12511 if (!order) { return; } 12512 12513 var next = order.next; 12514 var prev = order.prev; 12515 12516 if (next) { 12517 next.prev = prev; 12518 } else { 12519 // Already last 12520 return; 12521 } 12522 if (prev) { 12523 prev.next = next; 12524 } else if (next) { 12525 // Update first entry unless this is the 12526 // single entry 12527 this._drawFirst = next; 12528 } 12529 12530 order.prev = this._drawLast; 12531 this._drawLast.next = order; 12532 12533 order.next = null; 12534 this._drawLast = order; 12535 12536 this._requestRedraw(layer); 12537 }, 12538 12539 _bringToBack: function (layer) { 12540 var order = layer._order; 12541 12542 if (!order) { return; } 12543 12544 var next = order.next; 12545 var prev = order.prev; 12546 12547 if (prev) { 12548 prev.next = next; 12549 } else { 12550 // Already first 12551 return; 12552 } 12553 if (next) { 12554 next.prev = prev; 12555 } else if (prev) { 12556 // Update last entry unless this is the 12557 // single entry 12558 this._drawLast = prev; 12559 } 12560 12561 order.prev = null; 12562 12563 order.next = this._drawFirst; 12564 this._drawFirst.prev = order; 12565 this._drawFirst = order; 12566 12567 this._requestRedraw(layer); 12568 } 12569 }); 12570 12571 // @factory L.canvas(options?: Renderer options) 12572 // Creates a Canvas renderer with the given options. 12573 function canvas(options) { 12574 return canvas$1 ? new Canvas(options) : null; 12575 } 12576 12577 /* 12578 * Thanks to Dmitry Baranovsky and his Raphael library for inspiration! 12579 */ 12580 12581 12582 var vmlCreate = (function () { 12583 try { 12584 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml'); 12585 return function (name) { 12586 return document.createElement('<lvml:' + name + ' class="lvml">'); 12587 }; 12588 } catch (e) { 12589 return function (name) { 12590 return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">'); 12591 }; 12592 } 12593 })(); 12594 12595 12596 /* 12597 * @class SVG 12598 * 12599 * 12600 * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility 12601 * with old versions of Internet Explorer. 12602 */ 12603 12604 // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences 12605 var vmlMixin = { 12606 12607 _initContainer: function () { 12608 this._container = create$1('div', 'leaflet-vml-container'); 12609 }, 12610 12611 _update: function () { 12612 if (this._map._animatingZoom) { return; } 12613 Renderer.prototype._update.call(this); 12614 this.fire('update'); 12615 }, 12616 12617 _initPath: function (layer) { 12618 var container = layer._container = vmlCreate('shape'); 12619 12620 addClass(container, 'leaflet-vml-shape ' + (this.options.className || '')); 12621 12622 container.coordsize = '1 1'; 12623 12624 layer._path = vmlCreate('path'); 12625 container.appendChild(layer._path); 12626 12627 this._updateStyle(layer); 12628 this._layers[stamp(layer)] = layer; 12629 }, 12630 12631 _addPath: function (layer) { 12632 var container = layer._container; 12633 this._container.appendChild(container); 12634 12635 if (layer.options.interactive) { 12636 layer.addInteractiveTarget(container); 12637 } 12638 }, 12639 12640 _removePath: function (layer) { 12641 var container = layer._container; 12642 remove(container); 12643 layer.removeInteractiveTarget(container); 12644 delete this._layers[stamp(layer)]; 12645 }, 12646 12647 _updateStyle: function (layer) { 12648 var stroke = layer._stroke, 12649 fill = layer._fill, 12650 options = layer.options, 12651 container = layer._container; 12652 12653 container.stroked = !!options.stroke; 12654 container.filled = !!options.fill; 12655 12656 if (options.stroke) { 12657 if (!stroke) { 12658 stroke = layer._stroke = vmlCreate('stroke'); 12659 } 12660 container.appendChild(stroke); 12661 stroke.weight = options.weight + 'px'; 12662 stroke.color = options.color; 12663 stroke.opacity = options.opacity; 12664 12665 if (options.dashArray) { 12666 stroke.dashStyle = isArray(options.dashArray) ? 12667 options.dashArray.join(' ') : 12668 options.dashArray.replace(/( *, *)/g, ' '); 12669 } else { 12670 stroke.dashStyle = ''; 12671 } 12672 stroke.endcap = options.lineCap.replace('butt', 'flat'); 12673 stroke.joinstyle = options.lineJoin; 12674 12675 } else if (stroke) { 12676 container.removeChild(stroke); 12677 layer._stroke = null; 12678 } 12679 12680 if (options.fill) { 12681 if (!fill) { 12682 fill = layer._fill = vmlCreate('fill'); 12683 } 12684 container.appendChild(fill); 12685 fill.color = options.fillColor || options.color; 12686 fill.opacity = options.fillOpacity; 12687 12688 } else if (fill) { 12689 container.removeChild(fill); 12690 layer._fill = null; 12691 } 12692 }, 12693 12694 _updateCircle: function (layer) { 12695 var p = layer._point.round(), 12696 r = Math.round(layer._radius), 12697 r2 = Math.round(layer._radiusY || r); 12698 12699 this._setPath(layer, layer._empty() ? 'M0 0' : 12700 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360)); 12701 }, 12702 12703 _setPath: function (layer, path) { 12704 layer._path.v = path; 12705 }, 12706 12707 _bringToFront: function (layer) { 12708 toFront(layer._container); 12709 }, 12710 12711 _bringToBack: function (layer) { 12712 toBack(layer._container); 12713 } 12714 }; 12715 12716 var create = vml ? vmlCreate : svgCreate; 12717 12718 /* 12719 * @class SVG 12720 * @inherits Renderer 12721 * @aka L.SVG 12722 * 12723 * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG). 12724 * Inherits `Renderer`. 12725 * 12726 * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not 12727 * available in all web browsers, notably Android 2.x and 3.x. 12728 * 12729 * Although SVG is not available on IE7 and IE8, these browsers support 12730 * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language) 12731 * (a now deprecated technology), and the SVG renderer will fall back to VML in 12732 * this case. 12733 * 12734 * @example 12735 * 12736 * Use SVG by default for all paths in the map: 12737 * 12738 * ```js 12739 * var map = L.map('map', { 12740 * renderer: L.svg() 12741 * }); 12742 * ``` 12743 * 12744 * Use a SVG renderer with extra padding for specific vector geometries: 12745 * 12746 * ```js 12747 * var map = L.map('map'); 12748 * var myRenderer = L.svg({ padding: 0.5 }); 12749 * var line = L.polyline( coordinates, { renderer: myRenderer } ); 12750 * var circle = L.circle( center, { renderer: myRenderer } ); 12751 * ``` 12752 */ 12753 12754 var SVG = Renderer.extend({ 12755 12756 getEvents: function () { 12757 var events = Renderer.prototype.getEvents.call(this); 12758 events.zoomstart = this._onZoomStart; 12759 return events; 12760 }, 12761 12762 _initContainer: function () { 12763 this._container = create('svg'); 12764 12765 // makes it possible to click through svg root; we'll reset it back in individual paths 12766 this._container.setAttribute('pointer-events', 'none'); 12767 12768 this._rootGroup = create('g'); 12769 this._container.appendChild(this._rootGroup); 12770 }, 12771 12772 _destroyContainer: function () { 12773 remove(this._container); 12774 off(this._container); 12775 delete this._container; 12776 delete this._rootGroup; 12777 delete this._svgSize; 12778 }, 12779 12780 _onZoomStart: function () { 12781 // Drag-then-pinch interactions might mess up the center and zoom. 12782 // In this case, the easiest way to prevent this is re-do the renderer 12783 // bounds and padding when the zooming starts. 12784 this._update(); 12785 }, 12786 12787 _update: function () { 12788 if (this._map._animatingZoom && this._bounds) { return; } 12789 12790 Renderer.prototype._update.call(this); 12791 12792 var b = this._bounds, 12793 size = b.getSize(), 12794 container = this._container; 12795 12796 // set size of svg-container if changed 12797 if (!this._svgSize || !this._svgSize.equals(size)) { 12798 this._svgSize = size; 12799 container.setAttribute('width', size.x); 12800 container.setAttribute('height', size.y); 12801 } 12802 12803 // movement: update container viewBox so that we don't have to change coordinates of individual layers 12804 setPosition(container, b.min); 12805 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' ')); 12806 12807 this.fire('update'); 12808 }, 12809 12810 // methods below are called by vector layers implementations 12811 12812 _initPath: function (layer) { 12813 var path = layer._path = create('path'); 12814 12815 // @namespace Path 12816 // @option className: String = null 12817 // Custom class name set on an element. Only for SVG renderer. 12818 if (layer.options.className) { 12819 addClass(path, layer.options.className); 12820 } 12821 12822 if (layer.options.interactive) { 12823 addClass(path, 'leaflet-interactive'); 12824 } 12825 12826 this._updateStyle(layer); 12827 this._layers[stamp(layer)] = layer; 12828 }, 12829 12830 _addPath: function (layer) { 12831 if (!this._rootGroup) { this._initContainer(); } 12832 this._rootGroup.appendChild(layer._path); 12833 layer.addInteractiveTarget(layer._path); 12834 }, 12835 12836 _removePath: function (layer) { 12837 remove(layer._path); 12838 layer.removeInteractiveTarget(layer._path); 12839 delete this._layers[stamp(layer)]; 12840 }, 12841 12842 _updatePath: function (layer) { 12843 layer._project(); 12844 layer._update(); 12845 }, 12846 12847 _updateStyle: function (layer) { 12848 var path = layer._path, 12849 options = layer.options; 12850 12851 if (!path) { return; } 12852 12853 if (options.stroke) { 12854 path.setAttribute('stroke', options.color); 12855 path.setAttribute('stroke-opacity', options.opacity); 12856 path.setAttribute('stroke-width', options.weight); 12857 path.setAttribute('stroke-linecap', options.lineCap); 12858 path.setAttribute('stroke-linejoin', options.lineJoin); 12859 12860 if (options.dashArray) { 12861 path.setAttribute('stroke-dasharray', options.dashArray); 12862 } else { 12863 path.removeAttribute('stroke-dasharray'); 12864 } 12865 12866 if (options.dashOffset) { 12867 path.setAttribute('stroke-dashoffset', options.dashOffset); 12868 } else { 12869 path.removeAttribute('stroke-dashoffset'); 12870 } 12871 } else { 12872 path.setAttribute('stroke', 'none'); 12873 } 12874 12875 if (options.fill) { 12876 path.setAttribute('fill', options.fillColor || options.color); 12877 path.setAttribute('fill-opacity', options.fillOpacity); 12878 path.setAttribute('fill-rule', options.fillRule || 'evenodd'); 12879 } else { 12880 path.setAttribute('fill', 'none'); 12881 } 12882 }, 12883 12884 _updatePoly: function (layer, closed) { 12885 this._setPath(layer, pointsToPath(layer._parts, closed)); 12886 }, 12887 12888 _updateCircle: function (layer) { 12889 var p = layer._point, 12890 r = Math.max(Math.round(layer._radius), 1), 12891 r2 = Math.max(Math.round(layer._radiusY), 1) || r, 12892 arc = 'a' + r + ',' + r2 + ' 0 1,0 '; 12893 12894 // drawing a circle with two half-arcs 12895 var d = layer._empty() ? 'M0 0' : 12896 'M' + (p.x - r) + ',' + p.y + 12897 arc + (r * 2) + ',0 ' + 12898 arc + (-r * 2) + ',0 '; 12899 12900 this._setPath(layer, d); 12901 }, 12902 12903 _setPath: function (layer, path) { 12904 layer._path.setAttribute('d', path); 12905 }, 12906 12907 // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements 12908 _bringToFront: function (layer) { 12909 toFront(layer._path); 12910 }, 12911 12912 _bringToBack: function (layer) { 12913 toBack(layer._path); 12914 } 12915 }); 12916 12917 if (vml) { 12918 SVG.include(vmlMixin); 12919 } 12920 12921 // @namespace SVG 12922 // @factory L.svg(options?: Renderer options) 12923 // Creates a SVG renderer with the given options. 12924 function svg(options) { 12925 return svg$1 ||
vendor: 6,797 bytes, lines 12925-13170
12925vml ? new SVG(options) : null; 12926 } 12927 12928 Map.include({ 12929 // @namespace Map; @method getRenderer(layer: Path): Renderer 12930 // Returns the instance of `Renderer` that should be used to render the given 12931 // `Path`. It will ensure that the `renderer` options of the map and paths 12932 // are respected, and that the renderers do exist on the map. 12933 getRenderer: function (layer) { 12934 // @namespace Path; @option renderer: Renderer 12935 // Use this specific instance of `Renderer` for this path. Takes 12936 // precedence over the map's [default renderer](#map-renderer). 12937 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer; 12938 12939 if (!renderer) { 12940 renderer = this._renderer = this._createRenderer(); 12941 } 12942 12943 if (!this.hasLayer(renderer)) { 12944 this.addLayer(renderer); 12945 } 12946 return renderer; 12947 }, 12948 12949 _getPaneRenderer: function (name) { 12950 if (name === 'overlayPane' || name === undefined) { 12951 return false; 12952 } 12953 12954 var renderer = this._paneRenderers[name]; 12955 if (renderer === undefined) { 12956 renderer = this._createRenderer({pane: name}); 12957 this._paneRenderers[name] = renderer; 12958 } 12959 return renderer; 12960 }, 12961 12962 _createRenderer: function (options) { 12963 // @namespace Map; @option preferCanvas: Boolean = false 12964 // Whether `Path`s should be rendered on a `Canvas` renderer. 12965 // By default, all `Path`s are rendered in a `SVG` renderer. 12966 return (this.options.preferCanvas && canvas(options)) || svg(options); 12967 } 12968 }); 12969 12970 /* 12971 * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object. 12972 */ 12973 12974 /* 12975 * @class Rectangle 12976 * @aka L.Rectangle 12977 * @inherits Polygon 12978 * 12979 * A class for drawing rectangle overlays on a map. Extends `Polygon`. 12980 * 12981 * @example 12982 * 12983 * ```js 12984 * // define rectangle geographical bounds 12985 * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]]; 12986 * 12987 * // create an orange rectangle 12988 * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map); 12989 * 12990 * // zoom the map to the rectangle bounds 12991 * map.fitBounds(bounds); 12992 * ``` 12993 * 12994 */ 12995 12996 12997 var Rectangle = Polygon.extend({ 12998 initialize: function (latLngBounds, options) { 12999 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options); 13000 }, 13001 13002 // @method setBounds(latLngBounds: LatLngBounds): this 13003 // Redraws the rectangle with the passed bounds. 13004 setBounds: function (latLngBounds) { 13005 return this.setLatLngs(this._boundsToLatLngs(latLngBounds)); 13006 }, 13007 13008 _boundsToLatLngs: function (latLngBounds) { 13009 latLngBounds = toLatLngBounds(latLngBounds); 13010 return [ 13011 latLngBounds.getSouthWest(), 13012 latLngBounds.getNorthWest(), 13013 latLngBounds.getNorthEast(), 13014 latLngBounds.getSouthEast() 13015 ]; 13016 } 13017 }); 13018 13019 13020 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options) 13021 function rectangle(latLngBounds, options) { 13022 return new Rectangle(latLngBounds, options); 13023 } 13024 13025 SVG.create = create; 13026 SVG.pointsToPath = pointsToPath; 13027 13028 GeoJSON.geometryToLayer = geometryToLayer; 13029 GeoJSON.coordsToLatLng = coordsToLatLng; 13030 GeoJSON.coordsToLatLngs = coordsToLatLngs; 13031 GeoJSON.latLngToCoords = latLngToCoords; 13032 GeoJSON.latLngsToCoords = latLngsToCoords; 13033 GeoJSON.getFeature = getFeature; 13034 GeoJSON.asFeature = asFeature; 13035 13036 /* 13037 * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map 13038 * (zoom to a selected bounding box), enabled by default. 13039 */ 13040 13041 // @namespace Map 13042 // @section Interaction Options 13043 Map.mergeOptions({ 13044 // @option boxZoom: Boolean = true 13045 // Whether the map can be zoomed to a rectangular area specified by 13046 // dragging the mouse while pressing the shift key. 13047 boxZoom: true 13048 }); 13049 13050 var BoxZoom = Handler.extend({ 13051 initialize: function (map) { 13052 this._map = map; 13053 this._container = map._container; 13054 this._pane = map._panes.overlayPane; 13055 this._resetStateTimeout = 0; 13056 map.on('unload', this._destroy, this); 13057 }, 13058 13059 addHooks: function () { 13060 on(this._container, 'mousedown', this._onMouseDown, this); 13061 }, 13062 13063 removeHooks: function () { 13064 off(this._container, 'mousedown', this._onMouseDown, this); 13065 }, 13066 13067 moved: function () { 13068 return this._moved; 13069 }, 13070 13071 _destroy: function () { 13072 remove(this._pane); 13073 delete this._pane; 13074 }, 13075 13076 _resetState: function () { 13077 this._resetStateTimeout = 0; 13078 this._moved = false; 13079 }, 13080 13081 _clearDeferredResetState: function () { 13082 if (this._resetStateTimeout !== 0) { 13083 clearTimeout(this._resetStateTimeout); 13084 this._resetStateTimeout = 0; 13085 } 13086 }, 13087 13088 _onMouseDown: function (e) { 13089 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; } 13090 13091 // Clear the deferred resetState if it hasn't executed yet, otherwise it 13092 // will interrupt the interaction and orphan a box element in the container. 13093 this._clearDeferredResetState(); 13094 this._resetState(); 13095 13096 disableTextSelection(); 13097 disableImageDrag(); 13098 13099 this._startPoint = this._map.mouseEventToContainerPoint(e); 13100 13101 on(document, { 13102 contextmenu: stop, 13103 mousemove: this._onMouseMove, 13104 mouseup: this._onMouseUp, 13105 keydown: this._onKeyDown 13106 }, this); 13107 }, 13108 13109 _onMouseMove: function (e) { 13110 if (!this._moved) { 13111 this._moved = true; 13112 13113 this._box = create$1('div', 'leaflet-zoom-box', this._container); 13114 addClass(this._container, 'leaflet-crosshair'); 13115 13116 this._map.fire('boxzoomstart'); 13117 } 13118 13119 this._point = this._map.mouseEventToContainerPoint(e); 13120 13121 var bounds = new Bounds(this._point, this._startPoint), 13122 size = bounds.getSize(); 13123 13124 setPosition(this._box, bounds.min); 13125 13126 this._box.style.width = size.x + 'px'; 13127 this._box.style.height = size.y + 'px'; 13128 }, 13129 13130 _finish: function () { 13131 if (this._moved) { 13132 remove(this._box); 13133 removeClass(this._container, 'leaflet-crosshair'); 13134 } 13135 13136 enableTextSelection(); 13137 enableImageDrag(); 13138 13139 off(document, { 13140 contextmenu: stop, 13141 mousemove: this._onMouseMove, 13142 mouseup: this._onMouseUp, 13143 keydown: this._onKeyDown 13144 }, this); 13145 }, 13146 13147 _onMouseUp: function (e) { 13148 if ((e.which !== 1) && (e.button !== 1)) { return; } 13149 13150 this._finish(); 13151 13152 if (!this._moved) { return; } 13153 // Postpone to next JS tick so internal click event handling 13154 // still see it as "moved". 13155 this._clearDeferredResetState(); 13156 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0); 13157 13158 var bounds = new LatLngBounds( 13159 this._map.containerPointToLatLng(this._startPoint), 13160 this._map.containerPointToLatLng(this._point)); 13161 13162 this._map 13163 .fitBounds(bounds) 13164 .fire('boxzoomend', {boxZoomBounds: bounds}); 13165 }, 13166 13167 _onKeyDown: function (e) { 13168 if (e.keyCode === 27) { 13169 this._finish(); 13170
vendor: 17,310 bytes, lines 13170-13775
13170} 13171 } 13172 }); 13173 13174 // @section Handlers 13175 // @property boxZoom: Handler 13176 // Box (shift-drag with mouse) zoom handler. 13177 Map.addInitHook('addHandler', 'boxZoom', BoxZoom); 13178 13179 /* 13180 * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default. 13181 */ 13182 13183 // @namespace Map 13184 // @section Interaction Options 13185 13186 Map.mergeOptions({ 13187 // @option doubleClickZoom: Boolean|String = true 13188 // Whether the map can be zoomed in by double clicking on it and 13189 // zoomed out by double clicking while holding shift. If passed 13190 // `'center'`, double-click zoom will zoom to the center of the 13191 // view regardless of where the mouse was. 13192 doubleClickZoom: true 13193 }); 13194 13195 var DoubleClickZoom = Handler.extend({ 13196 addHooks: function () { 13197 this._map.on('dblclick', this._onDoubleClick, this); 13198 }, 13199 13200 removeHooks: function () { 13201 this._map.off('dblclick', this._onDoubleClick, this); 13202 }, 13203 13204 _onDoubleClick: function (e) { 13205 var map = this._map, 13206 oldZoom = map.getZoom(), 13207 delta = map.options.zoomDelta, 13208 zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta; 13209 13210 if (map.options.doubleClickZoom === 'center') { 13211 map.setZoom(zoom); 13212 } else { 13213 map.setZoomAround(e.containerPoint, zoom); 13214 } 13215 } 13216 }); 13217 13218 // @section Handlers 13219 // 13220 // Map properties include interaction handlers that allow you to control 13221 // interaction behavior in runtime, enabling or disabling certain features such 13222 // as dragging or touch zoom (see `Handler` methods). For example: 13223 // 13224 // ```js 13225 // map.doubleClickZoom.disable(); 13226 // ``` 13227 // 13228 // @property doubleClickZoom: Handler 13229 // Double click zoom handler. 13230 Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom); 13231 13232 /* 13233 * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default. 13234 */ 13235 13236 // @namespace Map 13237 // @section Interaction Options 13238 Map.mergeOptions({ 13239 // @option dragging: Boolean = true 13240 // Whether the map be draggable with mouse/touch or not. 13241 dragging: true, 13242 13243 // @section Panning Inertia Options 13244 // @option inertia: Boolean = * 13245 // If enabled, panning of the map will have an inertia effect where 13246 // the map builds momentum while dragging and continues moving in 13247 // the same direction for some time. Feels especially nice on touch 13248 // devices. Enabled by default unless running on old Android devices. 13249 inertia: !android23, 13250 13251 // @option inertiaDeceleration: Number = 3000 13252 // The rate with which the inertial movement slows down, in pixels/second². 13253 inertiaDeceleration: 3400, // px/s^2 13254 13255 // @option inertiaMaxSpeed: Number = Infinity 13256 // Max speed of the inertial movement, in pixels/second. 13257 inertiaMaxSpeed: Infinity, // px/s 13258 13259 // @option easeLinearity: Number = 0.2 13260 easeLinearity: 0.2, 13261 13262 // TODO refactor, move to CRS 13263 // @option worldCopyJump: Boolean = false 13264 // With this option enabled, the map tracks when you pan to another "copy" 13265 // of the world and seamlessly jumps to the original one so that all overlays 13266 // like markers and vector layers are still visible. 13267 worldCopyJump: false, 13268 13269 // @option maxBoundsViscosity: Number = 0.0 13270 // If `maxBounds` is set, this option will control how solid the bounds 13271 // are when dragging the map around. The default value of `0.0` allows the 13272 // user to drag outside the bounds at normal speed, higher values will 13273 // slow down map dragging outside bounds, and `1.0` makes the bounds fully 13274 // solid, preventing the user from dragging outside the bounds. 13275 maxBoundsViscosity: 0.0 13276 }); 13277 13278 var Drag = Handler.extend({ 13279 addHooks: function () { 13280 if (!this._draggable) { 13281 var map = this._map; 13282 13283 this._draggable = new Draggable(map._mapPane, map._container); 13284 13285 this._draggable.on({ 13286 dragstart: this._onDragStart, 13287 drag: this._onDrag, 13288 dragend: this._onDragEnd 13289 }, this); 13290 13291 this._draggable.on('predrag', this._onPreDragLimit, this); 13292 if (map.options.worldCopyJump) { 13293 this._draggable.on('predrag', this._onPreDragWrap, this); 13294 map.on('zoomend', this._onZoomEnd, this); 13295 13296 map.whenReady(this._onZoomEnd, this); 13297 } 13298 } 13299 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag'); 13300 this._draggable.enable(); 13301 this._positions = []; 13302 this._times = []; 13303 }, 13304 13305 removeHooks: function () { 13306 removeClass(this._map._container, 'leaflet-grab'); 13307 removeClass(this._map._container, 'leaflet-touch-drag'); 13308 this._draggable.disable(); 13309 }, 13310 13311 moved: function () { 13312 return this._draggable && this._draggable._moved; 13313 }, 13314 13315 moving: function () { 13316 return this._draggable && this._draggable._moving; 13317 }, 13318 13319 _onDragStart: function () { 13320 var map = this._map; 13321 13322 map._stop(); 13323 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) { 13324 var bounds = toLatLngBounds(this._map.options.maxBounds); 13325 13326 this._offsetLimit = toBounds( 13327 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1), 13328 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1) 13329 .add(this._map.getSize())); 13330 13331 this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity)); 13332 } else { 13333 this._offsetLimit = null; 13334 } 13335 13336 map 13337 .fire('movestart') 13338 .fire('dragstart'); 13339 13340 if (map.options.inertia) { 13341 this._positions = []; 13342 this._times = []; 13343 } 13344 }, 13345 13346 _onDrag: function (e) { 13347 if (this._map.options.inertia) { 13348 var time = this._lastTime = +new Date(), 13349 pos = this._lastPos = this._draggable._absPos || this._draggable._newPos; 13350 13351 this._positions.push(pos); 13352 this._times.push(time); 13353 13354 this._prunePositions(time); 13355 } 13356 13357 this._map 13358 .fire('move', e) 13359 .fire('drag', e); 13360 }, 13361 13362 _prunePositions: function (time) { 13363 while (this._positions.length > 1 && time - this._times[0] > 50) { 13364 this._positions.shift(); 13365 this._times.shift(); 13366 } 13367 }, 13368 13369 _onZoomEnd: function () { 13370 var pxCenter = this._map.getSize().divideBy(2), 13371 pxWorldCenter = this._map.latLngToLayerPoint([0, 0]); 13372 13373 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x; 13374 this._worldWidth = this._map.getPixelWorldBounds().getSize().x; 13375 }, 13376 13377 _viscousLimit: function (value, threshold) { 13378 return value - (value - threshold) * this._viscosity; 13379 }, 13380 13381 _onPreDragLimit: function () { 13382 if (!this._viscosity || !this._offsetLimit) { return; } 13383 13384 var offset = this._draggable._newPos.subtract(this._draggable._startPos); 13385 13386 var limit = this._offsetLimit; 13387 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); } 13388 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); } 13389 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); } 13390 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); } 13391 13392 this._draggable._newPos = this._draggable._startPos.add(offset); 13393 }, 13394 13395 _onPreDragWrap: function () { 13396 // TODO refactor to be able to adjust map pane position after zoom 13397 var worldWidth = this._worldWidth, 13398 halfWidth = Math.round(worldWidth / 2), 13399 dx = this._initialWorldOffset, 13400 x = this._draggable._newPos.x, 13401 newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx, 13402 newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx, 13403 newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2; 13404 13405 this._draggable._absPos = this._draggable._newPos.clone(); 13406 this._draggable._newPos.x = newX; 13407 }, 13408 13409 _onDragEnd: function (e) { 13410 var map = this._map, 13411 options = map.options, 13412 13413 noInertia = !options.inertia || this._times.length < 2; 13414 13415 map.fire('dragend', e); 13416 13417 if (noInertia) { 13418 map.fire('moveend'); 13419 13420 } else { 13421 this._prunePositions(+new Date()); 13422 13423 var direction = this._lastPos.subtract(this._positions[0]), 13424 duration = (this._lastTime - this._times[0]) / 1000, 13425 ease = options.easeLinearity, 13426 13427 speedVector = direction.multiplyBy(ease / duration), 13428 speed = speedVector.distanceTo([0, 0]), 13429 13430 limitedSpeed = Math.min(options.inertiaMaxSpeed, speed), 13431 limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed), 13432 13433 decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease), 13434 offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round(); 13435 13436 if (!offset.x && !offset.y) { 13437 map.fire('moveend'); 13438 13439 } else { 13440 offset = map._limitOffset(offset, map.options.maxBounds); 13441 13442 requestAnimFrame(function () { 13443 map.panBy(offset, { 13444 duration: decelerationDuration, 13445 easeLinearity: ease, 13446 noMoveStart: true, 13447 animate: true 13448 }); 13449 }); 13450 } 13451 } 13452 } 13453 }); 13454 13455 // @section Handlers 13456 // @property dragging: Handler 13457 // Map dragging handler (by both mouse and touch). 13458 Map.addInitHook('addHandler', 'dragging', Drag); 13459 13460 /* 13461 * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default. 13462 */ 13463 13464 // @namespace Map 13465 // @section Keyboard Navigation Options 13466 Map.mergeOptions({ 13467 // @option keyboard: Boolean = true 13468 // Makes the map focusable and allows users to navigate the map with keyboard 13469 // arrows and `+`/`-` keys. 13470 keyboard: true, 13471 13472 // @option keyboardPanDelta: Number = 80 13473 // Amount of pixels to pan when pressing an arrow key. 13474 keyboardPanDelta: 80 13475 }); 13476 13477 var Keyboard = Handler.extend({ 13478 13479 keyCodes: { 13480 left: [37], 13481 right: [39], 13482 down: [40], 13483 up: [38], 13484 zoomIn: [187, 107, 61, 171], 13485 zoomOut: [189, 109, 54, 173] 13486 }, 13487 13488 initialize: function (map) { 13489 this._map = map; 13490 13491 this._setPanDelta(map.options.keyboardPanDelta); 13492 this._setZoomDelta(map.options.zoomDelta); 13493 }, 13494 13495 addHooks: function () { 13496 var container = this._map._container; 13497 13498 // make the container focusable by tabbing 13499 if (container.tabIndex <= 0) { 13500 container.tabIndex = '0'; 13501 } 13502 13503 on(container, { 13504 focus: this._onFocus, 13505 blur: this._onBlur, 13506 mousedown: this._onMouseDown 13507 }, this); 13508 13509 this._map.on({ 13510 focus: this._addHooks, 13511 blur: this._removeHooks 13512 }, this); 13513 }, 13514 13515 removeHooks: function () { 13516 this._removeHooks(); 13517 13518 off(this._map._container, { 13519 focus: this._onFocus, 13520 blur: this._onBlur, 13521 mousedown: this._onMouseDown 13522 }, this); 13523 13524 this._map.off({ 13525 focus: this._addHooks, 13526 blur: this._removeHooks 13527 }, this); 13528 }, 13529 13530 _onMouseDown: function () { 13531 if (this._focused) { return; } 13532 13533 var body = document.body, 13534 docEl = document.documentElement, 13535 top = body.scrollTop || docEl.scrollTop, 13536 left = body.scrollLeft || docEl.scrollLeft; 13537 13538 this._map._container.focus(); 13539 13540 window.scrollTo(left, top); 13541 }, 13542 13543 _onFocus: function () { 13544 this._focused = true; 13545 this._map.fire('focus'); 13546 }, 13547 13548 _onBlur: function () { 13549 this._focused = false; 13550 this._map.fire('blur'); 13551 }, 13552 13553 _setPanDelta: function (panDelta) { 13554 var keys = this._panKeys = {}, 13555 codes = this.keyCodes, 13556 i, len; 13557 13558 for (i = 0, len = codes.left.length; i < len; i++) { 13559 keys[codes.left[i]] = [-1 * panDelta, 0]; 13560 } 13561 for (i = 0, len = codes.right.length; i < len; i++) { 13562 keys[codes.right[i]] = [panDelta, 0]; 13563 } 13564 for (i = 0, len = codes.down.length; i < len; i++) { 13565 keys[codes.down[i]] = [0, panDelta]; 13566 } 13567 for (i = 0, len = codes.up.length; i < len; i++) { 13568 keys[codes.up[i]] = [0, -1 * panDelta]; 13569 } 13570 }, 13571 13572 _setZoomDelta: function (zoomDelta) { 13573 var keys = this._zoomKeys = {}, 13574 codes = this.keyCodes, 13575 i, len; 13576 13577 for (i = 0, len = codes.zoomIn.length; i < len; i++) { 13578 keys[codes.zoomIn[i]] = zoomDelta; 13579 } 13580 for (i = 0, len = codes.zoomOut.length; i < len; i++) { 13581 keys[codes.zoomOut[i]] = -zoomDelta; 13582 } 13583 }, 13584 13585 _addHooks: function () { 13586 on(document, 'keydown', this._onKeyDown, this); 13587 }, 13588 13589 _removeHooks: function () { 13590 off(document, 'keydown', this._onKeyDown, this); 13591 }, 13592 13593 _onKeyDown: function (e) { 13594 if (e.altKey || e.ctrlKey || e.metaKey) { return; } 13595 13596 var key = e.keyCode, 13597 map = this._map, 13598 offset; 13599 13600 if (key in this._panKeys) { 13601 if (!map._panAnim || !map._panAnim._inProgress) { 13602 offset = this._panKeys[key]; 13603 if (e.shiftKey) { 13604 offset = toPoint(offset).multiplyBy(3); 13605 } 13606 13607 map.panBy(offset); 13608 13609 if (map.options.maxBounds) { 13610 map.panInsideBounds(map.options.maxBounds); 13611 } 13612 } 13613 } else if (key in this._zoomKeys) { 13614 map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]); 13615 13616 } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) { 13617 map.closePopup(); 13618 13619 } else { 13620 return; 13621 } 13622 13623 stop(e); 13624 } 13625 }); 13626 13627 // @section Handlers 13628 // @section Handlers 13629 // @property keyboard: Handler 13630 // Keyboard navigation handler. 13631 Map.addInitHook('addHandler', 'keyboard', Keyboard); 13632 13633 /* 13634 * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map. 13635 */ 13636 13637 // @namespace Map 13638 // @section Interaction Options 13639 Map.mergeOptions({ 13640 // @section Mouse wheel options 13641 // @option scrollWheelZoom: Boolean|String = true 13642 // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`, 13643 // it will zoom to the center of the view regardless of where the mouse was. 13644 scrollWheelZoom: true, 13645 13646 // @option wheelDebounceTime: Number = 40 13647 // Limits the rate at which a wheel can fire (in milliseconds). By default 13648 // user can't zoom via wheel more often than once per 40 ms. 13649 wheelDebounceTime: 40, 13650 13651 // @option wheelPxPerZoomLevel: Number = 60 13652 // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta)) 13653 // mean a change of one full zoom level. Smaller values will make wheel-zooming 13654 // faster (and vice versa). 13655 wheelPxPerZoomLevel: 60 13656 }); 13657 13658 var ScrollWheelZoom = Handler.extend({ 13659 addHooks: function () { 13660 on(this._map._container, 'wheel', this._onWheelScroll, this); 13661 13662 this._delta = 0; 13663 }, 13664 13665 removeHooks: function () { 13666 off(this._map._container, 'wheel', this._onWheelScroll, this); 13667 }, 13668 13669 _onWheelScroll: function (e) { 13670 var delta = getWheelDelta(e); 13671 13672 var debounce = this._map.options.wheelDebounceTime; 13673 13674 this._delta += delta; 13675 this._lastMousePos = this._map.mouseEventToContainerPoint(e); 13676 13677 if (!this._startTime) { 13678 this._startTime = +new Date(); 13679 } 13680 13681 var left = Math.max(debounce - (+new Date() - this._startTime), 0); 13682 13683 clearTimeout(this._timer); 13684 this._timer = setTimeout(bind(this._performZoom, this), left); 13685 13686 stop(e); 13687 }, 13688 13689 _performZoom: function () { 13690 var map = this._map, 13691 zoom = map.getZoom(), 13692 snap = this._map.options.zoomSnap || 0; 13693 13694 map._stop(); // stop panning and fly animations if any 13695 13696 // map the delta with a sigmoid function to -4..4 range leaning on -1..1 13697 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4), 13698 d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2, 13699 d4 = snap ? Math.ceil(d3 / snap) * snap : d3, 13700 delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom; 13701 13702 this._delta = 0; 13703 this._startTime = null; 13704 13705 if (!delta) { return; } 13706 13707 if (map.options.scrollWheelZoom === 'center') { 13708 map.setZoom(zoom + delta); 13709 } else { 13710 map.setZoomAround(this._lastMousePos, zoom + delta); 13711 } 13712 } 13713 }); 13714 13715 // @section Handlers 13716 // @property scrollWheelZoom: Handler 13717 // Scroll wheel zoom handler. 13718 Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom); 13719 13720 /* 13721 * L.Map.Tap is used to enable mobile hacks like quick taps and long hold. 13722 */ 13723 13724 // @namespace Map 13725 // @section Interaction Options 13726 Map.mergeOptions({ 13727 // @section Touch interaction options 13728 // @option tap: Boolean = true 13729 // Enables mobile hacks for supporting instant taps (fixing 200ms click 13730 // delay on iOS/Android) and touch holds (fired as `contextmenu` events). 13731 tap: true, 13732 13733 // @option tapTolerance: Number = 15 13734 // The max number of pixels a user can shift his finger during touch 13735 // for it to be considered a valid tap. 13736 tapTolerance: 15 13737 }); 13738 13739 var Tap = Handler.extend({ 13740 addHooks: function () { 13741 on(this._map._container, 'touchstart', this._onDown, this); 13742 }, 13743 13744 removeHooks: function () { 13745 off(this._map._container, 'touchstart', this._onDown, this); 13746 }, 13747 13748 _onDown: function (e) { 13749 if (!e.touches) { return; } 13750 13751 preventDefault(e); 13752 13753 this._fireClick = true; 13754 13755 // don't simulate click or track longpress if more than 1 touch 13756 if (e.touches.length > 1) { 13757 this._fireClick = false; 13758 clearTimeout(this._holdTimeout); 13759 return; 13760 } 13761 13762 var first = e.touches[0], 13763 el = first.target; 13764 13765 this._startPos = this._newPos = new Point(first.clientX, first.clientY); 13766 13767 // if touching a link, highlight it 13768 if (el.tagName && el.tagName.toLowerCase() === 'a') { 13769 addClass(el, 'leaflet-active'); 13770 } 13771 13772 // simulate long hold but setting a timeout 13773 this._holdTimeout = setTimeout(bind(function () { 13774 if (this._isTapValid()) { 13775 this._fireClick = false;
13776 this._onUp(); 13777 this._simulateEvent('contextmenu', first); 13778 } 13779 }, this), 1000); 13780 13781 this._simulateEvent('mousedown', first); 13782 13783 on(document, { 13784 touchmove: this._onMove, 13785 touchend: this._onUp 13786 }, this); 13787 }, 13788 13789 _onUp: function (e) { 13790 clearTimeout(this._holdTimeout); 13791 13792 off(document, { 13793 touchmove: this._onMove, 13794 touchend: this._onUp 13795 }, this); 13796 13797 if (this._fireClick && e && e.changedTouches) { 13798 13799 var first = e.changedTouches[0], 13800 el = first.target; 13801 13802 if (el && el.tagName && el.tagName.toLowerCase() === 'a') { 13803 removeClass(el, 'leaflet-active'); 13804 } 13805 13806 this._simulateEvent('mouseup', first); 13807 13808 // simulate click if the touch didn't move too much 13809 if (this._isTapValid()) { 13810 this._simulateEvent('click', first); 13811 } 13812 } 13813 }, 13814 13815 _isTapValid: function () { 13816 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance; 13817 }, 13818 13819 _onMove: function (e) { 13820 var first = e.touches[0]; 13821 this._newPos = new Point(first.clientX, first.clientY); 13822 this._simulateEvent('mousemove', first); 13823 }, 13824 13825 _simulateEvent: function (type, e) { 13826 var simulatedEvent = document.createEvent('MouseEvents'); 13827 13828 simulatedEvent._simulated = true; 13829 e.target._simulatedClick = true; 13830 13831 simulatedEvent.initMouseEvent( 13832 type, true, true, window, 1, 13833 e.screenX, e.screenY, 13834 e.clientX, e.clientY, 13835 false, false, false, false, 0, null); 13836 13837 e.target.dispatchEvent(simulatedEvent); 13838 } 13839 }); 13840 13841 // @section Handlers 13842 // @property tap: Handler 13843 // Mobile touch hacks (quick tap and touch hold) handler. 13844 if (touch && (!pointer || safari)) { 13845 Map.addInitHook('addHandler', 'tap', Tap); 13846 } 13847 13848 /* 13849 * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers. 13850 */ 13851 13852 // @namespace Map 13853 // @section Interaction Options 13854 Map.mergeOptions({ 13855 // @section Touch interaction options 13856 // @option touchZoom: Boolean|String = * 13857 // Whether the map can be zoomed by touch-dragging with two fingers. If 13858 // passed `'center'`, it will zoom to the center of the view regardless of 13859 // where the touch events (fingers) were. Enabled for touch-capable web 13860 // browsers except for old Androids. 13861 touchZoom: touch && !android23, 13862 13863 // @option bounceAtZoomLimits: Boolean = true 13864 // Set it to false if you don't want the map to zoom beyond min/max zoom 13865 // and then bounce back when pinch-zooming. 13866 bounceAtZoomLimits: true 13867 }); 13868 13869 var TouchZoom = Handler.extend({ 13870 addHooks: function () { 13871 addClass(this._map._container, 'leaflet-touch-zoom'); 13872 on(this._map._container, 'touchstart', this._onTouchStart, this); 13873 }, 13874 13875 removeHooks: function () { 13876 removeClass(this._map._container, 'leaflet-touch-zoom'); 13877 off(this._map._container, 'touchstart', this._onTouchStart, this); 13878 }, 13879 13880 _onTouchStart: function (e) { 13881 var map = this._map; 13882 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; } 13883 13884 var p1 = map.mouseEventToContainerPoint(e.touches[0]), 13885 p2 = map.mouseEventToContainerPoint(e.touches[1]); 13886 13887 this._centerPoint = map.getSize()._divideBy(2); 13888 this._startLatLng = map.containerPointToLatLng(this._centerPoint); 13889 if (map.options.touchZoom !== 'center') { 13890 this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2)); 13891 } 13892 13893 this._startDist = p1.distanceTo(p2); 13894 this._startZoom = map.getZoom(); 13895 13896 this._moved = false; 13897 this._zooming = true; 13898 13899 map._stop(); 13900 13901 on(document, 'touchmove', this._onTouchMove, this); 13902 on(document, 'touchend', this._onTouchEnd, this); 13903 13904 preventDefault(e); 13905 }, 13906 13907 _onTouchMove: function (e) { 13908 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; } 13909 13910 var map = this._map, 13911 p1 = map.mouseEventToContainerPoint(e.touches[0]), 13912 p2 = map.mouseEventToContainerPoint(e.touches[1]), 13913 scale = p1.distanceTo(p2) / this._startDist; 13914 13915 this._zoom = map.getScaleZoom(scale, this._startZoom); 13916 13917 if (!map.options.bounceAtZoomLimits && ( 13918 (this._zoom < map.getMinZoom() && scale < 1) || 13919 (this._zoom > map.getMaxZoom() && scale > 1))) { 13920 this._zoom = map._limitZoom(this._zoom); 13921 } 13922 13923 if (map.options.touchZoom === 'center') { 13924 this._center = this._startLatLng; 13925 if (scale === 1) { return; } 13926 } else { 13927 // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng 13928 var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint); 13929 if (scale === 1 && delta.x === 0 && delta.y === 0) { return; } 13930 this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom); 13931 } 13932 13933 if (!this._moved) { 13934 map._moveStart(true, false); 13935 this._moved = true; 13936 } 13937
vendor: 3,815 bytes, lines 13938-14069
13938 cancelAnimFrame(this._animRequest); 13939 13940 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false}); 13941 this._animRequest = requestAnimFrame(moveFn, this, true); 13942 13943 preventDefault(e); 13944 }, 13945 13946 _onTouchEnd: function () { 13947 if (!this._moved || !this._zooming) { 13948 this._zooming = false; 13949 return; 13950 } 13951 13952 this._zooming = false; 13953 cancelAnimFrame(this._animRequest); 13954 13955 off(document, 'touchmove', this._onTouchMove, this); 13956 off(document, 'touchend', this._onTouchEnd, this); 13957 13958 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate. 13959 if (this._map.options.zoomAnimation) { 13960 this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap); 13961 } else { 13962 this._map._resetView(this._center, this._map._limitZoom(this._zoom)); 13963 } 13964 } 13965 }); 13966 13967 // @section Handlers 13968 // @property touchZoom: Handler 13969 // Touch zoom handler. 13970 Map.addInitHook('addHandler', 'touchZoom', TouchZoom); 13971 13972 Map.BoxZoom = BoxZoom; 13973 Map.DoubleClickZoom = DoubleClickZoom; 13974 Map.Drag = Drag; 13975 Map.Keyboard = Keyboard; 13976 Map.ScrollWheelZoom = ScrollWheelZoom; 13977 Map.Tap = Tap; 13978 Map.TouchZoom = TouchZoom; 13979 13980 exports.Bounds = Bounds; 13981 exports.Browser = Browser; 13982 exports.CRS = CRS; 13983 exports.Canvas = Canvas; 13984 exports.Circle = Circle; 13985 exports.CircleMarker = CircleMarker; 13986 exports.Class = Class; 13987 exports.Control = Control; 13988 exports.DivIcon = DivIcon; 13989 exports.DivOverlay = DivOverlay; 13990 exports.DomEvent = DomEvent; 13991 exports.DomUtil = DomUtil; 13992 exports.Draggable = Draggable; 13993 exports.Evented = Evented; 13994 exports.FeatureGroup = FeatureGroup; 13995 exports.GeoJSON = GeoJSON; 13996 exports.GridLayer = GridLayer; 13997 exports.Handler = Handler; 13998 exports.Icon = Icon; 13999 exports.ImageOverlay = ImageOverlay; 14000 exports.LatLng = LatLng; 14001 exports.LatLngBounds = LatLngBounds; 14002 exports.Layer = Layer; 14003 exports.LayerGroup = LayerGroup; 14004 exports.LineUtil = LineUtil; 14005 exports.Map = Map; 14006 exports.Marker = Marker; 14007 exports.Mixin = Mixin; 14008 exports.Path = Path; 14009 exports.Point = Point; 14010 exports.PolyUtil = PolyUtil; 14011 exports.Polygon = Polygon; 14012 exports.Polyline = Polyline; 14013 exports.Popup = Popup; 14014 exports.PosAnimation = PosAnimation; 14015 exports.Projection = index; 14016 exports.Rectangle = Rectangle; 14017 exports.Renderer = Renderer; 14018 exports.SVG = SVG; 14019 exports.SVGOverlay = SVGOverlay; 14020 exports.TileLayer = TileLayer; 14021 exports.Tooltip = Tooltip; 14022 exports.Transformation = Transformation; 14023 exports.Util = Util; 14024 exports.VideoOverlay = VideoOverlay; 14025 exports.bind = bind; 14026 exports.bounds = toBounds; 14027 exports.canvas = canvas; 14028 exports.circle = circle; 14029 exports.circleMarker = circleMarker; 14030 exports.control = control; 14031 exports.divIcon = divIcon; 14032 exports.extend = extend; 14033 exports.featureGroup = featureGroup; 14034 exports.geoJSON = geoJSON; 14035 exports.geoJson = geoJson; 14036 exports.gridLayer = gridLayer; 14037 exports.icon = icon; 14038 exports.imageOverlay = imageOverlay; 14039 exports.latLng = toLatLng; 14040 exports.latLngBounds = toLatLngBounds; 14041 exports.layerGroup = layerGroup; 14042 exports.map = createMap; 14043 exports.marker = marker; 14044 exports.point = toPoint; 14045 exports.polygon = polygon; 14046 exports.polyline = polyline; 14047 exports.popup = popup; 14048 exports.rectangle = rectangle; 14049 exports.setOptions = setOptions; 14050 exports.stamp = stamp; 14051 exports.svg = svg; 14052 exports.svgOverlay = svgOverlay; 14053 exports.tileLayer = tileLayer; 14054 exports.tooltip = tooltip; 14055 exports.transformation = toTransformation; 14056 exports.version = version; 14057 exports.videoOverlay = videoOverlay; 14058 14059 var oldL = window.L; 14060 exports.noConflict = function() { 14061 window.L = oldL; 14062 return this; 14063 } 14064 14065 // Always export us to window global (see #2364) 14066 window.L = exports; 14067 14068})); 14069//# 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.