Non puoi selezionare più di 25 argomenti
Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
269 righe
10 KiB
269 righe
10 KiB
/* |
|
* Extends L.Map to synchronize the interaction on one map to one or more other maps. |
|
*/ |
|
|
|
(function () { |
|
var NO_ANIMATION = { |
|
animate: false, |
|
reset: true, |
|
disableViewprereset: true |
|
}; |
|
|
|
L.Sync = function () {}; |
|
/* |
|
* Helper function to compute the offset easily. |
|
* |
|
* The arguments are relative positions with respect to reference and target maps of |
|
* the point to sync. If you provide ratioRef=[0, 1], ratioTarget=[1, 0] will sync the |
|
* bottom left corner of the reference map with the top right corner of the target map. |
|
* The values can be less than 0 or greater than 1. It will sync points out of the map. |
|
*/ |
|
L.Sync.offsetHelper = function (ratioRef, ratioTarget) { |
|
var or = L.Util.isArray(ratioRef) ? ratioRef : [0.5, 0.5]; |
|
var ot = L.Util.isArray(ratioTarget) ? ratioTarget : [0.5, 0.5]; |
|
return function (center, zoom, refMap, targetMap) { |
|
var rs = refMap.getSize(); |
|
var ts = targetMap.getSize(); |
|
var pt = refMap.project(center, zoom) |
|
.subtract([(0.5 - or[0]) * rs.x, (0.5 - or[1]) * rs.y]) |
|
.add([(0.5 - ot[0]) * ts.x, (0.5 - ot[1]) * ts.y]); |
|
return refMap.unproject(pt, zoom); |
|
}; |
|
}; |
|
|
|
|
|
L.Map.include({ |
|
sync: function (map, options) { |
|
this._initSync(); |
|
options = L.extend({ |
|
noInitialSync: false, |
|
syncCursor: false, |
|
syncCursorMarkerOptions: { |
|
radius: 10, |
|
fillOpacity: 0.3, |
|
color: '#da291c', |
|
fillColor: '#fff' |
|
}, |
|
offsetFn: function (center, zoom, refMap, targetMap) { |
|
// no transformation at all |
|
return center; |
|
} |
|
}, options); |
|
|
|
// prevent double-syncing the map: |
|
if (this._syncMaps.indexOf(map) === -1) { |
|
this._syncMaps.push(map); |
|
this._syncOffsetFns[L.Util.stamp(map)] = options.offsetFn; |
|
} |
|
|
|
if (!options.noInitialSync) { |
|
map.setView( |
|
options.offsetFn(this.getCenter(), this.getZoom(), this, map), |
|
this.getZoom(), NO_ANIMATION); |
|
} |
|
if (options.syncCursor) { |
|
if (typeof map.cursor === 'undefined') { |
|
map.cursor = L.circleMarker([0, 0], options.syncCursorMarkerOptions).addTo(map); |
|
} |
|
|
|
this._cursors.push(map.cursor); |
|
|
|
this.on('mousemove', this._cursorSyncMove, this); |
|
this.on('mouseout', this._cursorSyncOut, this); |
|
} |
|
|
|
// on these events, we should reset the view on every synced map |
|
// dragstart is due to inertia |
|
this.on('resize zoomend', this._selfSetView); |
|
this.on('moveend', this._syncOnMoveend); |
|
this.on('dragend', this._syncOnDragend); |
|
return this; |
|
}, |
|
|
|
|
|
// unsync maps from each other |
|
unsync: function (map) { |
|
var self = this; |
|
|
|
if (this._cursors) { |
|
this._cursors.forEach(function (cursor, indx, _cursors) { |
|
if (cursor === map.cursor) { |
|
_cursors.splice(indx, 1); |
|
} |
|
}); |
|
} |
|
|
|
// TODO: hide cursor in stead of moving to 0, 0 |
|
if (map.cursor) { |
|
map.cursor.setLatLng([0, 0]); |
|
} |
|
|
|
if (this._syncMaps) { |
|
this._syncMaps.forEach(function (synced, id) { |
|
if (map === synced) { |
|
delete self._syncOffsetFns[L.Util.stamp(map)]; |
|
self._syncMaps.splice(id, 1); |
|
} |
|
}); |
|
} |
|
|
|
if (!this._syncMaps || this._syncMaps.length == 0) { |
|
// no more synced maps, so these events are not needed. |
|
this.off('resize zoomend', this._selfSetView); |
|
this.off('moveend', this._syncOnMoveend); |
|
this.off('dragend', this._syncOnDragend); |
|
} |
|
|
|
return this; |
|
}, |
|
|
|
// Checks if the map is synced with anything or a specifyc map |
|
isSynced: function (otherMap) { |
|
var has = (this.hasOwnProperty('_syncMaps') && Object.keys(this._syncMaps).length > 0); |
|
if (has && otherMap) { |
|
// Look for this specific map |
|
has = false; |
|
this._syncMaps.forEach(function (synced) { |
|
if (otherMap == synced) { has = true; } |
|
}); |
|
} |
|
return has; |
|
}, |
|
|
|
|
|
// Callbacks for events... |
|
_cursorSyncMove: function (e) { |
|
this._cursors.forEach(function (cursor) { |
|
cursor.setLatLng(e.latlng); |
|
}); |
|
}, |
|
|
|
_cursorSyncOut: function (e) { |
|
this._cursors.forEach(function (cursor) { |
|
// TODO: hide cursor in stead of moving to 0, 0 |
|
cursor.setLatLng([0, 0]); |
|
}); |
|
}, |
|
|
|
_selfSetView: function (e) { |
|
// reset the map, and let setView synchronize the others. |
|
this.setView(this.getCenter(), this.getZoom(), NO_ANIMATION); |
|
}, |
|
|
|
_syncOnMoveend: function (e) { |
|
if (this._syncDragend) { |
|
// This is 'the moveend' after the dragend. |
|
// Without inertia, it will be right after, |
|
// but when inertia is on, we need this to detect that. |
|
this._syncDragend = false; // before calling setView! |
|
this._selfSetView(e); |
|
this._syncMaps.forEach(function (toSync) { |
|
toSync.fire('moveend'); |
|
}); |
|
} |
|
}, |
|
|
|
_syncOnDragend: function (e) { |
|
// It is ugly to have state, but we need it in case of inertia. |
|
this._syncDragend = true; |
|
}, |
|
|
|
|
|
// overload methods on originalMap to replay interactions on _syncMaps; |
|
_initSync: function () { |
|
if (this._syncMaps) { |
|
return; |
|
} |
|
var originalMap = this; |
|
|
|
this._syncMaps = []; |
|
this._cursors = []; |
|
this._syncOffsetFns = {}; |
|
|
|
L.extend(originalMap, { |
|
setView: function (center, zoom, options, sync) { |
|
// Use this sandwich to disable and enable viewprereset |
|
// around setView call |
|
function sandwich (obj, fn) { |
|
var viewpreresets = []; |
|
var doit = options && options.disableViewprereset && obj && obj._events; |
|
if (doit) { |
|
// The event viewpreresets does an invalidateAll, |
|
// that reloads all the tiles. |
|
// That causes an annoying flicker. |
|
viewpreresets = obj._events.viewprereset; |
|
obj._events.viewprereset = []; |
|
} |
|
var ret = fn(obj); |
|
if (doit) { |
|
// restore viewpreresets event to its previous values |
|
obj._events.viewprereset = viewpreresets; |
|
} |
|
return ret; |
|
} |
|
|
|
// Looks better if the other maps 'follow' the active one, |
|
// so call this before _syncMaps |
|
var ret = sandwich(this, function (obj) { |
|
return L.Map.prototype.setView.call(obj, center, zoom, options); |
|
}); |
|
|
|
if (!sync) { |
|
originalMap._syncMaps.forEach(function (toSync) { |
|
sandwich(toSync, function (obj) { |
|
return toSync.setView( |
|
originalMap._syncOffsetFns[L.Util.stamp(toSync)](center, zoom, originalMap, toSync), |
|
zoom, options, true); |
|
}); |
|
}); |
|
} |
|
|
|
return ret; |
|
}, |
|
|
|
panBy: function (offset, options, sync) { |
|
if (!sync) { |
|
originalMap._syncMaps.forEach(function (toSync) { |
|
toSync.panBy(offset, options, true); |
|
}); |
|
} |
|
return L.Map.prototype.panBy.call(this, offset, options); |
|
}, |
|
|
|
_onResize: function (event, sync) { |
|
if (!sync) { |
|
originalMap._syncMaps.forEach(function (toSync) { |
|
toSync._onResize(event, true); |
|
}); |
|
} |
|
return L.Map.prototype._onResize.call(this, event); |
|
}, |
|
|
|
_stop: function (sync) { |
|
L.Map.prototype._stop.call(this); |
|
if (!sync) { |
|
originalMap._syncMaps.forEach(function (toSync) { |
|
toSync._stop(true); |
|
}); |
|
} |
|
} |
|
}); |
|
|
|
originalMap.dragging._draggable._updatePosition = function () { |
|
L.Draggable.prototype._updatePosition.call(this); |
|
var self = this; |
|
originalMap._syncMaps.forEach(function (toSync) { |
|
L.DomUtil.setPosition(toSync.dragging._draggable._element, self._newPos); |
|
toSync.eachLayer(function (layer) { |
|
if (layer._google !== undefined) { |
|
var offsetFn = originalMap._syncOffsetFns[L.Util.stamp(toSync)]; |
|
var center = offsetFn(originalMap.getCenter(), originalMap.getZoom(), originalMap, toSync); |
|
layer._google.setCenter(center); |
|
} |
|
}); |
|
toSync.fire('move'); |
|
}); |
|
}; |
|
} |
|
}); |
|
})();
|
|
|