/* μlogger
*
* Copyright(C) 2017 Bartek Fabiszewski (www.fabiszewski.net)
*
* This is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
// google maps
/** @namespace */
var uLogger = uLogger || {};
/** @namespace */
uLogger.mapAPI = uLogger.mapAPI || {};
/** @namespace */
uLogger.mapAPI.gmaps = (function(ns) {
/** @type {google.maps.Map} */
var map;
/** @type {google.maps.Polyline[]} */
var polies = [];
/** @type {google.maps.Marker[]} */
var markers = [];
/** @type {google.maps.InfoWindow[]} */
var popups = [];
/** @type {google.maps.InfoWindow} */
var popup;
/** @type {google.maps.PolylineOptions} */
var polyOptions;
/** @type {google.maps.MapOptions} */
var mapOptions;
/** @type {number} */
var timeoutHandle;
var name = 'gmaps';
var isLoaded = false;
var authError = false;
/**
* Initialize map
*/
function init() {
var url = '//maps.googleapis.com/maps/api/js?' + ((ns.config.gkey != null) ? ('key=' + ns.config.gkey + '&') : '') + 'callback=uLogger.mapAPI.gmaps.setLoaded';
ns.addScript(url, 'mapapi_gmaps');
if (!isLoaded) {
throw new Error("Google Maps API not ready");
}
start();
}
/**
* Start map engine when loaded
*/
function start() {
if (authError) {
gm_authFailure();
return;
}
google.maps.visualRefresh = true;
// noinspection JSValidateTypes
polyOptions = {
strokeColor: ns.config.strokeColor,
strokeOpacity: ns.config.strokeOpacity,
strokeWeight: ns.config.strokeWeight
};
// noinspection JSValidateTypes
mapOptions = {
center: new google.maps.LatLng(ns.config.init_latitude, ns.config.init_longitude),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP,
scaleControl: true
};
map = new google.maps.Map(ns.ui.map, mapOptions);
}
/**
* Clean up API
*/
function cleanup() {
polies = [];
markers = [];
popups = [];
map = null;
polyOptions = null;
mapOptions = null;
popup = null;
ns.clearMapCanvas();
}
/**
* Display track
* @param {HTMLCollection} positions XML element
* @param {boolean} update Should fit bounds if true
*/
function displayTrack(positions, update) {
var totalMeters = 0;
var totalSeconds = 0;
// init polyline
var poly = new google.maps.Polyline(polyOptions);
poly.setMap(map);
var path = poly.getPath();
var latlngbounds = new google.maps.LatLngBounds();
var posLen = positions.length;
for (var i = 0; i < posLen; i++) {
var p = ns.parsePosition(positions[i], i);
totalMeters += p.distance;
totalSeconds += p.seconds;
p.totalMeters = totalMeters;
p.totalSeconds = totalSeconds;
// set marker
setMarker(p, i, posLen);
// update polyline
var coordinates = new google.maps.LatLng(p.latitude, p.longitude);
path.push(coordinates);
latlngbounds.extend(coordinates);
}
if (update) {
map.fitBounds(latlngbounds);
if (i === 1) {
// only one point, zoom out
var zListener =
google.maps.event.addListenerOnce(map, 'bounds_changed', function () {
if (this.getZoom()) {
this.setZoom(15);
}
});
setTimeout(function () { google.maps.event.removeListener(zListener) }, 2000);
}
}
polies.push(poly);
ns.updateSummary(p.timestamp, totalMeters, totalSeconds);
if (p.tid !== ns.config.trackid) {
ns.config.trackid = p.tid;
ns.setTrack(ns.config.trackid);
}
ns.updateChart();
}
/**
* Clear map
*/
function clearMap() {
if (polies) {
for (var i = 0; i < polies.length; i++) {
polies[i].setMap(null);
}
}
if (markers) {
for (var j = 0; j < markers.length; j++) {
google.maps.event.removeListener(popups[j].listener);
popups[j].setMap(null);
markers[j].setMap(null);
}
}
markers.length = 0;
polies.length = 0;
popups.lentgth = 0;
}
/**
* Set marker
* @param {uLogger.Position} pos
* @param {number} id
* @param {number} posLen
*/
function setMarker(pos, id, posLen) {
// marker
// noinspection JSCheckFunctionSignatures
var marker = new google.maps.Marker({
position: new google.maps.LatLng(pos.latitude, pos.longitude),
title: (new Date(pos.timestamp * 1000)).toLocaleString(),
map: map
});
if (ns.isLatest()) {
marker.setIcon('images/marker-red.png');
} else if (id === 0) {
marker.setIcon('images/marker-green.png');
} else if (id === posLen - 1) {
marker.setIcon('images/marker-red.png');
} else {
marker.setIcon('images/marker-white.png');
}
// popup
var content = ns.getPopupHtml(pos, id, posLen);
popup = new google.maps.InfoWindow();
// noinspection JSUndefinedPropertyAssignment
popup.listener = google.maps.event.addListener(marker, 'click', (function (_marker, _content) {
return function () {
popup.setContent(_content);
popup.open(map, _marker);
ns.chartShowPosition(id);
}
})(marker, content));
markers.push(marker);
popups.push(popup);
}
/**
* Add listener on chart to show position on map
* @param {google.visualization.LineChart} chart
* @param {google.visualization.DataTable} data
*/
function addChartEvent(chart, data) {
google.visualization.events.addListener(chart, 'select', function () {
if (popup) { popup.close(); clearTimeout(timeoutHandle); }
var selection = chart.getSelection()[0];
if (selection) {
var id = data.getValue(selection.row, 0) - 1;
var icon = markers[id].getIcon();
markers[id].setIcon('images/marker-gold.png');
timeoutHandle = setTimeout(function () { markers[id].setIcon(icon); }, 2000);
}
});
}
/**
* Get map bounds
* eg. ((52.20105108685229, 20.789387865580238), (52.292069558807135, 21.172192736185707))
* @returns {number[]} Bounds
*/
function getBounds() {
var bounds = map.getBounds();
var lat_sw = bounds.getSouthWest().lat();
var lon_sw = bounds.getSouthWest().lng();
var lat_ne = bounds.getNorthEast().lat();
var lon_ne = bounds.getNorthEast().lng();
return [lon_sw, lat_sw, lon_ne, lat_ne];
}
/**
* Zoom to track extent
*/
function zoomToExtent() {
var latlngbounds = new google.maps.LatLngBounds();
for (var i = 0; i < markers.length; i++) {
var coordinates = new google.maps.LatLng(markers[i].position.lat(), markers[i].position.lng());
latlngbounds.extend(coordinates);
}
map.fitBounds(latlngbounds);
}
/**
* Zoom to bounds
* @param {number[]} bounds
*/
function zoomToBounds(bounds) {
var sw = new google.maps.LatLng(bounds[1], bounds[0]);
var ne = new google.maps.LatLng(bounds[3], bounds[2]);
var latLngBounds = new google.maps.LatLngBounds(sw, ne);
map.fitBounds(latLngBounds);
}
/**
* Update size
*/
function updateSize() {
// ignore for google API
}
return {
name: name,
init: init,
setLoaded: function () { isLoaded = true; },
cleanup: cleanup,
displayTrack: displayTrack,
clearMap: clearMap,
setMarker: setMarker,
addChartEvent: addChartEvent,
getBounds: getBounds,
zoomToExtent: zoomToExtent,
zoomToBounds: zoomToBounds,
updateSize: updateSize
}
})(uLogger);
/**
* Callback for Google Maps API
* It will be called when authentication fails
*/
function gm_authFailure() {
uLogger.mapAPI.gmaps.authError = true;
var message = uLogger.sprintf(uLogger.lang.strings['apifailure'], 'Google Maps');
message += '
' + uLogger.lang.strings['gmauthfailure'];
message += '
' + uLogger.lang.strings['gmapilink'];
uLogger.ui.showModal(message);
}