1 | import * as Util from '../core/Util';
|
2 | import {Evented} from '../core/Events';
|
3 | import {EPSG3857} from '../geo/crs/CRS.EPSG3857';
|
4 | import {Point, toPoint} from '../geometry/Point';
|
5 | import {Bounds, toBounds} from '../geometry/Bounds';
|
6 | import {LatLng, toLatLng} from '../geo/LatLng';
|
7 | import {LatLngBounds, toLatLngBounds} from '../geo/LatLngBounds';
|
8 | import Browser from '../core/Browser';
|
9 | import * as DomEvent from '../dom/DomEvent';
|
10 | import * as DomUtil from '../dom/DomUtil';
|
11 | import {PosAnimation} from '../dom/PosAnimation';
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | export var Map = Evented.extend({
|
33 |
|
34 | options: {
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | crs: EPSG3857,
|
40 |
|
41 |
|
42 |
|
43 | center: undefined,
|
44 |
|
45 |
|
46 |
|
47 | zoom: undefined,
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 | minZoom: undefined,
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 | maxZoom: undefined,
|
60 |
|
61 |
|
62 |
|
63 | layers: [],
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 | maxBounds: undefined,
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | renderer: undefined,
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | zoomAnimation: true,
|
83 |
|
84 |
|
85 |
|
86 | zoomAnimationThreshold: 4,
|
87 |
|
88 |
|
89 |
|
90 |
|
91 | fadeAnimation: true,
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 | markerZoomAnimation: true,
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 | transform3DLimit: 8388608,
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 | zoomSnap: 1,
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 | zoomDelta: 1,
|
120 |
|
121 |
|
122 |
|
123 | trackResize: true
|
124 | },
|
125 |
|
126 | initialize: function (id, options) {
|
127 | options = Util.setOptions(this, options);
|
128 |
|
129 |
|
130 |
|
131 | this._handlers = [];
|
132 | this._layers = {};
|
133 | this._zoomBoundLayers = {};
|
134 | this._sizeChanged = true;
|
135 |
|
136 | this._initContainer(id);
|
137 | this._initLayout();
|
138 |
|
139 |
|
140 | this._onResize = Util.bind(this._onResize, this);
|
141 |
|
142 | this._initEvents();
|
143 |
|
144 | if (options.maxBounds) {
|
145 | this.setMaxBounds(options.maxBounds);
|
146 | }
|
147 |
|
148 | if (options.zoom !== undefined) {
|
149 | this._zoom = this._limitZoom(options.zoom);
|
150 | }
|
151 |
|
152 | if (options.center && options.zoom !== undefined) {
|
153 | this.setView(toLatLng(options.center), options.zoom, {reset: true});
|
154 | }
|
155 |
|
156 | this.callInitHooks();
|
157 |
|
158 |
|
159 | this._zoomAnimated = DomUtil.TRANSITION && Browser.any3d && !Browser.mobileOpera &&
|
160 | this.options.zoomAnimation;
|
161 |
|
162 |
|
163 |
|
164 | if (this._zoomAnimated) {
|
165 | this._createAnimProxy();
|
166 | DomEvent.on(this._proxy, DomUtil.TRANSITION_END, this._catchTransitionEnd, this);
|
167 | }
|
168 |
|
169 | this._addLayers(this.options.layers);
|
170 | },
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 | setView: function (center, zoom, options) {
|
179 |
|
180 | zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
|
181 | center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
|
182 | options = options || {};
|
183 |
|
184 | this._stop();
|
185 |
|
186 | if (this._loaded && !options.reset && options !== true) {
|
187 |
|
188 | if (options.animate !== undefined) {
|
189 | options.zoom = Util.extend({animate: options.animate}, options.zoom);
|
190 | options.pan = Util.extend({animate: options.animate, duration: options.duration}, options.pan);
|
191 | }
|
192 |
|
193 |
|
194 | var moved = (this._zoom !== zoom) ?
|
195 | this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
|
196 | this._tryAnimatedPan(center, options.pan);
|
197 |
|
198 | if (moved) {
|
199 |
|
200 | clearTimeout(this._sizeTimer);
|
201 | return this;
|
202 | }
|
203 | }
|
204 |
|
205 |
|
206 | this._resetView(center, zoom, options.pan && options.pan.noMoveStart);
|
207 |
|
208 | return this;
|
209 | },
|
210 |
|
211 |
|
212 |
|
213 | setZoom: function (zoom, options) {
|
214 | if (!this._loaded) {
|
215 | this._zoom = zoom;
|
216 | return this;
|
217 | }
|
218 | return this.setView(this.getCenter(), zoom, {zoom: options});
|
219 | },
|
220 |
|
221 |
|
222 |
|
223 | zoomIn: function (delta, options) {
|
224 | delta = delta || (Browser.any3d ? this.options.zoomDelta : 1);
|
225 | return this.setZoom(this._zoom + delta, options);
|
226 | },
|
227 |
|
228 |
|
229 |
|
230 | zoomOut: function (delta, options) {
|
231 | delta = delta || (Browser.any3d ? this.options.zoomDelta : 1);
|
232 | return this.setZoom(this._zoom - delta, options);
|
233 | },
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 |
|
241 | setZoomAround: function (latlng, zoom, options) {
|
242 | var scale = this.getZoomScale(zoom),
|
243 | viewHalf = this.getSize().divideBy(2),
|
244 | containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
|
245 |
|
246 | centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
|
247 | newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
|
248 |
|
249 | return this.setView(newCenter, zoom, {zoom: options});
|
250 | },
|
251 |
|
252 | _getBoundsCenterZoom: function (bounds, options) {
|
253 |
|
254 | options = options || {};
|
255 | bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
|
256 |
|
257 | var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
|
258 | paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
|
259 |
|
260 | zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
|
261 |
|
262 | zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
|
263 |
|
264 | if (zoom === Infinity) {
|
265 | return {
|
266 | center: bounds.getCenter(),
|
267 | zoom: zoom
|
268 | };
|
269 | }
|
270 |
|
271 | var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
|
272 |
|
273 | swPoint = this.project(bounds.getSouthWest(), zoom),
|
274 | nePoint = this.project(bounds.getNorthEast(), zoom),
|
275 | center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
|
276 |
|
277 | return {
|
278 | center: center,
|
279 | zoom: zoom
|
280 | };
|
281 | },
|
282 |
|
283 |
|
284 |
|
285 |
|
286 | fitBounds: function (bounds, options) {
|
287 |
|
288 | bounds = toLatLngBounds(bounds);
|
289 |
|
290 | if (!bounds.isValid()) {
|
291 | throw new Error('Bounds are not valid.');
|
292 | }
|
293 |
|
294 | var target = this._getBoundsCenterZoom(bounds, options);
|
295 | return this.setView(target.center, target.zoom, options);
|
296 | },
|
297 |
|
298 |
|
299 |
|
300 |
|
301 | fitWorld: function (options) {
|
302 | return this.fitBounds([[-90, -180], [90, 180]], options);
|
303 | },
|
304 |
|
305 |
|
306 |
|
307 | panTo: function (center, options) {
|
308 | return this.setView(center, this._zoom, {pan: options});
|
309 | },
|
310 |
|
311 |
|
312 |
|
313 | panBy: function (offset, options) {
|
314 | offset = toPoint(offset).round();
|
315 | options = options || {};
|
316 |
|
317 | if (!offset.x && !offset.y) {
|
318 | return this.fire('moveend');
|
319 | }
|
320 |
|
321 |
|
322 | if (options.animate !== true && !this.getSize().contains(offset)) {
|
323 | this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
|
324 | return this;
|
325 | }
|
326 |
|
327 | if (!this._panAnim) {
|
328 | this._panAnim = new PosAnimation();
|
329 |
|
330 | this._panAnim.on({
|
331 | 'step': this._onPanTransitionStep,
|
332 | 'end': this._onPanTransitionEnd
|
333 | }, this);
|
334 | }
|
335 |
|
336 |
|
337 | if (!options.noMoveStart) {
|
338 | this.fire('movestart');
|
339 | }
|
340 |
|
341 |
|
342 | if (options.animate !== false) {
|
343 | DomUtil.addClass(this._mapPane, 'leaflet-pan-anim');
|
344 |
|
345 | var newPos = this._getMapPanePos().subtract(offset).round();
|
346 | this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
|
347 | } else {
|
348 | this._rawPanBy(offset);
|
349 | this.fire('move').fire('moveend');
|
350 | }
|
351 |
|
352 | return this;
|
353 | },
|
354 |
|
355 |
|
356 |
|
357 |
|
358 | flyTo: function (targetCenter, targetZoom, options) {
|
359 |
|
360 | options = options || {};
|
361 | if (options.animate === false || !Browser.any3d) {
|
362 | return this.setView(targetCenter, targetZoom, options);
|
363 | }
|
364 |
|
365 | this._stop();
|
366 |
|
367 | var from = this.project(this.getCenter()),
|
368 | to = this.project(targetCenter),
|
369 | size = this.getSize(),
|
370 | startZoom = this._zoom;
|
371 |
|
372 | targetCenter = toLatLng(targetCenter);
|
373 | targetZoom = targetZoom === undefined ? startZoom : targetZoom;
|
374 |
|
375 | var w0 = Math.max(size.x, size.y),
|
376 | w1 = w0 * this.getZoomScale(startZoom, targetZoom),
|
377 | u1 = (to.distanceTo(from)) || 1,
|
378 | rho = 1.42,
|
379 | rho2 = rho * rho;
|
380 |
|
381 | function r(i) {
|
382 | var s1 = i ? -1 : 1,
|
383 | s2 = i ? w1 : w0,
|
384 | t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
|
385 | b1 = 2 * s2 * rho2 * u1,
|
386 | b = t1 / b1,
|
387 | sq = Math.sqrt(b * b + 1) - b;
|
388 |
|
389 |
|
390 |
|
391 | var log = sq < 0.000000001 ? -18 : Math.log(sq);
|
392 |
|
393 | return log;
|
394 | }
|
395 |
|
396 | function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
|
397 | function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
|
398 | function tanh(n) { return sinh(n) / cosh(n); }
|
399 |
|
400 | var r0 = r(0);
|
401 |
|
402 | function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
|
403 | function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
|
404 |
|
405 | function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
|
406 |
|
407 | var start = Date.now(),
|
408 | S = (r(1) - r0) / rho,
|
409 | duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
|
410 |
|
411 | function frame() {
|
412 | var t = (Date.now() - start) / duration,
|
413 | s = easeOut(t) * S;
|
414 |
|
415 | if (t <= 1) {
|
416 | this._flyToFrame = Util.requestAnimFrame(frame, this);
|
417 |
|
418 | this._move(
|
419 | this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
|
420 | this.getScaleZoom(w0 / w(s), startZoom),
|
421 | {flyTo: true});
|
422 |
|
423 | } else {
|
424 | this
|
425 | ._move(targetCenter, targetZoom)
|
426 | ._moveEnd(true);
|
427 | }
|
428 | }
|
429 |
|
430 | this._moveStart(true, options.noMoveStart);
|
431 |
|
432 | frame.call(this);
|
433 | return this;
|
434 | },
|
435 |
|
436 |
|
437 |
|
438 |
|
439 | flyToBounds: function (bounds, options) {
|
440 | var target = this._getBoundsCenterZoom(bounds, options);
|
441 | return this.flyTo(target.center, target.zoom, options);
|
442 | },
|
443 |
|
444 |
|
445 |
|
446 | setMaxBounds: function (bounds) {
|
447 | bounds = toLatLngBounds(bounds);
|
448 |
|
449 | if (this.listens('moveend', this._panInsideMaxBounds)) {
|
450 | this.off('moveend', this._panInsideMaxBounds);
|
451 | }
|
452 |
|
453 | if (!bounds.isValid()) {
|
454 | this.options.maxBounds = null;
|
455 | return this;
|
456 | }
|
457 |
|
458 | this.options.maxBounds = bounds;
|
459 |
|
460 | if (this._loaded) {
|
461 | this._panInsideMaxBounds();
|
462 | }
|
463 |
|
464 | return this.on('moveend', this._panInsideMaxBounds);
|
465 | },
|
466 |
|
467 |
|
468 |
|
469 | setMinZoom: function (zoom) {
|
470 | var oldZoom = this.options.minZoom;
|
471 | this.options.minZoom = zoom;
|
472 |
|
473 | if (this._loaded && oldZoom !== zoom) {
|
474 | this.fire('zoomlevelschange');
|
475 |
|
476 | if (this.getZoom() < this.options.minZoom) {
|
477 | return this.setZoom(zoom);
|
478 | }
|
479 | }
|
480 |
|
481 | return this;
|
482 | },
|
483 |
|
484 |
|
485 |
|
486 | setMaxZoom: function (zoom) {
|
487 | var oldZoom = this.options.maxZoom;
|
488 | this.options.maxZoom = zoom;
|
489 |
|
490 | if (this._loaded && oldZoom !== zoom) {
|
491 | this.fire('zoomlevelschange');
|
492 |
|
493 | if (this.getZoom() > this.options.maxZoom) {
|
494 | return this.setZoom(zoom);
|
495 | }
|
496 | }
|
497 |
|
498 | return this;
|
499 | },
|
500 |
|
501 |
|
502 |
|
503 | panInsideBounds: function (bounds, options) {
|
504 | this._enforcingBounds = true;
|
505 | var center = this.getCenter(),
|
506 | newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
|
507 |
|
508 | if (!center.equals(newCenter)) {
|
509 | this.panTo(newCenter, options);
|
510 | }
|
511 |
|
512 | this._enforcingBounds = false;
|
513 | return this;
|
514 | },
|
515 |
|
516 |
|
517 |
|
518 |
|
519 |
|
520 |
|
521 | panInside: function (latlng, options) {
|
522 | options = options || {};
|
523 |
|
524 | var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
|
525 | paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
|
526 | pixelCenter = this.project(this.getCenter()),
|
527 | pixelPoint = this.project(latlng),
|
528 | pixelBounds = this.getPixelBounds(),
|
529 | paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]),
|
530 | paddedSize = paddedBounds.getSize();
|
531 |
|
532 | if (!paddedBounds.contains(pixelPoint)) {
|
533 | this._enforcingBounds = true;
|
534 | var centerOffset = pixelPoint.subtract(paddedBounds.getCenter());
|
535 | var offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize);
|
536 | pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x;
|
537 | pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y;
|
538 | this.panTo(this.unproject(pixelCenter), options);
|
539 | this._enforcingBounds = false;
|
540 | }
|
541 | return this;
|
542 | },
|
543 |
|
544 |
|
545 |
|
546 |
|
547 |
|
548 |
|
549 |
|
550 |
|
551 |
|
552 |
|
553 |
|
554 |
|
555 |
|
556 |
|
557 | invalidateSize: function (options) {
|
558 | if (!this._loaded) { return this; }
|
559 |
|
560 | options = Util.extend({
|
561 | animate: false,
|
562 | pan: true
|
563 | }, options === true ? {animate: true} : options);
|
564 |
|
565 | var oldSize = this.getSize();
|
566 | this._sizeChanged = true;
|
567 | this._lastCenter = null;
|
568 |
|
569 | var newSize = this.getSize(),
|
570 | oldCenter = oldSize.divideBy(2).round(),
|
571 | newCenter = newSize.divideBy(2).round(),
|
572 | offset = oldCenter.subtract(newCenter);
|
573 |
|
574 | if (!offset.x && !offset.y) { return this; }
|
575 |
|
576 | if (options.animate && options.pan) {
|
577 | this.panBy(offset);
|
578 |
|
579 | } else {
|
580 | if (options.pan) {
|
581 | this._rawPanBy(offset);
|
582 | }
|
583 |
|
584 | this.fire('move');
|
585 |
|
586 | if (options.debounceMoveend) {
|
587 | clearTimeout(this._sizeTimer);
|
588 | this._sizeTimer = setTimeout(Util.bind(this.fire, this, 'moveend'), 200);
|
589 | } else {
|
590 | this.fire('moveend');
|
591 | }
|
592 | }
|
593 |
|
594 |
|
595 |
|
596 |
|
597 | return this.fire('resize', {
|
598 | oldSize: oldSize,
|
599 | newSize: newSize
|
600 | });
|
601 | },
|
602 |
|
603 |
|
604 |
|
605 |
|
606 | stop: function () {
|
607 | this.setZoom(this._limitZoom(this._zoom));
|
608 | if (!this.options.zoomSnap) {
|
609 | this.fire('viewreset');
|
610 | }
|
611 | return this._stop();
|
612 | },
|
613 |
|
614 |
|
615 |
|
616 |
|
617 |
|
618 |
|
619 |
|
620 |
|
621 |
|
622 |
|
623 | locate: function (options) {
|
624 |
|
625 | options = this._locateOptions = Util.extend({
|
626 | timeout: 10000,
|
627 | watch: false
|
628 |
|
629 |
|
630 |
|
631 |
|
632 | }, options);
|
633 |
|
634 | if (!('geolocation' in navigator)) {
|
635 | this._handleGeolocationError({
|
636 | code: 0,
|
637 | message: 'Geolocation not supported.'
|
638 | });
|
639 | return this;
|
640 | }
|
641 |
|
642 | var onResponse = Util.bind(this._handleGeolocationResponse, this),
|
643 | onError = Util.bind(this._handleGeolocationError, this);
|
644 |
|
645 | if (options.watch) {
|
646 | this._locationWatchId =
|
647 | navigator.geolocation.watchPosition(onResponse, onError, options);
|
648 | } else {
|
649 | navigator.geolocation.getCurrentPosition(onResponse, onError, options);
|
650 | }
|
651 | return this;
|
652 | },
|
653 |
|
654 |
|
655 |
|
656 |
|
657 |
|
658 | stopLocate: function () {
|
659 | if (navigator.geolocation && navigator.geolocation.clearWatch) {
|
660 | navigator.geolocation.clearWatch(this._locationWatchId);
|
661 | }
|
662 | if (this._locateOptions) {
|
663 | this._locateOptions.setView = false;
|
664 | }
|
665 | return this;
|
666 | },
|
667 |
|
668 | _handleGeolocationError: function (error) {
|
669 | if (!this._container._leaflet_id) { return; }
|
670 |
|
671 | var c = error.code,
|
672 | message = error.message ||
|
673 | (c === 1 ? 'permission denied' :
|
674 | (c === 2 ? 'position unavailable' : 'timeout'));
|
675 |
|
676 | if (this._locateOptions.setView && !this._loaded) {
|
677 | this.fitWorld();
|
678 | }
|
679 |
|
680 |
|
681 |
|
682 |
|
683 | this.fire('locationerror', {
|
684 | code: c,
|
685 | message: 'Geolocation error: ' + message + '.'
|
686 | });
|
687 | },
|
688 |
|
689 | _handleGeolocationResponse: function (pos) {
|
690 | if (!this._container._leaflet_id) { return; }
|
691 |
|
692 | var lat = pos.coords.latitude,
|
693 | lng = pos.coords.longitude,
|
694 | latlng = new LatLng(lat, lng),
|
695 | bounds = latlng.toBounds(pos.coords.accuracy * 2),
|
696 | options = this._locateOptions;
|
697 |
|
698 | if (options.setView) {
|
699 | var zoom = this.getBoundsZoom(bounds);
|
700 | this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
|
701 | }
|
702 |
|
703 | var data = {
|
704 | latlng: latlng,
|
705 | bounds: bounds,
|
706 | timestamp: pos.timestamp
|
707 | };
|
708 |
|
709 | for (var i in pos.coords) {
|
710 | if (typeof pos.coords[i] === 'number') {
|
711 | data[i] = pos.coords[i];
|
712 | }
|
713 | }
|
714 |
|
715 |
|
716 |
|
717 |
|
718 | this.fire('locationfound', data);
|
719 | },
|
720 |
|
721 |
|
722 |
|
723 |
|
724 |
|
725 | addHandler: function (name, HandlerClass) {
|
726 | if (!HandlerClass) { return this; }
|
727 |
|
728 | var handler = this[name] = new HandlerClass(this);
|
729 |
|
730 | this._handlers.push(handler);
|
731 |
|
732 | if (this.options[name]) {
|
733 | handler.enable();
|
734 | }
|
735 |
|
736 | return this;
|
737 | },
|
738 |
|
739 |
|
740 |
|
741 | remove: function () {
|
742 |
|
743 | this._initEvents(true);
|
744 | if (this.options.maxBounds) { this.off('moveend', this._panInsideMaxBounds); }
|
745 |
|
746 | if (this._containerId !== this._container._leaflet_id) {
|
747 | throw new Error('Map container is being reused by another instance');
|
748 | }
|
749 |
|
750 | try {
|
751 |
|
752 | delete this._container._leaflet_id;
|
753 | delete this._containerId;
|
754 | } catch (e) {
|
755 |
|
756 | this._container._leaflet_id = undefined;
|
757 |
|
758 | this._containerId = undefined;
|
759 | }
|
760 |
|
761 | if (this._locationWatchId !== undefined) {
|
762 | this.stopLocate();
|
763 | }
|
764 |
|
765 | this._stop();
|
766 |
|
767 | DomUtil.remove(this._mapPane);
|
768 |
|
769 | if (this._clearControlPos) {
|
770 | this._clearControlPos();
|
771 | }
|
772 | if (this._resizeRequest) {
|
773 | Util.cancelAnimFrame(this._resizeRequest);
|
774 | this._resizeRequest = null;
|
775 | }
|
776 |
|
777 | this._clearHandlers();
|
778 |
|
779 | if (this._loaded) {
|
780 |
|
781 |
|
782 |
|
783 | this.fire('unload');
|
784 | }
|
785 |
|
786 | var i;
|
787 | for (i in this._layers) {
|
788 | this._layers[i].remove();
|
789 | }
|
790 | for (i in this._panes) {
|
791 | DomUtil.remove(this._panes[i]);
|
792 | }
|
793 |
|
794 | this._layers = [];
|
795 | this._panes = [];
|
796 | delete this._mapPane;
|
797 | delete this._renderer;
|
798 |
|
799 | return this;
|
800 | },
|
801 |
|
802 |
|
803 |
|
804 |
|
805 |
|
806 |
|
807 | createPane: function (name, container) {
|
808 | var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
|
809 | pane = DomUtil.create('div', className, container || this._mapPane);
|
810 |
|
811 | if (name) {
|
812 | this._panes[name] = pane;
|
813 | }
|
814 | return pane;
|
815 | },
|
816 |
|
817 |
|
818 |
|
819 |
|
820 |
|
821 | getCenter: function () {
|
822 | this._checkIfLoaded();
|
823 |
|
824 | if (this._lastCenter && !this._moved()) {
|
825 | return this._lastCenter.clone();
|
826 | }
|
827 | return this.layerPointToLatLng(this._getCenterLayerPoint());
|
828 | },
|
829 |
|
830 |
|
831 |
|
832 | getZoom: function () {
|
833 | return this._zoom;
|
834 | },
|
835 |
|
836 |
|
837 |
|
838 | getBounds: function () {
|
839 | var bounds = this.getPixelBounds(),
|
840 | sw = this.unproject(bounds.getBottomLeft()),
|
841 | ne = this.unproject(bounds.getTopRight());
|
842 |
|
843 | return new LatLngBounds(sw, ne);
|
844 | },
|
845 |
|
846 |
|
847 |
|
848 | getMinZoom: function () {
|
849 | return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
|
850 | },
|
851 |
|
852 |
|
853 |
|
854 | getMaxZoom: function () {
|
855 | return this.options.maxZoom === undefined ?
|
856 | (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
|
857 | this.options.maxZoom;
|
858 | },
|
859 |
|
860 |
|
861 |
|
862 |
|
863 |
|
864 |
|
865 | getBoundsZoom: function (bounds, inside, padding) {
|
866 | bounds = toLatLngBounds(bounds);
|
867 | padding = toPoint(padding || [0, 0]);
|
868 |
|
869 | var zoom = this.getZoom() || 0,
|
870 | min = this.getMinZoom(),
|
871 | max = this.getMaxZoom(),
|
872 | nw = bounds.getNorthWest(),
|
873 | se = bounds.getSouthEast(),
|
874 | size = this.getSize().subtract(padding),
|
875 | boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
|
876 | snap = Browser.any3d ? this.options.zoomSnap : 1,
|
877 | scalex = size.x / boundsSize.x,
|
878 | scaley = size.y / boundsSize.y,
|
879 | scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
|
880 |
|
881 | zoom = this.getScaleZoom(scale, zoom);
|
882 |
|
883 | if (snap) {
|
884 | zoom = Math.round(zoom / (snap / 100)) * (snap / 100);
|
885 | zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
|
886 | }
|
887 |
|
888 | return Math.max(min, Math.min(max, zoom));
|
889 | },
|
890 |
|
891 |
|
892 |
|
893 | getSize: function () {
|
894 | if (!this._size || this._sizeChanged) {
|
895 | this._size = new Point(
|
896 | this._container.clientWidth || 0,
|
897 | this._container.clientHeight || 0);
|
898 |
|
899 | this._sizeChanged = false;
|
900 | }
|
901 | return this._size.clone();
|
902 | },
|
903 |
|
904 |
|
905 |
|
906 |
|
907 | getPixelBounds: function (center, zoom) {
|
908 | var topLeftPoint = this._getTopLeftPoint(center, zoom);
|
909 | return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
|
910 | },
|
911 |
|
912 |
|
913 |
|
914 |
|
915 |
|
916 |
|
917 |
|
918 | getPixelOrigin: function () {
|
919 | this._checkIfLoaded();
|
920 | return this._pixelOrigin;
|
921 | },
|
922 |
|
923 |
|
924 |
|
925 |
|
926 | getPixelWorldBounds: function (zoom) {
|
927 | return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
|
928 | },
|
929 |
|
930 |
|
931 |
|
932 |
|
933 |
|
934 | getPane: function (pane) {
|
935 | return typeof pane === 'string' ? this._panes[pane] : pane;
|
936 | },
|
937 |
|
938 |
|
939 |
|
940 |
|
941 | getPanes: function () {
|
942 | return this._panes;
|
943 | },
|
944 |
|
945 |
|
946 |
|
947 | getContainer: function () {
|
948 | return this._container;
|
949 | },
|
950 |
|
951 |
|
952 |
|
953 |
|
954 |
|
955 |
|
956 |
|
957 | getZoomScale: function (toZoom, fromZoom) {
|
958 |
|
959 | var crs = this.options.crs;
|
960 | fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
|
961 | return crs.scale(toZoom) / crs.scale(fromZoom);
|
962 | },
|
963 |
|
964 |
|
965 |
|
966 |
|
967 |
|
968 | getScaleZoom: function (scale, fromZoom) {
|
969 | var crs = this.options.crs;
|
970 | fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
|
971 | var zoom = crs.zoom(scale * crs.scale(fromZoom));
|
972 | return isNaN(zoom) ? Infinity : zoom;
|
973 | },
|
974 |
|
975 |
|
976 |
|
977 |
|
978 |
|
979 |
|
980 | project: function (latlng, zoom) {
|
981 | zoom = zoom === undefined ? this._zoom : zoom;
|
982 | return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
|
983 | },
|
984 |
|
985 |
|
986 |
|
987 | unproject: function (point, zoom) {
|
988 | zoom = zoom === undefined ? this._zoom : zoom;
|
989 | return this.options.crs.pointToLatLng(toPoint(point), zoom);
|
990 | },
|
991 |
|
992 |
|
993 |
|
994 |
|
995 | layerPointToLatLng: function (point) {
|
996 | var projectedPoint = toPoint(point).add(this.getPixelOrigin());
|
997 | return this.unproject(projectedPoint);
|
998 | },
|
999 |
|
1000 |
|
1001 |
|
1002 |
|
1003 | latLngToLayerPoint: function (latlng) {
|
1004 | var projectedPoint = this.project(toLatLng(latlng))._round();
|
1005 | return projectedPoint._subtract(this.getPixelOrigin());
|
1006 | },
|
1007 |
|
1008 |
|
1009 |
|
1010 |
|
1011 |
|
1012 |
|
1013 |
|
1014 | wrapLatLng: function (latlng) {
|
1015 | return this.options.crs.wrapLatLng(toLatLng(latlng));
|
1016 | },
|
1017 |
|
1018 |
|
1019 |
|
1020 |
|
1021 |
|
1022 |
|
1023 |
|
1024 | wrapLatLngBounds: function (latlng) {
|
1025 | return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
|
1026 | },
|
1027 |
|
1028 |
|
1029 |
|
1030 |
|
1031 | distance: function (latlng1, latlng2) {
|
1032 | return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
|
1033 | },
|
1034 |
|
1035 |
|
1036 |
|
1037 |
|
1038 | containerPointToLayerPoint: function (point) {
|
1039 | return toPoint(point).subtract(this._getMapPanePos());
|
1040 | },
|
1041 |
|
1042 |
|
1043 |
|
1044 |
|
1045 | layerPointToContainerPoint: function (point) {
|
1046 | return toPoint(point).add(this._getMapPanePos());
|
1047 | },
|
1048 |
|
1049 |
|
1050 |
|
1051 |
|
1052 | containerPointToLatLng: function (point) {
|
1053 | var layerPoint = this.containerPointToLayerPoint(toPoint(point));
|
1054 | return this.layerPointToLatLng(layerPoint);
|
1055 | },
|
1056 |
|
1057 |
|
1058 |
|
1059 |
|
1060 | latLngToContainerPoint: function (latlng) {
|
1061 | return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
|
1062 | },
|
1063 |
|
1064 |
|
1065 |
|
1066 |
|
1067 | mouseEventToContainerPoint: function (e) {
|
1068 | return DomEvent.getMousePosition(e, this._container);
|
1069 | },
|
1070 |
|
1071 |
|
1072 |
|
1073 |
|
1074 | mouseEventToLayerPoint: function (e) {
|
1075 | return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
|
1076 | },
|
1077 |
|
1078 |
|
1079 |
|
1080 |
|
1081 | mouseEventToLatLng: function (e) {
|
1082 | return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
|
1083 | },
|
1084 |
|
1085 |
|
1086 |
|
1087 |
|
1088 | _initContainer: function (id) {
|
1089 | var container = this._container = DomUtil.get(id);
|
1090 |
|
1091 | if (!container) {
|
1092 | throw new Error('Map container not found.');
|
1093 | } else if (container._leaflet_id) {
|
1094 | throw new Error('Map container is already initialized.');
|
1095 | }
|
1096 |
|
1097 | DomEvent.on(container, 'scroll', this._onScroll, this);
|
1098 | this._containerId = Util.stamp(container);
|
1099 | },
|
1100 |
|
1101 | _initLayout: function () {
|
1102 | var container = this._container;
|
1103 |
|
1104 | this._fadeAnimated = this.options.fadeAnimation && Browser.any3d;
|
1105 |
|
1106 | DomUtil.addClass(container, 'leaflet-container' +
|
1107 | (Browser.touch ? ' leaflet-touch' : '') +
|
1108 | (Browser.retina ? ' leaflet-retina' : '') +
|
1109 | (Browser.ielt9 ? ' leaflet-oldie' : '') +
|
1110 | (Browser.safari ? ' leaflet-safari' : '') +
|
1111 | (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
|
1112 |
|
1113 | var position = DomUtil.getStyle(container, 'position');
|
1114 |
|
1115 | if (position !== 'absolute' && position !== 'relative' && position !== 'fixed' && position !== 'sticky') {
|
1116 | container.style.position = 'relative';
|
1117 | }
|
1118 |
|
1119 | this._initPanes();
|
1120 |
|
1121 | if (this._initControlPos) {
|
1122 | this._initControlPos();
|
1123 | }
|
1124 | },
|
1125 |
|
1126 | _initPanes: function () {
|
1127 | var panes = this._panes = {};
|
1128 | this._paneRenderers = {};
|
1129 |
|
1130 |
|
1131 |
|
1132 |
|
1133 |
|
1134 |
|
1135 |
|
1136 |
|
1137 |
|
1138 |
|
1139 |
|
1140 |
|
1141 |
|
1142 | this._mapPane = this.createPane('mapPane', this._container);
|
1143 | DomUtil.setPosition(this._mapPane, new Point(0, 0));
|
1144 |
|
1145 |
|
1146 |
|
1147 | this.createPane('tilePane');
|
1148 |
|
1149 |
|
1150 | this.createPane('overlayPane');
|
1151 |
|
1152 |
|
1153 | this.createPane('shadowPane');
|
1154 |
|
1155 |
|
1156 | this.createPane('markerPane');
|
1157 |
|
1158 |
|
1159 | this.createPane('tooltipPane');
|
1160 |
|
1161 |
|
1162 | this.createPane('popupPane');
|
1163 |
|
1164 | if (!this.options.markerZoomAnimation) {
|
1165 | DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide');
|
1166 | DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide');
|
1167 | }
|
1168 | },
|
1169 |
|
1170 |
|
1171 |
|
1172 |
|
1173 |
|
1174 | _resetView: function (center, zoom, noMoveStart) {
|
1175 | DomUtil.setPosition(this._mapPane, new Point(0, 0));
|
1176 |
|
1177 | var loading = !this._loaded;
|
1178 | this._loaded = true;
|
1179 | zoom = this._limitZoom(zoom);
|
1180 |
|
1181 | this.fire('viewprereset');
|
1182 |
|
1183 | var zoomChanged = this._zoom !== zoom;
|
1184 | this
|
1185 | ._moveStart(zoomChanged, noMoveStart)
|
1186 | ._move(center, zoom)
|
1187 | ._moveEnd(zoomChanged);
|
1188 |
|
1189 |
|
1190 |
|
1191 |
|
1192 | this.fire('viewreset');
|
1193 |
|
1194 |
|
1195 |
|
1196 |
|
1197 | if (loading) {
|
1198 | this.fire('load');
|
1199 | }
|
1200 | },
|
1201 |
|
1202 | _moveStart: function (zoomChanged, noMoveStart) {
|
1203 |
|
1204 |
|
1205 |
|
1206 |
|
1207 | if (zoomChanged) {
|
1208 | this.fire('zoomstart');
|
1209 | }
|
1210 | if (!noMoveStart) {
|
1211 | this.fire('movestart');
|
1212 | }
|
1213 | return this;
|
1214 | },
|
1215 |
|
1216 | _move: function (center, zoom, data, supressEvent) {
|
1217 | if (zoom === undefined) {
|
1218 | zoom = this._zoom;
|
1219 | }
|
1220 | var zoomChanged = this._zoom !== zoom;
|
1221 |
|
1222 | this._zoom = zoom;
|
1223 | this._lastCenter = center;
|
1224 | this._pixelOrigin = this._getNewPixelOrigin(center);
|
1225 |
|
1226 | if (!supressEvent) {
|
1227 |
|
1228 |
|
1229 |
|
1230 | if (zoomChanged || (data && data.pinch)) {
|
1231 | this.fire('zoom', data);
|
1232 | }
|
1233 |
|
1234 |
|
1235 |
|
1236 |
|
1237 | this.fire('move', data);
|
1238 | } else if (data && data.pinch) {
|
1239 | this.fire('zoom', data);
|
1240 | }
|
1241 | return this;
|
1242 | },
|
1243 |
|
1244 | _moveEnd: function (zoomChanged) {
|
1245 |
|
1246 |
|
1247 | if (zoomChanged) {
|
1248 | this.fire('zoomend');
|
1249 | }
|
1250 |
|
1251 |
|
1252 |
|
1253 |
|
1254 | return this.fire('moveend');
|
1255 | },
|
1256 |
|
1257 | _stop: function () {
|
1258 | Util.cancelAnimFrame(this._flyToFrame);
|
1259 | if (this._panAnim) {
|
1260 | this._panAnim.stop();
|
1261 | }
|
1262 | return this;
|
1263 | },
|
1264 |
|
1265 | _rawPanBy: function (offset) {
|
1266 | DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
|
1267 | },
|
1268 |
|
1269 | _getZoomSpan: function () {
|
1270 | return this.getMaxZoom() - this.getMinZoom();
|
1271 | },
|
1272 |
|
1273 | _panInsideMaxBounds: function () {
|
1274 | if (!this._enforcingBounds) {
|
1275 | this.panInsideBounds(this.options.maxBounds);
|
1276 | }
|
1277 | },
|
1278 |
|
1279 | _checkIfLoaded: function () {
|
1280 | if (!this._loaded) {
|
1281 | throw new Error('Set map center and zoom first.');
|
1282 | }
|
1283 | },
|
1284 |
|
1285 |
|
1286 |
|
1287 |
|
1288 | _initEvents: function (remove) {
|
1289 | this._targets = {};
|
1290 | this._targets[Util.stamp(this._container)] = this;
|
1291 |
|
1292 | var onOff = remove ? DomEvent.off : DomEvent.on;
|
1293 |
|
1294 |
|
1295 |
|
1296 |
|
1297 |
|
1298 |
|
1299 |
|
1300 |
|
1301 |
|
1302 |
|
1303 |
|
1304 |
|
1305 |
|
1306 |
|
1307 |
|
1308 |
|
1309 |
|
1310 |
|
1311 |
|
1312 |
|
1313 |
|
1314 |
|
1315 |
|
1316 |
|
1317 |
|
1318 |
|
1319 |
|
1320 |
|
1321 | onOff(this._container, 'click dblclick mousedown mouseup ' +
|
1322 | 'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this);
|
1323 |
|
1324 | if (this.options.trackResize) {
|
1325 | onOff(window, 'resize', this._onResize, this);
|
1326 | }
|
1327 |
|
1328 | if (Browser.any3d && this.options.transform3DLimit) {
|
1329 | (remove ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
|
1330 | }
|
1331 | },
|
1332 |
|
1333 | _onResize: function () {
|
1334 | Util.cancelAnimFrame(this._resizeRequest);
|
1335 | this._resizeRequest = Util.requestAnimFrame(
|
1336 | function () { this.invalidateSize({debounceMoveend: true}); }, this);
|
1337 | },
|
1338 |
|
1339 | _onScroll: function () {
|
1340 | this._container.scrollTop = 0;
|
1341 | this._container.scrollLeft = 0;
|
1342 | },
|
1343 |
|
1344 | _onMoveEnd: function () {
|
1345 | var pos = this._getMapPanePos();
|
1346 | if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
|
1347 |
|
1348 |
|
1349 | this._resetView(this.getCenter(), this.getZoom());
|
1350 | }
|
1351 | },
|
1352 |
|
1353 | _findEventTargets: function (e, type) {
|
1354 | var targets = [],
|
1355 | target,
|
1356 | isHover = type === 'mouseout' || type === 'mouseover',
|
1357 | src = e.target || e.srcElement,
|
1358 | dragging = false;
|
1359 |
|
1360 | while (src) {
|
1361 | target = this._targets[Util.stamp(src)];
|
1362 | if (target && (type === 'click' || type === 'preclick') && this._draggableMoved(target)) {
|
1363 |
|
1364 | dragging = true;
|
1365 | break;
|
1366 | }
|
1367 | if (target && target.listens(type, true)) {
|
1368 | if (isHover && !DomEvent.isExternalTarget(src, e)) { break; }
|
1369 | targets.push(target);
|
1370 | if (isHover) { break; }
|
1371 | }
|
1372 | if (src === this._container) { break; }
|
1373 | src = src.parentNode;
|
1374 | }
|
1375 | if (!targets.length && !dragging && !isHover && this.listens(type, true)) {
|
1376 | targets = [this];
|
1377 | }
|
1378 | return targets;
|
1379 | },
|
1380 |
|
1381 | _isClickDisabled: function (el) {
|
1382 | while (el && el !== this._container) {
|
1383 | if (el['_leaflet_disable_click']) { return true; }
|
1384 | el = el.parentNode;
|
1385 | }
|
1386 | },
|
1387 |
|
1388 | _handleDOMEvent: function (e) {
|
1389 | var el = (e.target || e.srcElement);
|
1390 | if (!this._loaded || el['_leaflet_disable_events'] || e.type === 'click' && this._isClickDisabled(el)) {
|
1391 | return;
|
1392 | }
|
1393 |
|
1394 | var type = e.type;
|
1395 |
|
1396 | if (type === 'mousedown') {
|
1397 |
|
1398 | DomUtil.preventOutline(el);
|
1399 | }
|
1400 |
|
1401 | this._fireDOMEvent(e, type);
|
1402 | },
|
1403 |
|
1404 | _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
|
1405 |
|
1406 | _fireDOMEvent: function (e, type, canvasTargets) {
|
1407 |
|
1408 | if (e.type === 'click') {
|
1409 |
|
1410 |
|
1411 |
|
1412 |
|
1413 |
|
1414 | var synth = Util.extend({}, e);
|
1415 | synth.type = 'preclick';
|
1416 | this._fireDOMEvent(synth, synth.type, canvasTargets);
|
1417 | }
|
1418 |
|
1419 |
|
1420 | var targets = this._findEventTargets(e, type);
|
1421 |
|
1422 | if (canvasTargets) {
|
1423 | var filtered = [];
|
1424 | for (var i = 0; i < canvasTargets.length; i++) {
|
1425 | if (canvasTargets[i].listens(type, true)) {
|
1426 | filtered.push(canvasTargets[i]);
|
1427 | }
|
1428 | }
|
1429 | targets = filtered.concat(targets);
|
1430 | }
|
1431 |
|
1432 | if (!targets.length) { return; }
|
1433 |
|
1434 | if (type === 'contextmenu') {
|
1435 | DomEvent.preventDefault(e);
|
1436 | }
|
1437 |
|
1438 | var target = targets[0];
|
1439 | var data = {
|
1440 | originalEvent: e
|
1441 | };
|
1442 |
|
1443 | if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') {
|
1444 | var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
|
1445 | data.containerPoint = isMarker ?
|
1446 | this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
|
1447 | data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
|
1448 | data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
|
1449 | }
|
1450 |
|
1451 | for (i = 0; i < targets.length; i++) {
|
1452 | targets[i].fire(type, data, true);
|
1453 | if (data.originalEvent._stopped ||
|
1454 | (targets[i].options.bubblingMouseEvents === false && Util.indexOf(this._mouseEvents, type) !== -1)) { return; }
|
1455 | }
|
1456 | },
|
1457 |
|
1458 | _draggableMoved: function (obj) {
|
1459 | obj = obj.dragging && obj.dragging.enabled() ? obj : this;
|
1460 | return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
|
1461 | },
|
1462 |
|
1463 | _clearHandlers: function () {
|
1464 | for (var i = 0, len = this._handlers.length; i < len; i++) {
|
1465 | this._handlers[i].disable();
|
1466 | }
|
1467 | },
|
1468 |
|
1469 |
|
1470 |
|
1471 |
|
1472 |
|
1473 |
|
1474 |
|
1475 | whenReady: function (callback, context) {
|
1476 | if (this._loaded) {
|
1477 | callback.call(context || this, {target: this});
|
1478 | } else {
|
1479 | this.on('load', callback, context);
|
1480 | }
|
1481 | return this;
|
1482 | },
|
1483 |
|
1484 |
|
1485 |
|
1486 |
|
1487 | _getMapPanePos: function () {
|
1488 | return DomUtil.getPosition(this._mapPane) || new Point(0, 0);
|
1489 | },
|
1490 |
|
1491 | _moved: function () {
|
1492 | var pos = this._getMapPanePos();
|
1493 | return pos && !pos.equals([0, 0]);
|
1494 | },
|
1495 |
|
1496 | _getTopLeftPoint: function (center, zoom) {
|
1497 | var pixelOrigin = center && zoom !== undefined ?
|
1498 | this._getNewPixelOrigin(center, zoom) :
|
1499 | this.getPixelOrigin();
|
1500 | return pixelOrigin.subtract(this._getMapPanePos());
|
1501 | },
|
1502 |
|
1503 | _getNewPixelOrigin: function (center, zoom) {
|
1504 | var viewHalf = this.getSize()._divideBy(2);
|
1505 | return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
|
1506 | },
|
1507 |
|
1508 | _latLngToNewLayerPoint: function (latlng, zoom, center) {
|
1509 | var topLeft = this._getNewPixelOrigin(center, zoom);
|
1510 | return this.project(latlng, zoom)._subtract(topLeft);
|
1511 | },
|
1512 |
|
1513 | _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
|
1514 | var topLeft = this._getNewPixelOrigin(center, zoom);
|
1515 | return toBounds([
|
1516 | this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
|
1517 | this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
|
1518 | this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
|
1519 | this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
|
1520 | ]);
|
1521 | },
|
1522 |
|
1523 |
|
1524 | _getCenterLayerPoint: function () {
|
1525 | return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
|
1526 | },
|
1527 |
|
1528 |
|
1529 | _getCenterOffset: function (latlng) {
|
1530 | return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
|
1531 | },
|
1532 |
|
1533 |
|
1534 | _limitCenter: function (center, zoom, bounds) {
|
1535 |
|
1536 | if (!bounds) { return center; }
|
1537 |
|
1538 | var centerPoint = this.project(center, zoom),
|
1539 | viewHalf = this.getSize().divideBy(2),
|
1540 | viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
|
1541 | offset = this._getBoundsOffset(viewBounds, bounds, zoom);
|
1542 |
|
1543 |
|
1544 |
|
1545 |
|
1546 | if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
|
1547 | return center;
|
1548 | }
|
1549 |
|
1550 | return this.unproject(centerPoint.add(offset), zoom);
|
1551 | },
|
1552 |
|
1553 |
|
1554 | _limitOffset: function (offset, bounds) {
|
1555 | if (!bounds) { return offset; }
|
1556 |
|
1557 | var viewBounds = this.getPixelBounds(),
|
1558 | newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
|
1559 |
|
1560 | return offset.add(this._getBoundsOffset(newBounds, bounds));
|
1561 | },
|
1562 |
|
1563 |
|
1564 | _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
|
1565 | var projectedMaxBounds = toBounds(
|
1566 | this.project(maxBounds.getNorthEast(), zoom),
|
1567 | this.project(maxBounds.getSouthWest(), zoom)
|
1568 | ),
|
1569 | minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
|
1570 | maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
|
1571 |
|
1572 | dx = this._rebound(minOffset.x, -maxOffset.x),
|
1573 | dy = this._rebound(minOffset.y, -maxOffset.y);
|
1574 |
|
1575 | return new Point(dx, dy);
|
1576 | },
|
1577 |
|
1578 | _rebound: function (left, right) {
|
1579 | return left + right > 0 ?
|
1580 | Math.round(left - right) / 2 :
|
1581 | Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
|
1582 | },
|
1583 |
|
1584 | _limitZoom: function (zoom) {
|
1585 | var min = this.getMinZoom(),
|
1586 | max = this.getMaxZoom(),
|
1587 | snap = Browser.any3d ? this.options.zoomSnap : 1;
|
1588 | if (snap) {
|
1589 | zoom = Math.round(zoom / snap) * snap;
|
1590 | }
|
1591 | return Math.max(min, Math.min(max, zoom));
|
1592 | },
|
1593 |
|
1594 | _onPanTransitionStep: function () {
|
1595 | this.fire('move');
|
1596 | },
|
1597 |
|
1598 | _onPanTransitionEnd: function () {
|
1599 | DomUtil.removeClass(this._mapPane, 'leaflet-pan-anim');
|
1600 | this.fire('moveend');
|
1601 | },
|
1602 |
|
1603 | _tryAnimatedPan: function (center, options) {
|
1604 |
|
1605 | var offset = this._getCenterOffset(center)._trunc();
|
1606 |
|
1607 |
|
1608 | if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
|
1609 |
|
1610 | this.panBy(offset, options);
|
1611 |
|
1612 | return true;
|
1613 | },
|
1614 |
|
1615 | _createAnimProxy: function () {
|
1616 |
|
1617 | var proxy = this._proxy = DomUtil.create('div', 'leaflet-proxy leaflet-zoom-animated');
|
1618 | this._panes.mapPane.appendChild(proxy);
|
1619 |
|
1620 | this.on('zoomanim', function (e) {
|
1621 | var prop = DomUtil.TRANSFORM,
|
1622 | transform = this._proxy.style[prop];
|
1623 |
|
1624 | DomUtil.setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
|
1625 |
|
1626 |
|
1627 | if (transform === this._proxy.style[prop] && this._animatingZoom) {
|
1628 | this._onZoomTransitionEnd();
|
1629 | }
|
1630 | }, this);
|
1631 |
|
1632 | this.on('load moveend', this._animMoveEnd, this);
|
1633 |
|
1634 | this._on('unload', this._destroyAnimProxy, this);
|
1635 | },
|
1636 |
|
1637 | _destroyAnimProxy: function () {
|
1638 | DomUtil.remove(this._proxy);
|
1639 | this.off('load moveend', this._animMoveEnd, this);
|
1640 | delete this._proxy;
|
1641 | },
|
1642 |
|
1643 | _animMoveEnd: function () {
|
1644 | var c = this.getCenter(),
|
1645 | z = this.getZoom();
|
1646 | DomUtil.setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
|
1647 | },
|
1648 |
|
1649 | _catchTransitionEnd: function (e) {
|
1650 | if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
|
1651 | this._onZoomTransitionEnd();
|
1652 | }
|
1653 | },
|
1654 |
|
1655 | _nothingToAnimate: function () {
|
1656 | return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
|
1657 | },
|
1658 |
|
1659 | _tryAnimatedZoom: function (center, zoom, options) {
|
1660 |
|
1661 | if (this._animatingZoom) { return true; }
|
1662 |
|
1663 | options = options || {};
|
1664 |
|
1665 |
|
1666 | if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
|
1667 | Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
|
1668 |
|
1669 |
|
1670 | var scale = this.getZoomScale(zoom),
|
1671 | offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
|
1672 |
|
1673 |
|
1674 | if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
|
1675 |
|
1676 | Util.requestAnimFrame(function () {
|
1677 | this
|
1678 | ._moveStart(true, false)
|
1679 | ._animateZoom(center, zoom, true);
|
1680 | }, this);
|
1681 |
|
1682 | return true;
|
1683 | },
|
1684 |
|
1685 | _animateZoom: function (center, zoom, startAnim, noUpdate) {
|
1686 | if (!this._mapPane) { return; }
|
1687 |
|
1688 | if (startAnim) {
|
1689 | this._animatingZoom = true;
|
1690 |
|
1691 |
|
1692 | this._animateToCenter = center;
|
1693 | this._animateToZoom = zoom;
|
1694 |
|
1695 | DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim');
|
1696 | }
|
1697 |
|
1698 |
|
1699 |
|
1700 |
|
1701 | this.fire('zoomanim', {
|
1702 | center: center,
|
1703 | zoom: zoom,
|
1704 | noUpdate: noUpdate
|
1705 | });
|
1706 |
|
1707 | if (!this._tempFireZoomEvent) {
|
1708 | this._tempFireZoomEvent = this._zoom !== this._animateToZoom;
|
1709 | }
|
1710 |
|
1711 | this._move(this._animateToCenter, this._animateToZoom, undefined, true);
|
1712 |
|
1713 |
|
1714 | setTimeout(Util.bind(this._onZoomTransitionEnd, this), 250);
|
1715 | },
|
1716 |
|
1717 | _onZoomTransitionEnd: function () {
|
1718 | if (!this._animatingZoom) { return; }
|
1719 |
|
1720 | if (this._mapPane) {
|
1721 | DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim');
|
1722 | }
|
1723 |
|
1724 | this._animatingZoom = false;
|
1725 |
|
1726 | this._move(this._animateToCenter, this._animateToZoom, undefined, true);
|
1727 |
|
1728 | if (this._tempFireZoomEvent) {
|
1729 | this.fire('zoom');
|
1730 | }
|
1731 | delete this._tempFireZoomEvent;
|
1732 |
|
1733 | this.fire('move');
|
1734 |
|
1735 | this._moveEnd(true);
|
1736 | }
|
1737 | });
|
1738 |
|
1739 |
|
1740 |
|
1741 |
|
1742 |
|
1743 |
|
1744 |
|
1745 |
|
1746 |
|
1747 |
|
1748 |
|
1749 | export function createMap(id, options) {
|
1750 | return new Map(id, options);
|
1751 | }
|