eon-map
Version:
Mapbox Realtime Maps with PubNub. Part of the EON Framework.
324 lines (215 loc) • 8.55 kB
JavaScript
module.exports = function (options) {
options.debug = options.debug || false;
var clog = function(s, o, e) {
if (options.debug) {
if (e) {
console.log('EON-MAP:', s, o, e);
} else {
console.log('EON-MAP:', s, o);
}
}
};
if(typeof(options.pubnub) == "undefined" && console) {
return console.error("PubNub not found. See http://www.pubnub.com/docs/javascript/javascript-sdk.html#_where_do_i_get_the_code");
}
if(typeof(L) == "undefined" && console) {
return console.error("You need to include the Mapbox or Google Javascript library.");
}
var self = this;
var geo = {
bearing : function (lat1,lng1,lat2,lng2) {
var dLon = this._toRad(lng2-lng1);
var y = Math.sin(dLon) * Math.cos(this._toRad(lat2));
var x = Math.cos(this._toRad(lat1))*Math.sin(this._toRad(lat2)) - Math.sin(this._toRad(lat1))*Math.cos(this._toRad(lat2))*Math.cos(dLon);
var brng = this._toDeg(Math.atan2(y, x));
return ((brng + 360) % 360);
},
_toRad : function(deg) {
return deg * Math.PI / 180;
},
_toDeg : function(rad) {
return rad * 180 / Math.PI;
}
};
self.pubnub = options.pubnub || PubNub || false;
if(!self.pubnub) {
error = "PubNub not found. See http://www.pubnub.com/docs/javascript/javascript-sdk.html#_where_do_i_get_the_code";
}
options.id = options.id || false;
options.channels = options.channels || false;
options.channelGroups = options.channelGroups || false;
options.transform = options.transform || function(m){return m};
options.history = options.history || false;
options.message = options.message || function(){};
options.connect = options.connect || function(){};
options.rotate = options.rotate || false;
options.marker = options.marker || L.marker;
options.options = options.options || {};
options.provider = options.provider || 'mapbox';
options.googleMutant = options.googleMutant || {
type: 'roadmap' // valid values are 'roadmap', 'satellite', 'terrain' and 'hybrid'
};
clog('Options', options);
self.markers = {};
if(!options.id) {
return console.error('You need to set an ID for your Mapbox element.');
}
if(!options.channels && !options.channelGroups) {
return console.error('You need to supply channels or channelGroups to connect to.');
}
if(options.provider == 'mapbox') {
if(typeof(options.mbToken) == "undefined" && console) {
return console.error("Please supply a Mapbox Token: https://www.mapbox.com/help/create-api-access-token/");
}
if(typeof(options.mbId) == "undefined" && console) {
return console.error("Please supply a Mapbox Map ID: https://www.mapbox.com/help/define-map-id/");
}
L.mapbox.accessToken = options.mbToken;
self.map = L.mapbox.map(options.id, options.mbId, options.options);
}
if(options.provider == 'google') {
if(typeof(options.googleKey) == "undefined" && console) {
return console.error("Please supply a Google Maps API Key");
}
options.options - options.options || {};
options.options.center = options.options.center || new L.LatLng(30.2672, -97.7531);
options.options.zoom = options.options.zoom || 5;
GoogleMapsLoader.KEY = options.googleKey;
self.map = new L.Map('map', options.options);
GoogleMapsLoader.load(function(google) {
var roads = L.gridLayer.googleMutant(options.googleMutant).addTo(self.map);
});
}
self.refreshRate = options.refreshRate || 10;
self.lastUpdates = {};
self.update = function (seed, animate) {
clog('Markers:', 'Updating', seed);
for(var key in seed) {
if(seed.hasOwnProperty(key)) {
if(!self.markers[key]) {
var data = seed[key].data || {};
self.markers[key]= options.marker(seed[key].latlng, seed[key].data);
self.markers[key].addTo(self.map);
} else {
if(animate) {
clog('Markers:', 'Animating');
self.animate(key, seed[key].latlng);
} else {
clog('Markers:', 'Updating');
self.updateMarker(key, seed[key].latlng);
}
}
}
self.lastUpdates[key] = new Date().getTime();
}
};
var isNumber = function(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
};
self.updateMarker = function (index, point) {
if(point && point.length > 1) {
if(isNumber(point[0]) && isNumber(point[1])) {
self.markers[index].setLatLng(point);
}
}
};
self.animations = {};
self.animate = function (index, destination) {
var startlatlng = self.markers[index].getLatLng();
var animation = {
start: startlatlng,
dest: destination,
time: new Date().getTime(),
length: new Date().getTime() - (self.lastUpdates[index] || new Date().getTime())
};
clog('Animation:', animation);
self.animations[index] = animation;
clog('Animations:', self.animations);
};
self.refresh = function() {
var s = {};
for(var index in self.markers) {
if(self.markers.hasOwnProperty(index) && typeof self.animations[index] !== 'undefined') {
s.position = self.animations[index].start;
// number of steps in this animation
s.maxSteps = Math.round(self.animations[index].length / self.refreshRate)
// time that has passed since that message
s.timeSince = new Date().getTime() - self.animations[index].time;
s.numSteps = Math.round(s.timeSince / self.refreshRate); // if this is 1 or 0 it fucks up steps
if(s.numSteps <= s.maxSteps) {
// probably has to do with this math
s.latDistance = self.animations[index].dest[0] - s.position.lat;
s.lngDistance = self.animations[index].dest[1] - s.position.lng;
s.lat = s.position.lat + ((s.latDistance / s.maxSteps) * s.numSteps);
s.lng = s.position.lng + ((s.lngDistance / s.maxSteps) * s.numSteps);
s.nextStep = [s.lat, s.lng];
self.updateMarker(index, s.nextStep);
if(options.rotate) {
self.markers[index].options.angle = geo.bearing(s.position.lat, s.position.lng, s.lat, s.lng);
}
}
}
index++;
}
};
self.pubnub.addListener({
status: function(statusEvent) {
if (statusEvent.category === "PNConnectedCategory") {
options.connect();
}
},
message: function(m) {
if(options.channels.indexOf(m.channel) > -1) {
clog('PubNub:', 'Got Message');
message = options.transform(m.message);
options.message(message, m.timetoken, m.channel);
self.update(message, true);
}
}
});
self.loadHistory = function() {
for(var i in options.channels) {
self.pubnub.history({
channel: options.channels[i],
includeTimetoken: true,
count: 10
}, function(status, payload) {
payload.messages = payload.messages.reverse();
for(var a in payload.messages) {
payload.messages[a].entry = options.transform(payload.messages[a].entry);
options.message(payload.messages[a].entry, payload.messages[a].timetoken, options.channels);
self.update(payload.messages[a].entry, true);
}
});
}
}
if(options.channelGroups) {
// assuming an intialized PubNub instance already exists
self.pubnub.channelGroups.listChannels({
channelGroup: options.channelGroups
}, function (status, response) {
if (status.error) {
clog("operation failed w/ error:", status);
return;
}
options.channels = response.channels;
if(options.history) {
self.loadHistory();
}
self.pubnub.subscribe({
channelGroups: options.channelGroups
});
}
);
} else {
if(options.history) {
self.loadHistory();
}
self.pubnub.subscribe({
channels: options.channels
});
}
self.refresh();
setInterval(self.refresh, self.refreshRate);
return self.map;
};