tracman-server/static/js/map.js

465 lines
12 KiB
JavaScript

'use strict';
/* global mapuser userid disp noHeader mapKey navigator $ token */
import io from 'socket.io-client';
import $ from 'jquery';
import loadGoogleMapsAPI from 'load-google-maps-api';
// Variables
var map, marker, elevator, newLoc;
const mapElem = document.getElementById('map'),
socket = io('//'+window.location.hostname);
// Convert to feet if needed
function metersToFeet(meters){
//console.log('metersToFeet('+meters+')')
return (mapuser.settings.units=='standard')? (meters*3.28084).toFixed(): meters.toFixed()
}
// socket.io stuff
socket
.on('connect', function(){
console.log("Connected!");
// Can get location
socket.emit('can-get', mapuser._id );
// Can set location too
if (mapuser._id===userid) {
socket.emit('can-set', userid );
}
})
.on('disconnect', function(){
console.log("Disconnected!");
})
.on('error', function (err){
console.error('❌️',err.message);
});
// Show/hide map if location is set/unset
function toggleMaps(loc) {
if (loc.lat===0&&loc.lon===0) {
$('#map').hide();
$('#view').hide();
$('#notset').show();
}
else {
$('#map').show();
$('#view').show();
$('#notset').hide();
}
}
// Toggle maps on page load
$(function() {
toggleMaps(mapuser.last);
// Controls
var wpid, newloc;
// Set location
$('#set-loc').click(function(){
if (!userid===mapuser._id){ alert('You are not logged in! '); }
else { if (!navigator.geolocation){ alert('Geolocation not enabled. '); }
else { navigator.geolocation.getCurrentPosition(
// Success callback
function(pos){
var newloc = {
ts: Date.now(),
tok: token,
usr: userid,
alt: pos.coords.altitude,
lat: pos.coords.latitude,
lon: pos.coords.longitude,
spd: (pos.coords.speed||0)
};
socket.emit('set', newloc);
toggleMaps(newloc);
console.log('Set location:',newloc.lat+", "+newloc.lon);
},
// Error callback
function(err) {
alert("Unable to set location.");
console.error('❌️',err.message);
},
// Options
{ enableHighAccuracy:true }
); } }
});
// Track location
$('#track-loc').click(function(){
if (!userid===mapuser._id) { alert('You are not logged in! '); }
else {
// Start tracking
if (!wpid) {
if (!navigator.geolocation) { alert('Unable to track location. '); }
else {
$('#track-loc').html('<i class="fa fa-crosshairs fa-spin"></i>Stop').prop('title',"Click here to stop tracking your location. ");
wpid = navigator.geolocation.watchPosition(
// Success callback
function(pos) {
newloc = {
ts: Date.now(),
tok: token,
usr: userid,
lat: pos.coords.latitude,
lon: pos.coords.longitude,
alt: pos.coords.altitude,
spd: (pos.coords.speed||0)
}
socket.emit('set',newloc)
toggleMaps(newloc)
console.log('Set location:',newloc.lat+", "+newloc.lon)
},
// Error callback
function(err){
alert("Unable to track location.");
console.error(err.message);
},
// Options
{ enableHighAccuracy:true }
);
}
}
// Stop tracking
else {
$('#track-loc').html('<i class="fa fa-crosshairs"></i>Track').prop('title',"Click here to track your location. ");
navigator.geolocation.clearWatch(wpid);
wpid = undefined;
}
}
});
// Clear location
$('#clear-loc').click(function(){
if (!userid===mapuser._id) { alert('You are not logged in! '); }
else {
// Stop tracking
if (wpid) {
$('#track-loc').html('<i class="fa fa-crosshairs"></i>Track');
navigator.geolocation.clearWatch(wpid);
wpid = undefined;
}
// Clear location
newloc = {
ts: Date.now(),
tok: token,
usr: userid,
lat:0, lon:0, spd:0
}; socket.emit('set',newloc);
// Turn off map
toggleMaps(newloc);
console.log('Cleared location');
}
});
});
// Load google maps API
loadGoogleMapsAPI({ key:mapKey })
.then( function(googlemaps) {
// Create map
if (disp!=='1') {
// Create map and marker elements
map = new googlemaps.Map( mapElem, {
center: {
lat: mapuser.last.lat,
lng: mapuser.last.lon
},
panControl: false,
scrollwheel: true,
scaleControl: (mapuser.settings.showScale)?true:false,
draggable: false,
zoom: mapuser.settings.defaultZoom,
streetViewControl: false,
zoomControlOptions: {position: googlemaps.ControlPosition.LEFT_TOP},
mapTypeId: (mapuser.settings.defaultMap=='road')?googlemaps.MapTypeId.ROADMAP:googlemaps.MapTypeId.HYBRID
});
marker = new googlemaps.Marker({
position: { lat:mapuser.last.lat, lng:mapuser.last.lon },
title: mapuser.name,
icon: (mapuser.settings.marker)?'/static/img/marker/'+mapuser.settings.marker+'.png':'/static/img/marker/red.png',
map: map,
draggable: false
});
map.addListener('zoom_changed',function(){
map.setCenter(marker.getPosition());
});
// Create iFrame logo
if (noHeader!=='0' && mapuser._id!=='demo') {
const logoDiv = document.createElement('div');
logoDiv.id = 'map-logo';
logoDiv.innerHTML = '<a href="https://tracman.org/">'+
'<img src="https://tracman.org/static/img/style/logo-28.png" alt="[]">'+
"<span class='text'>Tracman</span></a>";
map.controls[googlemaps.ControlPosition.BOTTOM_LEFT].push(logoDiv);
}
// Create update time block
const timeDiv = document.createElement('div');
timeDiv.id = 'timestamp';
if (mapuser.last.time) {
timeDiv.innerHTML = 'location updated '+new Date(mapuser.last.time).toLocaleString();
}
map.controls[googlemaps.ControlPosition.RIGHT_BOTTOM].push(timeDiv);
// Create speed block
if (mapuser.settings.showSpeed) {
const speedSign = document.createElement('div'),
speedLabel = document.createElement('div'),
speedText = document.createElement('div'),
speedUnit = document.createElement('div');
speedLabel.id = 'spd-label';
speedLabel.innerHTML = 'SPEED';
speedText.id = 'spd';
speedText.innerHTML = (mapuser.settings.units=='standard')?(parseFloat(mapuser.last.spd)*2.23694).toFixed():mapuser.last.spd.toFixed();
speedUnit.id = 'spd-unit';
speedUnit.innerHTML = (mapuser.settings.units=='standard')?'m.p.h.':'k.p.h.';
speedSign.id = 'spd-sign';
speedSign.appendChild(speedLabel);
speedSign.appendChild(speedText);
speedSign.appendChild(speedUnit);
map.controls[googlemaps.ControlPosition.TOP_RIGHT].push(speedSign);
}
// Create altitude block
if (mapuser.settings.showAlt) {
elevator = new googlemaps.ElevationService;
const altitudeSign = document.createElement('div'),
altitudeLabel = document.createElement('div'),
altitudeText = document.createElement('div'),
altitudeUnit = document.createElement('div');
altitudeLabel.id = 'alt-label';
altitudeText.id = 'alt';
altitudeUnit.id = 'alt-unit';
altitudeSign.id = 'alt-sign';
altitudeText.innerHTML = '';
altitudeLabel.innerHTML = 'ALTITUDE';
parseAlt(mapuser.last).then( function(alt){
altitudeText.innerHTML = metersToFeet(alt)
}).catch( function(err){
console.error("Could not load altitude from last known location: ",err)
});
altitudeUnit.innerHTML = (mapuser.settings.units=='standard')?'feet':'meters';
altitudeSign.appendChild(altitudeLabel);
altitudeSign.appendChild(altitudeText);
altitudeSign.appendChild(altitudeUnit);
map.controls[googlemaps.ControlPosition.TOP_RIGHT].push(altitudeSign);
}
}
// Create streetview
if (disp!=='0' && mapuser.settings.showStreetview) {
updateStreetView(parseLoc(mapuser.last),10);
}
// Get altitude from Google API
function getAlt(loc){
return new Promise( function(resolve,reject){
// Get elevator service
elevator = elevator || new googlemaps.ElevationService;
return elevator.getElevationForLocations({
// Query API
'locations': [{ lat:loc.lat, lng:loc.lon }]
}, function(results, status, error_message) {
// Success; return altitude
if (status === googlemaps.ElevationStatus.OK && results[0]) {
console.log("Altitude was retrieved from Google Elevations API as",results[0].elevation,'m')
resolve( results[0].elevation )
}
// Unable to get any altitude
else {
reject(Error(error_message))
}
});
})
}
// Parse altitude
function parseAlt(loc){
//console.log('parseAlt('+loc+'})')
return new Promise( function(resolve,reject){
// Check if altitude was provided
if (typeof loc.alt=='number'){
console.log('Altitude was provided in loc as ',loc.alt,'m')
resolve(loc.alt)
}
// No altitude provided
else {
console.log('No altitude was provided in loc')
// Query google altitude API
getAlt(loc).then( function(alt){
resolve(alt)
}).catch( function (err) {
reject(err)
})
}
})
}
// Parse location
function parseLoc(loc) {
loc.spd = (mapuser.settings.units=='standard')?parseFloat(loc.spd)*2.23694:parseFloat(loc.spd);
loc.dir = parseFloat(loc.dir);
loc.lat = parseFloat(loc.lat);
loc.lon = parseFloat(loc.lon);
//loc.alt = parseAlt(loc);
loc.tim = new Date(loc.tim).toLocaleString();
return loc;
}
// Got location
socket.on('get', function(loc) {
console.log("Got location:",loc.lat+", "+loc.lon);
// Parse location
newLoc = parseLoc(loc);
// Update map
if (disp!=='1') {
//console.log('Updating map...')
// Update time
$('#timestamp').text('location updated '+newLoc.tim);
// Update marker and map center
googlemaps.event.trigger(map,'resize');
map.setCenter({ lat:newLoc.lat, lng:newLoc.lon });
marker.setPosition({ lat:newLoc.lat, lng:newLoc.lon });
// Update speed
if (mapuser.settings.showSpeed) {
$('#spd').text( newLoc.spd.toFixed() );
}
// Update altitude
if (mapuser.settings.showAlt) {
//console.log('updating altitude...');
parseAlt(loc).then(function(alt){
$('#alt').text( metersToFeet(alt) )
}).catch(function(err){
$('#alt').text( '????' )
console.error(err);
})
}
}
// Update street view
if (disp!=='0' && mapuser.settings.showStreetview) {
updateStreetView(newLoc,10);
}
});
// Get street view imagery
function getStreetViewData(loc,rad,cb) {
// Ensure that the location hasn't changed (or this is the initial setting)
if ( newLoc == null || loc.tim===newLoc.tim ) {
if (!sv) { var sv=new googlemaps.StreetViewService(); }
sv.getPanorama({
location: {
lat: loc.lat,
lng: loc.lon
},
radius: rad
}, function(data,status){ switch (status){
// Success
case googlemaps.StreetViewStatus.OK:
cb(data);
break;
// No results in that radius
case googlemaps.StreetViewStatus.ZERO_RESULTS:
// Try again with a bigger radius
getStreetViewData(loc,rad*2,cb);
break;
// Error
default:
console.error(new Error('❌️ Street view not available: '+status).message);
} });
}
}
// Update streetview
function updateStreetView(loc) {
// Calculate bearing between user and position of streetview image
// https://stackoverflow.com/a/26609687/3006854
function getBearing(userLoc, imageLoc) {
return 90-(
Math.atan2( userLoc.lat-imageLoc.latLng.lat(), userLoc.lon-imageLoc.latLng.lng() )
* (180/Math.PI) ) % 360;
}
// Get dimensions for sv request (images proportional to element up to 640x640)
function getDimensions(element) {
// Window is smaller than max
if ( element.width()<640 && element.height()<640 ){
return element.width().toFixed()+'x'+element.height().toFixed();
}
// Width must be made proportional to 640
else if (element.width()>element.height()) {
return '640x'+(element.height()*640/element.width()).toFixed();
}
// Height must be made proportional to 640
else {
return (element.width()*640/element.height()).toFixed()+'x640';
}
}
// Set image
getStreetViewData(loc, 2, function(data){
$('#viewImg').attr('src','https://maps.googleapis.com/maps/api/streetview?'+
'size='+ getDimensions($('#view')) +
'&location='+ data.location.latLng.lat() +','+ data.location.latLng.lng() +
'&fov=90' + // Inclination
// Show direction if moving, point to user if stationary
'&heading='+ ( (loc.spd>2)? loc.dir: getBearing(loc,data.location) ).toString() +
'&key='+ mapKey
);
});
}
// Error loading gmaps API
}).catch( function(err) {
console.error(err);
});