468 lines
14 KiB
JavaScript
468 lines
14 KiB
JavaScript
'use strict'
|
|
/* global alert 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')
|
|
const socket = io('//' + window.location.hostname)
|
|
const IDLE_TIMEOUT = 300 // 5 minutes in seconds
|
|
var _idleSecondsCounter = 0
|
|
|
|
// Idle timeout listeners
|
|
function resetIdleSecondsCounter () {
|
|
_idleSecondsCounter = 0
|
|
}
|
|
document.onclick = resetIdleSecondsCounter
|
|
document.onmousemove = resetIdleSecondsCounter
|
|
document.onkeypress = resetIdleSecondsCounter
|
|
|
|
// Disconnect socket.io if user is idle for longer than IDLE_TIMEOUT seconds
|
|
window.setInterval( function CheckIdleTime () {
|
|
_idleSecondsCounter++
|
|
// Disconnect idle user if still connected
|
|
if (_idleSecondsCounter >= IDLE_TIMEOUT) {
|
|
if (socket.connected) {
|
|
console.log('Disconnecting because idle for more than',IDLE_TIMEOUT,'seconds.')
|
|
socket.disconnect()
|
|
}
|
|
// Connect user if disconnected
|
|
} else {
|
|
if (!socket.connected) {
|
|
console.log('Reconnecting the user because they are no longer idle.')
|
|
socket.connect()
|
|
}
|
|
}
|
|
}, 1000)
|
|
|
|
// 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.stack)
|
|
})
|
|
|
|
// 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()
|
|
}
|
|
}
|
|
|
|
// On page load
|
|
$(function () {
|
|
|
|
toggleMaps(mapuser.last)
|
|
|
|
// Controls
|
|
var wpid, newloc
|
|
|
|
// Set location
|
|
$('#set-loc').click(function () {
|
|
|
|
// Check if logged in and enabled
|
|
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.stack)
|
|
},
|
|
|
|
// Options
|
|
{ enableHighAccuracy: true }
|
|
|
|
)
|
|
}
|
|
}
|
|
})
|
|
|
|
// Track location
|
|
$('#track-loc').click(function () {
|
|
|
|
// Check for login
|
|
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.stack)
|
|
},
|
|
|
|
// 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),
|
|
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')
|
|
const speedLabel = document.createElement('div')
|
|
const speedText = document.createElement('div')
|
|
const 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')
|
|
const altitudeLabel = document.createElement('div')
|
|
const altitudeText = document.createElement('div')
|
|
const 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()
|
|
// Query API
|
|
return elevator.getElevationForLocations({
|
|
'locations': [{ lat: loc.lat, lng: loc.lon }]
|
|
}, function (results, status, errorMessage) {
|
|
// 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(errorMessage))
|
|
})
|
|
})
|
|
}
|
|
|
|
// 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.stack)
|
|
})
|
|
}
|
|
}
|
|
|
|
// 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 : String(getBearing(loc, data.location))) +
|
|
'&key=' + mapKey
|
|
)
|
|
})
|
|
}
|
|
|
|
// Error loading gmaps API
|
|
}).catch(function (err) {
|
|
console.error(err.stack)
|
|
})
|