pokemap
Version:
PokemonGo-Map APIs for node.js
548 lines (457 loc) • 20.2 kB
JavaScript
(function (exports) {
;
// TODO how can we get rid of this? put it in a real stylesheet maybe?
var light2Style=[{"elementType":"geometry","stylers":[{"hue":"#ff4400"},{"saturation":-68},{"lightness":-4},{"gamma":0.72}]},{"featureType":"road","elementType":"labels.icon"},{"featureType":"landscape.man_made","elementType":"geometry","stylers":[{"hue":"#0077ff"},{"gamma":3.1}]},{"featureType":"water","stylers":[{"hue":"#00ccff"},{"gamma":0.44},{"saturation":-33}]},{"featureType":"poi.park","stylers":[{"hue":"#44ff00"},{"saturation":-23}]},{"featureType":"water","elementType":"labels.text.fill","stylers":[{"hue":"#007fff"},{"gamma":0.77},{"saturation":65},{"lightness":99}]},{"featureType":"water","elementType":"labels.text.stroke","stylers":[{"gamma":0.11},{"weight":5.6},{"saturation":99},{"hue":"#0091ff"},{"lightness":-86}]},{"featureType":"transit.line","elementType":"geometry","stylers":[{"lightness":-48},{"hue":"#ff5e00"},{"gamma":1.2},{"saturation":-23}]},{"featureType":"transit","elementType":"labels.text.stroke","stylers":[{"saturation":-64},{"hue":"#ff9100"},{"lightness":16},{"gamma":0.47},{"weight":2.7}]}];
var darkStyle=[{"featureType":"all","elementType":"labels.text.fill","stylers":[{"saturation":36},{"color":"#b39964"},{"lightness":40}]},{"featureType":"all","elementType":"labels.text.stroke","stylers":[{"visibility":"on"},{"color":"#000000"},{"lightness":16}]},{"featureType":"all","elementType":"labels.icon","stylers":[{"visibility":"off"}]},{"featureType":"administrative","elementType":"geometry.fill","stylers":[{"color":"#000000"},{"lightness":20}]},{"featureType":"administrative","elementType":"geometry.stroke","stylers":[{"color":"#000000"},{"lightness":17},{"weight":1.2}]},{"featureType":"landscape","elementType":"geometry","stylers":[{"color":"#000000"},{"lightness":20}]},{"featureType":"poi","elementType":"geometry","stylers":[{"color":"#000000"},{"lightness":21}]},{"featureType":"road.highway","elementType":"geometry.fill","stylers":[{"color":"#000000"},{"lightness":17}]},{"featureType":"road.highway","elementType":"geometry.stroke","stylers":[{"color":"#000000"},{"lightness":29},{"weight":0.2}]},{"featureType":"road.arterial","elementType":"geometry","stylers":[{"color":"#000000"},{"lightness":18}]},{"featureType":"road.local","elementType":"geometry","stylers":[{"color":"#181818"},{"lightness":16}]},{"featureType":"transit","elementType":"geometry","stylers":[{"color":"#000000"},{"lightness":19}]},{"featureType":"water","elementType":"geometry","stylers":[{"lightness":17},{"color":"#525252"}]}];
var pGoStyle=[{"featureType":"landscape.man_made","elementType":"geometry.fill","stylers":[{"color":"#a1f199"}]},{"featureType":"landscape.natural.landcover","elementType":"geometry.fill","stylers":[{"color":"#37bda2"}]},{"featureType":"landscape.natural.terrain","elementType":"geometry.fill","stylers":[{"color":"#37bda2"}]},{"featureType":"poi.attraction","elementType":"geometry.fill","stylers":[{"visibility":"on"}]},{"featureType":"poi.business","elementType":"geometry.fill","stylers":[{"color":"#e4dfd9"}]},{"featureType":"poi.business","elementType":"labels.icon","stylers":[{"visibility":"off"}]},{"featureType":"poi.park","elementType":"geometry.fill","stylers":[{"color":"#37bda2"}]},{"featureType":"road","elementType":"geometry.fill","stylers":[{"color":"#84b09e"}]},{"featureType":"road","elementType":"geometry.stroke","stylers":[{"color":"#fafeb8"},{"weight":"1.25"}]},{"featureType":"road.highway","elementType":"labels.icon","stylers":[{"visibility":"off"}]},{"featureType":"water","elementType":"geometry.fill","stylers":[{"color":"#5ddad6"}]}];
// TODO this doesn't belong here in maps, but rather in main.js
function _sendNotifiction(title, text, icon) {
var notification = new Notification(title, {
icon: icon,
body: text,
sound: 'sounds/ding.mp3'
});
notification.onclick = function () {
window.open(window.location.href);
};
}
function sendNotification(title, text, icon) {
var p;
if (Notification.permission === "granted") {
p = Notification.requestPermission();
p.then(function () {
_sendNotifiction(title, text, icon);
});
return;
}
_sendNotifiction(title, text, icon);
}
var POKEMAP = exports.POKEMAP = {};
POKEMAP.create = function (opts) {
/*
* opts = {
* onChangeLocation: function (lat, lon) { ... }
* , onChangeConfig: function (key, val) { ... }
* }
*/
var STATE = { cfg: {} };
var google;
// Dicts
var map_pokemons = {}; // Pokemon
var map_gyms = {}; // Gyms
var map_pokestops = {}; // Pokestops
var map_scanned = {}; // Pokestops
var gym_types = ["Uncontested", "Mystic", "Valor", "Instinct"];
var audio = new Audio('https://github.com/AHAAAAAAA/PokemonGo-Map/raw/develop/static/sounds/ding.mp3');
function pad(number) {
return number <= 99 ? ("0" + number).slice(-2) : number;
}
function pokemonLabel(name, disappear_time, id, latitude, longitude) {
var disappear_date = new Date(disappear_time);
// TODO template properly with jQuery
var contentstring = ""
+ "<div>"
+ "<b>" + name + "</b>"
+ "<span> - </span>"
+ "<small>"
+ "<a href='http://www.pokemon.com/us/pokedex/" + id + "' target='_blank' title='View in Pokedex'>#" + id + "</a>"
+ "</small>"
+ "</div>"
+ "<div>"
+ "Disappears at " + pad(disappear_date.getHours())
+ ":" + pad(disappear_date.getMinutes())
+ ":" + pad(disappear_date.getSeconds())
+ "<span class='label-countdown' disappears-at='" + disappear_time + "'>(00m00s)</span></div>"
+ "<div>"
+ "<a href='https://www.google.com/maps/dir/Current+Location/" + latitude + "," + longitude + "'"
+ " target='_blank' title='View in Maps'>Get directions</a>"
+ "</div>";
return contentstring;
}
function gymLabel(team_name, team_id, gym_points) {
var gym_color = [
"0, 0, 0, .4"
, "74, 138, 202, .6"
, "240, 68, 58, .6"
, "254, 217, 40, .6"
];
var str;
if (0 === team_id) {
str = "<div><center>"
+ "<div>"
+ "<b style='color:rgba(" + gym_color[team_id] + ")'>" + team_name + "</b><br>"
+ "<img height='70px' style='padding: 5px;' src='static/forts/" + team_name + "_large.png'>"
+ "</div>"
+ "</center></div>";
}
else {
str = "<div><center>"
+ "<div style='padding-bottom: 2px'>Gym owned by:</div>"
+ "<div>"
+ "<b style='color:rgba(" + gym_color[team_id] + ")'>Team " + team_name + "</b><br>"
+ "<img height='70px' style='padding: 5px;' src='static/forts/" + team_name + "_large.png'>"
+ "</div>"
+ "<div>Prestige: " + gym_points + "</div>"
+ "</center></div>";
}
return str;
}
function pokestopLabel(lured, last_modified, active_pokemon_id, latitude, longitude) {
var str;
if (lured) {
var active_pokemon = opts.idToPokemon[active_pokemon_id];
var last_modified_date = new Date(last_modified);
var current_date = new Date();
var time_until_expire = current_date.getTime() - last_modified_date.getTime();
var expire_date = new Date(current_date.getTime() + time_until_expire);
var expire_time = expire_date.getTime();
str = "<div><b>Lured Pokéstop</b></div>"
+ "<div>Lured Pokémon: " + active_pokemon + "<span> - </span> <small>"
+ "<a href='http://www.pokemon.com/us/pokedex/" + active_pokemon_id + "'"
+ " target='_blank' title='View in Pokedex'>#" + active_pokemon_id + "</a></small>"
+ "</div>"
+ "<div>"
+ "Lure expires at " + pad(expire_date.getHours())
+ ":" + pad(expire_date.getMinutes()) + ":" + pad(expire_date.getSeconds())
+ "<span class='label-countdown' disappears-at='" + expire_time + "'>(00m00s)</span></div>"
+ "<div>"
+ "<div>"
+ "<a href='https://www.google.com/maps/dir/Current+Location/" + latitude + "," + longitude + "'"
+ " target='_blank' title='View in Maps'>Get directions</a>"
+ "</div>";
} else {
str = "<div><b>Pokéstop</b></div>"
+ "<div>"
+ "<a href='https://www.google.com/maps/dir/Current+Location/" + latitude + "," + longitude + "'"
+ " target='_blank' title='View in Maps'>Get directions</a>"
+ "</div>";
}
return str;
}
function updateLabelDiffTime() {
$('.label-countdown').each(function(index, element) {
var disappearsAt = new Date(parseInt(element.getAttribute("disappears-at")));
var now = new Date();
var difference = Math.abs(disappearsAt - now);
var hours = Math.floor(difference / 36e5);
var minutes = Math.floor((difference - (hours * 36e5)) / 6e4);
var seconds = Math.floor((difference - (hours * 36e5) - (minutes * 6e4)) / 1e3);
var timestring;
if (disappearsAt < now) {
timestring = "(expired)";
} else {
timestring = "(";
if (hours > 0) {
timestring = hours + "h";
}
timestring += ("0" + minutes).slice(-2) + "m";
timestring += ("0" + seconds).slice(-2) + "s";
timestring += ")";
}
$(element).text(timestring);
});
}
function setupPokemonMarker(item) {
var marker = new google.maps.Marker({
position: {
lat: item.latitude,
lng: item.longitude
},
map: STATE.map,
icon: 'static/icons/' + item.pokemon_id + '.png'
});
marker.infoWindow = new google.maps.InfoWindow({
content: pokemonLabel(item.pokemon_name, item.disappear_time, item.pokemon_id, item.latitude, item.longitude)
});
if (opts.notifiedPokemon.indexOf(item.pokemon_id) > -1) {
if(localStorage.playSound === 'true'){
audio.play();
}
sendNotification('A wild ' + item.pokemon_name + ' appeared!', 'Click to load map', 'static/icons/' + item.pokemon_id + '.png');
}
addListeners(marker);
return marker;
}
function setupGymMarker(item) {
var marker = new google.maps.Marker({
position: {
lat: item.latitude,
lng: item.longitude
},
map: STATE.map,
icon: 'static/forts/' + gym_types[item.team_id] + '.png'
});
marker.infoWindow = new google.maps.InfoWindow({
content: gymLabel(gym_types[item.team_id], item.team_id, item.gym_points)
});
addListeners(marker);
return marker;
}
function setupPokestopMarker(item) {
var imagename = item.lure_expiration ? "PstopLured" : "Pstop";
var marker = new google.maps.Marker({
position: {
lat: item.latitude,
lng: item.longitude
},
map: STATE.map,
icon: 'static/forts/' + imagename + '.png',
});
marker.infoWindow = new google.maps.InfoWindow({
content: pokestopLabel(!!item.lure_expiration, item.last_modified, item.active_pokemon_id, item.latitude, item.longitude)
});
addListeners(marker);
return marker;
}
function getColorByDate(value) {
//Changes the color from red to green over 15 mins
var diff = (Date.now() - value) / 1000 / 60 / 15;
if (diff > 1) {
diff = 1;
}
//value from 0 to 1 - Green to Red
var hue = ((1 - diff) * 120).toString(10);
return ["hsl(", hue, ",100%,50%)"].join("");
}
function addListeners(marker) {
marker.addListener('click', function() {
marker.infoWindow.open(STATE.map, marker);
updateLabelDiffTime();
marker.persist = true;
});
google.maps.event.addListener(marker.infoWindow, 'closeclick', function() {
marker.persist = null;
});
marker.addListener('mouseover', function() {
marker.infoWindow.open(STATE.map, marker);
updateLabelDiffTime();
});
marker.addListener('mouseout', function() {
if (!marker.persist) {
marker.infoWindow.close();
}
});
return marker;
}
function clearStaleMarkers() {
$.each(map_pokemons, function(key/*, value*/) {
if (map_pokemons[key].disappear_time < new Date().getTime() ||
opts.excludedPokemon.indexOf(map_pokemons[key].pokemon_id) >= 0) {
map_pokemons[key].marker.setMap(null);
delete map_pokemons[key];
}
});
$.each(map_scanned, function(key/*, value*/) {
//If older than 15mins remove
if (map_scanned[key].last_modified < (new Date().getTime() - 15 * 60 * 1000)) {
map_scanned[key].marker.setMap(null);
//console.log('delete scanned');
delete map_scanned[key];
}
});
}
function _isReady(log) {
if (log && !STATE.mapReady) {
console.log('ignore map update - google.maps object not yet loaded');
}
if (log && !(STATE.lat && STATE.lng)) {
console.log('ignore map update - location not set yet');
}
return STATE.mapReady && STATE.lat && STATE.lng;
}
function init(_google) {
google = _google || window.google;
STATE.mapReady = !!google;
if (_isReady()) {
_init();
}
}
function updateLocation(lat, lng, changeType) {
if (lat && lng && 'number' === typeof lat && 'number' === typeof lng) {
if (lat !== STATE.lat || lng !== STATE.lng) {
STATE.lat = lat;
STATE.lng = lng;
}
}
if (_isReady()) {
_init();
}
STATE.marker.setPosition(new google.maps.LatLng(STATE.lat, STATE.lng));
if ('center' === changeType) {
// http://stackoverflow.com/questions/10917648/google-maps-api-v3-recenter-the-map-to-a-marker
STATE.map.setCenter(STATE.marker.getPosition());
}
}
function updateConfig(key, val) {
STATE.cfg[key] = val;
clearTimeout(STATE.updateTimeout);
STATE.updateTimeout = setTimeout(function () {
updateMap();
}, 50);
}
function _init() {
if (STATE.map) {
return;
}
window.setInterval(updateLabelDiffTime, 1000);
$('.js-geolocation-container').hide();
$('.js-map').show();
STATE.map = new google.maps.Map(document.getElementById('map'), {
center: {
lat: STATE.lat,
lng: STATE.lng
},
zoom: 16,
fullscreenControl: true,
streetViewControl: false,
mapTypeControl: true,
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
position: google.maps.ControlPosition.RIGHT_TOP,
mapTypeIds: [
google.maps.MapTypeId.ROADMAP,
google.maps.MapTypeId.SATELLITE,
'dark_style',
'style_light2',
'style_pgo'
]
}
});
var style_dark = new google.maps.StyledMapType(darkStyle, {name: "Dark"});
STATE.map.mapTypes.set('dark_style', style_dark);
var style_light2 = new google.maps.StyledMapType(light2Style, {name: "Light2"});
STATE.map.mapTypes.set('style_light2', style_light2);
var style_pgo = new google.maps.StyledMapType(pGoStyle, {name: "PokemonGo"});
STATE.map.mapTypes.set('style_pgo', style_pgo);
STATE.map.addListener('maptypeid_changed', function(/*s*/) {
opts.onChangeConfig('mapStyle', this.mapTypeId);
});
if (!STATE.cfg.mapStyle || STATE.cfg.mapStyle === 'undefined') {
opts.onChangeConfig('mapStyle', 'roadmap');
}
STATE.map.setMapTypeId(STATE.cfg.mapStyle);
STATE.marker = new google.maps.Marker({
draggable: true,
position: {
lat: STATE.lat,
lng: STATE.lng
},
map: STATE.map,
animation: google.maps.Animation.DROP
});
google.maps.event.addListener(STATE.marker, "dragend", function (event) {
STATE.lat = event.latLng.lat();
STATE.lng = event.latLng.lng();
opts.onChangeLocation(STATE.lat, STATE.lng, 'drag');
});
google.maps.event.addListener(STATE.map, 'click', function (event) {
STATE.lat = event.latLng.lat();
STATE.lng = event.latLng.lng();
opts.onChangeLocation(STATE.lat, STATE.lng, 'click');
});
}
function processGyms(item){
if (!STATE.cfg.showGyms) {
return false; // in case the checkbox was unchecked in the meantime.
}
if (item.gym_id in map_gyms) {
// if team has changed, create new marker (new icon)
if (map_gyms[item.gym_id].team_id !== item.team_id) {
map_gyms[item.gym_id].marker.setMap(null);
map_gyms[item.gym_id].marker = setupGymMarker(item);
} else { // if it hasn't changed generate new label only (in case prestige has changed)
map_gyms[item.gym_id].marker.infoWindow = new google.maps.InfoWindow({
content: gymLabel(gym_types[item.team_id], item.team_id, item.gym_points)
});
}
}
else { // add marker to map and item to dict
if (item.marker) { item.marker.setMap(null); }
item.marker = setupGymMarker(item);
map_gyms[item.gym_id] = item;
}
}
function setupScannedMarker(item) {
//console.log('setupScannedMarker');
//console.log(item);
var circleCenter = new google.maps.LatLng(item.latitude, item.longitude);
var opts = {
map: STATE.map,
center: circleCenter,
radius: 100, // 10 miles in metres
fillColor: getColorByDate(item.last_modified),
strokeWeight: 1
};
//console.log(opts);
var marker = new google.maps.Circle(opts);
return marker;
}
function processScanned(item) {
if (!STATE.cfg.showScanned) {
//console.log('ignore scanned');
return false;
}
if (item.scanned_id in map_scanned) {
//console.log('update old scanned');
map_scanned[item.scanned_id].marker.setOptions({
fillColor: getColorByDate(item.last_modified)
});
} else { // add marker to map and item to dict
//console.log('create new scanned');
if (item.marker) { item.marker.setMap(null); }
item.marker = setupScannedMarker(item);
map_scanned[item.scanned_id] = item;
}
}
function processPokestops(item) {
if (!STATE.cfg.showPokestops) {
return false;
} else if (!(item.pokestop_id in map_pokestops)) { // add marker to map and item to dict
// add marker to map and item to dict
if (item.marker) { item.marker.setMap(null); }
item.marker = setupPokestopMarker(item);
map_pokestops[item.pokestop_id] = item;
}
}
function processPokemon(item) {
if (!STATE.cfg.showPokemon) {
return false; // in case the checkbox was unchecked in the meantime.
}
if (!(item.encounter_id in map_pokemons) &&
opts.excludedPokemon.indexOf(item.pokemon_id) < 0) {
// add marker to map and item to dict
if (item.marker) { item.marker.setMap(null); }
item.marker = setupPokemonMarker(item);
map_pokemons[item.encounter_id] = item;
}
}
function updateMap(_result) {
if (!_isReady(true)) {
return;
}
if (_result) {
STATE.result = _result;
}
if (!STATE.result) {
console.log('updateMap ignored - waiting for heartbeat');
return;
}
STATE.result.pokemons.forEach(processPokemon);
STATE.result.pokestops.forEach(processPokestops);
STATE.result.gyms.forEach(processGyms);
STATE.result.scanned.forEach(processScanned);
clearStaleMarkers();
}
return {
init: init
, setData: updateMap
, setLocation: updateLocation
, setConfig: updateConfig
// data
, pokemon: map_pokemons
, gyms: map_gyms
, scanned: map_scanned
, pokestops: map_pokestops
};
};
}(window));