568 lines
17 KiB
JavaScript
568 lines
17 KiB
JavaScript
// Generated by CoffeeScript 1.6.2
|
|
/** @preserve OverlappingMarkerSpiderfier
|
|
https://github.com/jawj/OverlappingMarkerSpiderfier
|
|
Copyright (c) 2011 - 2012 George MacKerron
|
|
Released under the MIT licence: http://opensource.org/licenses/mit-license
|
|
Note: The Google Maps API v3 must be included *before* this code
|
|
*/
|
|
|
|
|
|
(function() {
|
|
var _ref,
|
|
__hasProp = {}.hasOwnProperty,
|
|
__slice = [].slice;
|
|
|
|
if (((_ref = this['google']) != null ? _ref['maps'] : void 0) == null) {
|
|
return;
|
|
}
|
|
|
|
this['OverlappingMarkerSpiderfier'] = (function() {
|
|
var ge, gm, lcH, lcU, mt, p, twoPi, x, _i, _len, _ref1;
|
|
|
|
p = _Class.prototype;
|
|
|
|
_ref1 = [_Class, p];
|
|
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
|
x = _ref1[_i];
|
|
x['VERSION'] = '0.3.3';
|
|
}
|
|
|
|
gm = google.maps;
|
|
|
|
ge = gm.event;
|
|
|
|
mt = gm.MapTypeId;
|
|
|
|
twoPi = Math.PI * 2;
|
|
|
|
p['keepSpiderfied'] = false;
|
|
|
|
p['markersWontHide'] = false;
|
|
|
|
p['markersWontMove'] = false;
|
|
|
|
p['nearbyDistance'] = 20;
|
|
|
|
p['circleSpiralSwitchover'] = 20;
|
|
|
|
p['circleFootSeparation'] = 43;
|
|
|
|
p['circleStartAngle'] = twoPi / 12;
|
|
|
|
p['spiralFootSeparation'] = 26;
|
|
|
|
p['spiralLengthStart'] = 11;
|
|
|
|
p['spiralLengthFactor'] = 4;
|
|
|
|
p['spiderfiedZIndex'] = 1000;
|
|
|
|
p['usualLegZIndex'] = 10;
|
|
|
|
p['highlightedLegZIndex'] = 20;
|
|
|
|
p['legWeight'] = 1.5;
|
|
|
|
p['legColors'] = {
|
|
'usual': {},
|
|
'highlighted': {}
|
|
};
|
|
|
|
lcU = p['legColors']['usual'];
|
|
|
|
lcH = p['legColors']['highlighted'];
|
|
|
|
lcU[mt.HYBRID] = lcU[mt.SATELLITE] = '#fff';
|
|
|
|
lcH[mt.HYBRID] = lcH[mt.SATELLITE] = '#f00';
|
|
|
|
lcU[mt.TERRAIN] = lcU[mt.ROADMAP] = '#444';
|
|
|
|
lcH[mt.TERRAIN] = lcH[mt.ROADMAP] = '#f00';
|
|
|
|
function _Class(map, opts) {
|
|
var e, k, v, _j, _len1, _ref2,
|
|
_this = this;
|
|
|
|
this.map = map;
|
|
if (opts == null) {
|
|
opts = {};
|
|
}
|
|
for (k in opts) {
|
|
if (!__hasProp.call(opts, k)) continue;
|
|
v = opts[k];
|
|
this[k] = v;
|
|
}
|
|
this.projHelper = new this.constructor.ProjHelper(this.map);
|
|
this.initMarkerArrays();
|
|
this.listeners = {};
|
|
_ref2 = ['click', 'zoom_changed', 'maptypeid_changed'];
|
|
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
|
e = _ref2[_j];
|
|
ge.addListener(this.map, e, function() {
|
|
return _this['unspiderfy']();
|
|
});
|
|
}
|
|
}
|
|
|
|
p.initMarkerArrays = function() {
|
|
this.markers = [];
|
|
return this.markerListenerRefs = [];
|
|
};
|
|
|
|
p['addMarker'] = function(marker) {
|
|
var listenerRefs,
|
|
_this = this;
|
|
|
|
if (marker['_oms'] != null) {
|
|
return this;
|
|
}
|
|
marker['_oms'] = true;
|
|
listenerRefs = [
|
|
ge.addListener(marker, 'click', function(event) {
|
|
return _this.spiderListener(marker, event);
|
|
})
|
|
];
|
|
if (!this['markersWontHide']) {
|
|
listenerRefs.push(ge.addListener(marker, 'visible_changed', function() {
|
|
return _this.markerChangeListener(marker, false);
|
|
}));
|
|
}
|
|
if (!this['markersWontMove']) {
|
|
listenerRefs.push(ge.addListener(marker, 'position_changed', function() {
|
|
return _this.markerChangeListener(marker, true);
|
|
}));
|
|
}
|
|
this.markerListenerRefs.push(listenerRefs);
|
|
this.markers.push(marker);
|
|
return this;
|
|
};
|
|
|
|
p.markerChangeListener = function(marker, positionChanged) {
|
|
if ((marker['_omsData'] != null) && (positionChanged || !marker.getVisible()) && !((this.spiderfying != null) || (this.unspiderfying != null))) {
|
|
return this['unspiderfy'](positionChanged ? marker : null);
|
|
}
|
|
};
|
|
|
|
p['getMarkers'] = function() {
|
|
return this.markers.slice(0);
|
|
};
|
|
|
|
p['removeMarker'] = function(marker) {
|
|
var i, listenerRef, listenerRefs, _j, _len1;
|
|
|
|
if (marker['_omsData'] != null) {
|
|
this['unspiderfy']();
|
|
}
|
|
i = this.arrIndexOf(this.markers, marker);
|
|
if (i < 0) {
|
|
return this;
|
|
}
|
|
listenerRefs = this.markerListenerRefs.splice(i, 1)[0];
|
|
for (_j = 0, _len1 = listenerRefs.length; _j < _len1; _j++) {
|
|
listenerRef = listenerRefs[_j];
|
|
ge.removeListener(listenerRef);
|
|
}
|
|
delete marker['_oms'];
|
|
this.markers.splice(i, 1);
|
|
return this;
|
|
};
|
|
|
|
p['clearMarkers'] = function() {
|
|
var i, listenerRef, listenerRefs, marker, _j, _k, _len1, _len2, _ref2;
|
|
|
|
this['unspiderfy']();
|
|
_ref2 = this.markers;
|
|
for (i = _j = 0, _len1 = _ref2.length; _j < _len1; i = ++_j) {
|
|
marker = _ref2[i];
|
|
listenerRefs = this.markerListenerRefs[i];
|
|
for (_k = 0, _len2 = listenerRefs.length; _k < _len2; _k++) {
|
|
listenerRef = listenerRefs[_k];
|
|
ge.removeListener(listenerRef);
|
|
}
|
|
delete marker['_oms'];
|
|
}
|
|
this.initMarkerArrays();
|
|
return this;
|
|
};
|
|
|
|
p['addListener'] = function(event, func) {
|
|
var _base, _ref2;
|
|
|
|
((_ref2 = (_base = this.listeners)[event]) != null ? _ref2 : _base[event] = []).push(func);
|
|
return this;
|
|
};
|
|
|
|
p['removeListener'] = function(event, func) {
|
|
var i;
|
|
|
|
i = this.arrIndexOf(this.listeners[event], func);
|
|
if (!(i < 0)) {
|
|
this.listeners[event].splice(i, 1);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
p['clearListeners'] = function(event) {
|
|
this.listeners[event] = [];
|
|
return this;
|
|
};
|
|
|
|
p.trigger = function() {
|
|
var args, event, func, _j, _len1, _ref2, _ref3, _results;
|
|
|
|
event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
|
_ref3 = (_ref2 = this.listeners[event]) != null ? _ref2 : [];
|
|
_results = [];
|
|
for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) {
|
|
func = _ref3[_j];
|
|
_results.push(func.apply(null, args));
|
|
}
|
|
return _results;
|
|
};
|
|
|
|
p.generatePtsCircle = function(count, centerPt) {
|
|
var angle, angleStep, circumference, i, legLength, _j, _results;
|
|
|
|
circumference = this['circleFootSeparation'] * (2 + count);
|
|
legLength = circumference / twoPi;
|
|
angleStep = twoPi / count;
|
|
_results = [];
|
|
for (i = _j = 0; 0 <= count ? _j < count : _j > count; i = 0 <= count ? ++_j : --_j) {
|
|
angle = this['circleStartAngle'] + i * angleStep;
|
|
_results.push(new gm.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle)));
|
|
}
|
|
return _results;
|
|
};
|
|
|
|
p.generatePtsSpiral = function(count, centerPt) {
|
|
var angle, i, legLength, pt, _j, _results;
|
|
|
|
legLength = this['spiralLengthStart'];
|
|
angle = 0;
|
|
_results = [];
|
|
for (i = _j = 0; 0 <= count ? _j < count : _j > count; i = 0 <= count ? ++_j : --_j) {
|
|
angle += this['spiralFootSeparation'] / legLength + i * 0.0005;
|
|
pt = new gm.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle));
|
|
legLength += twoPi * this['spiralLengthFactor'] / angle;
|
|
_results.push(pt);
|
|
}
|
|
return _results;
|
|
};
|
|
|
|
p.spiderListener = function(marker, event) {
|
|
var m, mPt, markerPt, markerSpiderfied, nDist, nearbyMarkerData, nonNearbyMarkers, pxSq, _j, _len1, _ref2;
|
|
|
|
markerSpiderfied = marker['_omsData'] != null;
|
|
if (!(markerSpiderfied && this['keepSpiderfied'])) {
|
|
this['unspiderfy']();
|
|
}
|
|
if (markerSpiderfied || this.map.getStreetView().getVisible() || this.map.getMapTypeId() === 'GoogleEarthAPI') {
|
|
return this.trigger('click', marker, event);
|
|
} else {
|
|
nearbyMarkerData = [];
|
|
nonNearbyMarkers = [];
|
|
nDist = this['nearbyDistance'];
|
|
pxSq = nDist * nDist;
|
|
markerPt = this.llToPt(marker.position);
|
|
_ref2 = this.markers;
|
|
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
|
m = _ref2[_j];
|
|
if (!((m.map != null) && m.getVisible())) {
|
|
continue;
|
|
}
|
|
mPt = this.llToPt(m.position);
|
|
if (this.ptDistanceSq(mPt, markerPt) < pxSq) {
|
|
nearbyMarkerData.push({
|
|
marker: m,
|
|
markerPt: mPt
|
|
});
|
|
} else {
|
|
nonNearbyMarkers.push(m);
|
|
}
|
|
}
|
|
if (nearbyMarkerData.length === 1) {
|
|
return this.trigger('click', marker, event);
|
|
} else {
|
|
return this.spiderfy(nearbyMarkerData, nonNearbyMarkers);
|
|
}
|
|
}
|
|
};
|
|
|
|
p['markersNearMarker'] = function(marker, firstOnly) {
|
|
var m, mPt, markerPt, markers, nDist, pxSq, _j, _len1, _ref2, _ref3, _ref4;
|
|
|
|
if (firstOnly == null) {
|
|
firstOnly = false;
|
|
}
|
|
if (this.projHelper.getProjection() == null) {
|
|
throw "Must wait for 'idle' event on map before calling markersNearMarker";
|
|
}
|
|
nDist = this['nearbyDistance'];
|
|
pxSq = nDist * nDist;
|
|
markerPt = this.llToPt(marker.position);
|
|
markers = [];
|
|
_ref2 = this.markers;
|
|
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
|
m = _ref2[_j];
|
|
if (m === marker || (m.map == null) || !m.getVisible()) {
|
|
continue;
|
|
}
|
|
mPt = this.llToPt((_ref3 = (_ref4 = m['_omsData']) != null ? _ref4.usualPosition : void 0) != null ? _ref3 : m.position);
|
|
if (this.ptDistanceSq(mPt, markerPt) < pxSq) {
|
|
markers.push(m);
|
|
if (firstOnly) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return markers;
|
|
};
|
|
|
|
p['markersNearAnyOtherMarker'] = function() {
|
|
var i, i1, i2, m, m1, m1Data, m2, m2Data, mData, nDist, pxSq, _j, _k, _l, _len1, _len2, _len3, _ref2, _ref3, _ref4, _results;
|
|
|
|
if (this.projHelper.getProjection() == null) {
|
|
throw "Must wait for 'idle' event on map before calling markersNearAnyOtherMarker";
|
|
}
|
|
nDist = this['nearbyDistance'];
|
|
pxSq = nDist * nDist;
|
|
mData = (function() {
|
|
var _j, _len1, _ref2, _ref3, _ref4, _results;
|
|
|
|
_ref2 = this.markers;
|
|
_results = [];
|
|
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
|
m = _ref2[_j];
|
|
_results.push({
|
|
pt: this.llToPt((_ref3 = (_ref4 = m['_omsData']) != null ? _ref4.usualPosition : void 0) != null ? _ref3 : m.position),
|
|
willSpiderfy: false
|
|
});
|
|
}
|
|
return _results;
|
|
}).call(this);
|
|
_ref2 = this.markers;
|
|
for (i1 = _j = 0, _len1 = _ref2.length; _j < _len1; i1 = ++_j) {
|
|
m1 = _ref2[i1];
|
|
if (!((m1.map != null) && m1.getVisible())) {
|
|
continue;
|
|
}
|
|
m1Data = mData[i1];
|
|
if (m1Data.willSpiderfy) {
|
|
continue;
|
|
}
|
|
_ref3 = this.markers;
|
|
for (i2 = _k = 0, _len2 = _ref3.length; _k < _len2; i2 = ++_k) {
|
|
m2 = _ref3[i2];
|
|
if (i2 === i1) {
|
|
continue;
|
|
}
|
|
if (!((m2.map != null) && m2.getVisible())) {
|
|
continue;
|
|
}
|
|
m2Data = mData[i2];
|
|
if (i2 < i1 && !m2Data.willSpiderfy) {
|
|
continue;
|
|
}
|
|
if (this.ptDistanceSq(m1Data.pt, m2Data.pt) < pxSq) {
|
|
m1Data.willSpiderfy = m2Data.willSpiderfy = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
_ref4 = this.markers;
|
|
_results = [];
|
|
for (i = _l = 0, _len3 = _ref4.length; _l < _len3; i = ++_l) {
|
|
m = _ref4[i];
|
|
if (mData[i].willSpiderfy) {
|
|
_results.push(m);
|
|
}
|
|
}
|
|
return _results;
|
|
};
|
|
|
|
p.makeHighlightListenerFuncs = function(marker) {
|
|
var _this = this;
|
|
|
|
return {
|
|
highlight: function() {
|
|
return marker['_omsData'].leg.setOptions({
|
|
strokeColor: _this['legColors']['highlighted'][_this.map.mapTypeId],
|
|
zIndex: _this['highlightedLegZIndex']
|
|
});
|
|
},
|
|
unhighlight: function() {
|
|
return marker['_omsData'].leg.setOptions({
|
|
strokeColor: _this['legColors']['usual'][_this.map.mapTypeId],
|
|
zIndex: _this['usualLegZIndex']
|
|
});
|
|
}
|
|
};
|
|
};
|
|
|
|
p.spiderfy = function(markerData, nonNearbyMarkers) {
|
|
var bodyPt, footLl, footPt, footPts, highlightListenerFuncs, leg, marker, md, nearestMarkerDatum, numFeet, spiderfiedMarkers;
|
|
|
|
this.spiderfying = true;
|
|
numFeet = markerData.length;
|
|
bodyPt = this.ptAverage((function() {
|
|
var _j, _len1, _results;
|
|
|
|
_results = [];
|
|
for (_j = 0, _len1 = markerData.length; _j < _len1; _j++) {
|
|
md = markerData[_j];
|
|
_results.push(md.markerPt);
|
|
}
|
|
return _results;
|
|
})());
|
|
footPts = numFeet >= this['circleSpiralSwitchover'] ? this.generatePtsSpiral(numFeet, bodyPt).reverse() : this.generatePtsCircle(numFeet, bodyPt);
|
|
spiderfiedMarkers = (function() {
|
|
var _j, _len1, _results,
|
|
_this = this;
|
|
|
|
_results = [];
|
|
for (_j = 0, _len1 = footPts.length; _j < _len1; _j++) {
|
|
footPt = footPts[_j];
|
|
footLl = this.ptToLl(footPt);
|
|
nearestMarkerDatum = this.minExtract(markerData, function(md) {
|
|
return _this.ptDistanceSq(md.markerPt, footPt);
|
|
});
|
|
marker = nearestMarkerDatum.marker;
|
|
leg = new gm.Polyline({
|
|
map: this.map,
|
|
path: [marker.position, footLl],
|
|
strokeColor: this['legColors']['usual'][this.map.mapTypeId],
|
|
strokeWeight: this['legWeight'],
|
|
zIndex: this['usualLegZIndex']
|
|
});
|
|
marker['_omsData'] = {
|
|
usualPosition: marker.position,
|
|
leg: leg
|
|
};
|
|
if (this['legColors']['highlighted'][this.map.mapTypeId] !== this['legColors']['usual'][this.map.mapTypeId]) {
|
|
highlightListenerFuncs = this.makeHighlightListenerFuncs(marker);
|
|
marker['_omsData'].hightlightListeners = {
|
|
highlight: ge.addListener(marker, 'mouseover', highlightListenerFuncs.highlight),
|
|
unhighlight: ge.addListener(marker, 'mouseout', highlightListenerFuncs.unhighlight)
|
|
};
|
|
}
|
|
marker.setPosition(footLl);
|
|
marker.setZIndex(Math.round(this['spiderfiedZIndex'] + footPt.y));
|
|
_results.push(marker);
|
|
}
|
|
return _results;
|
|
}).call(this);
|
|
delete this.spiderfying;
|
|
this.spiderfied = true;
|
|
return this.trigger('spiderfy', spiderfiedMarkers, nonNearbyMarkers);
|
|
};
|
|
|
|
p['unspiderfy'] = function(markerNotToMove) {
|
|
var listeners, marker, nonNearbyMarkers, unspiderfiedMarkers, _j, _len1, _ref2;
|
|
|
|
if (markerNotToMove == null) {
|
|
markerNotToMove = null;
|
|
}
|
|
if (this.spiderfied == null) {
|
|
return this;
|
|
}
|
|
this.unspiderfying = true;
|
|
unspiderfiedMarkers = [];
|
|
nonNearbyMarkers = [];
|
|
_ref2 = this.markers;
|
|
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
|
|
marker = _ref2[_j];
|
|
if (marker['_omsData'] != null) {
|
|
marker['_omsData'].leg.setMap(null);
|
|
if (marker !== markerNotToMove) {
|
|
marker.setPosition(marker['_omsData'].usualPosition);
|
|
}
|
|
marker.setZIndex(null);
|
|
listeners = marker['_omsData'].hightlightListeners;
|
|
if (listeners != null) {
|
|
ge.removeListener(listeners.highlight);
|
|
ge.removeListener(listeners.unhighlight);
|
|
}
|
|
delete marker['_omsData'];
|
|
unspiderfiedMarkers.push(marker);
|
|
} else {
|
|
nonNearbyMarkers.push(marker);
|
|
}
|
|
}
|
|
delete this.unspiderfying;
|
|
delete this.spiderfied;
|
|
this.trigger('unspiderfy', unspiderfiedMarkers, nonNearbyMarkers);
|
|
return this;
|
|
};
|
|
|
|
p.ptDistanceSq = function(pt1, pt2) {
|
|
var dx, dy;
|
|
|
|
dx = pt1.x - pt2.x;
|
|
dy = pt1.y - pt2.y;
|
|
return dx * dx + dy * dy;
|
|
};
|
|
|
|
p.ptAverage = function(pts) {
|
|
var numPts, pt, sumX, sumY, _j, _len1;
|
|
|
|
sumX = sumY = 0;
|
|
for (_j = 0, _len1 = pts.length; _j < _len1; _j++) {
|
|
pt = pts[_j];
|
|
sumX += pt.x;
|
|
sumY += pt.y;
|
|
}
|
|
numPts = pts.length;
|
|
return new gm.Point(sumX / numPts, sumY / numPts);
|
|
};
|
|
|
|
p.llToPt = function(ll) {
|
|
return this.projHelper.getProjection().fromLatLngToDivPixel(ll);
|
|
};
|
|
|
|
p.ptToLl = function(pt) {
|
|
return this.projHelper.getProjection().fromDivPixelToLatLng(pt);
|
|
};
|
|
|
|
p.minExtract = function(set, func) {
|
|
var bestIndex, bestVal, index, item, val, _j, _len1;
|
|
|
|
for (index = _j = 0, _len1 = set.length; _j < _len1; index = ++_j) {
|
|
item = set[index];
|
|
val = func(item);
|
|
if ((typeof bestIndex === "undefined" || bestIndex === null) || val < bestVal) {
|
|
bestVal = val;
|
|
bestIndex = index;
|
|
}
|
|
}
|
|
return set.splice(bestIndex, 1)[0];
|
|
};
|
|
|
|
p.arrIndexOf = function(arr, obj) {
|
|
var i, o, _j, _len1;
|
|
|
|
if (arr.indexOf != null) {
|
|
return arr.indexOf(obj);
|
|
}
|
|
for (i = _j = 0, _len1 = arr.length; _j < _len1; i = ++_j) {
|
|
o = arr[i];
|
|
if (o === obj) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
_Class.ProjHelper = function(map) {
|
|
return this.setMap(map);
|
|
};
|
|
|
|
_Class.ProjHelper.prototype = new gm.OverlayView();
|
|
|
|
_Class.ProjHelper.prototype['draw'] = function() {};
|
|
|
|
return _Class;
|
|
|
|
})();
|
|
|
|
}).call(this); |