Ein paar wenige Dateien und schon ist die Web-App fertig, oder?
Diese Web-App stellt folgende Funtionen zur Verfügung:
Google-Maps Kartenansicht mit Umschlatmöglichkeit zur Satelitenbild-Ansicht
Zentrierung der Karte zum aktuellen Standort
Aufnehmen der Koordinaten in eine Liste, zur Tracking-Anzeige in der Karte und zum Download als GPS-Datei im GPX Format
Löschen / Zurücksetzen der aufgenommenen Werte
Anzeige der Aktuellen Werte Longitude, Latitude, Altitude, Speed, Accuracy und Anzahl der eingehenden Werte im Verhätnis zu den aufgenommenen Werten
ungenaue als auch doppelte Werte werden ignoriert und nicht in die Werteliste aufgenommen
Es ist noch eine Funktion zur Anzeige aufgenommener Tracks bzw. von anderer Seite zur Verfügung gestelleter GPX-Dateinen angedacht, des Weiteren einige Eingabefelder zur Beschreibung der GPX-Datei. (ist noch in Arbeit)
Die komplette Definition der App wird in einer manifest Datei abgelegt, zu beachten sind hier besonders die Rechte, je nach Typ stehen auch unter Umständen nicht alle Möglichkeiten zur Verfügung. Unter Firefox OS kann man z.B. nicht beim Typ Web auf die SD-Card zugreifen, also ist später unter umständen ein anderer Weg einzuplanen, in diesem Fall der Download. Die meisten Punkte erklären sich hoffentlich von alleine.
manifest.webapp (1,02 kByte) 22.03.2020 22:35
{
"version": "0.3.0",
"name": "Gocher Maps Web App",
"description": "Firefox OS Maps Web App",
"launch_path": "/index.htm",
"icons": {
"16": "/img/icons/icon16x16.png",
"48": "/img/icons/icon48x48.png",
"60": "/img/icons/icon60x60.png",
"128": "/img/icons/icon128x128.png",
"512": "/img/icons/icon512x512.png"
},
"developer": {
"name": "Udo Schmal",
"url": "http://www.gocher.me"
},
"type": "web",
"permissions": {
"geolocation": {
"description": "Needed for the app to get positions from the device."
},
"device-storage:sdcard": { "access": "readwrite" }
},
"installs_allowed_from": [
"*"
],
"locales": {
"en": {
"description": "Firefox OS Maps Web App",
"developer": {
"name": "Udo Schmal",
"url": "http://www.gocher.me"
}
},
"de": {
"description": "Firefox OS Maps Web App",
"developer": {
"name": "Udo Schmal",
"url": "http://www.gocher.me"
}
}
},
"default_locale": "en"
}
In der index.html der eigentlichen Seite werden in dieser App lediglich die benötigten JavaScripts und Stylesheets eingebunden.
index.htm HTML (804 Bytes) 06.05.2021 22:54
<!DOCTYPE html >
<html >
<head >
<meta charset = "utf-8" />
<title > Gocher Maps Web App </title >
<meta name = "description" content = "Firefox OS Maps Web App" />
<meta name = "viewport" content = "initial-scale=1.0, user-scalable=no, width=device-width, minimum-scale=1, maximum-scale=1" />
<link rel = "stylesheet" href = "app.css" />
<meta http-equiv = "Content-Security-Policy" content = "default-src 'self' https://maps.googleapis.com/; script-src 'self' https://maps.googleapis.com/; style-src 'self' https://maps.googleapis.com/; img-src 'self' data: https://maps.googleapis.com/ " />
<script type = "text/javascript" src = "https://maps.googleapis.com/maps/api/js?v=3" > </script >
<script type = "text/javascript" src = "app.js" defer > </script >
</head >
<body role = "application" > </body >
</html >
Ein paar wenige definitionen für die Gestaltung, den gößten Teil der Datei stellen die eingebetteten Bilddaten dar.
app.css StyleSheet (7,94 kByte) 22.03.2020 22:35
[role="toolbar"] {height : 4rem ; width : 100% ; position : fixed ; bottom : 0 ; left : 0 ; z-index : 100 ; background : rgba(0,0,0, 0.85 ) ;}
[role="toolbar"] ul {float : left ; list-style : none ; padding : 0 ; margin : 0 ;}
[role="toolbar"] ul :last-child {float : right ;}
[role="toolbar"] li {float : left ;}
[role="toolbar"] button {width : 5.5rem ; height : 4rem ; border : none ; font-size : 0 ; background : transparent no-repeat 50% 50% / 3rem auto ; padding : 0 ; border-radius : 0 ;}
[role="toolbar"] button :active , [role="toolbar"] button .active {background-color : #008aaa ;}
[role="toolbar"] .pack-icon-mark {background-image : url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4NDA1RDQxQUE3MjA2ODExODhDNkE3NjAxQjM4MUZDNSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDOUMyRkJBN0REODYxMUUxQjVDNEE4QjczREVCRENENCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDOUMyRkJBNkREODYxMUUxQjVDNEE4QjczREVCRENENCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjk4MjBGQzc5QjkyMDY4MTE4OEM2QjcwQTAwNDE1NjQ1IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjg0MDVENDFBQTcyMDY4MTE4OEM2QTc2MDFCMzgxRkM1Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+hFqHHQAAAQFJREFUeNpi/P//P8NAAiaGAQajDhh1AAsFermAWB2IeYH4FhC/IMsUUDYkAvMBcTkQZwIxOxCvBOLv/xHgDxAvBWJeIs2DY2IVykEtegjE/P9xg02kOoDaacAXiGMHOhFOA2KDgXQADxDvBeJwJPNBCVaYntlQCIhXAPF9ID4MxEeA+DO1syExQA6KvYH410AVRKCQ2EbtNHAfmtr1gDgQiA/gUHcFiDOoURAhlwOggkgUi5oSIP6FVCacBmIxQmYzEtkgAcXjQyB+BMTyeNRpQ+MbpG4NEP+hZV2ADVyF4tHqeNQBw69F9AGIK4D4E7UdwDjaMRl1wKgDBtoBAAEGAJMNVePSORzwAAAAAElFTkSuQmCC ) ;}
[role="toolbar"] .pack-icon-share {background-image : url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4NDA1RDQxQUE3MjA2ODExODhDNkE3NjAxQjM4MUZDNSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDOUM5NjE4Q0REODYxMUUxQjVDNEE4QjczREVCRENENCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDOUM5NjE4QkREODYxMUUxQjVDNEE4QjczREVCRENENCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjk4MjBGQzc5QjkyMDY4MTE4OEM2QjcwQTAwNDE1NjQ1IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjg0MDVENDFBQTcyMDY4MTE4OEM2QTc2MDFCMzgxRkM1Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+14Nt4AAAAWxJREFUeNpi/P//P8NAAsZRB4w6YDA7QAuItwCxAIlm/gPiBUBcQokD2IG4B4jFyfCUG9QRQgMVBcuA2IvYkGNC47NRwQHoPrIDYl5iHACK8w4apLNWID4KxKKEHLCVzDgnBugC8V5sjkB2gCCNcxzIEfvQHcFCoaEqQBwOxIZALAf1kCIQ8wDxGagadST1OlBHOAHxa0iKAeYCKP4AxEuR+PgwFxBPBeK//8kDm2FmkRMCoBS9B4jNKAi5f5REwSwky/8C8XUgfoVsKBowQSsTLgJxCiLTkhYFFtAgBKktBWIBIqLrMFLQXwBiEWR5UkMgAYjvQovbezjU9AOxIxAboImDfO4CxG/wlYSEgDEBy0FADIgViLGcnDSwhIDl2EAVEJ8H4i/YJFnISICkgsOkVEaEwHdqF49MFDqIqi2it0DMCsQ7sVSpRJsHTWxE1y3IaWAuECcDsTOFngIVSItGW8WjDhh1ALEAIMAA+H41QW+KcxcAAAAASUVORK5CYII= ) ;}
[role="toolbar"] .pack-icon-move {background-image : url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4NDA1RDQxQUE3MjA2ODExODhDNkE3NjAxQjM4MUZDNSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDOUM5NjE4OEREODYxMUUxQjVDNEE4QjczREVCRENENCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDOUM5NjE4N0REODYxMUUxQjVDNEE4QjczREVCRENENCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjk4MjBGQzc5QjkyMDY4MTE4OEM2QjcwQTAwNDE1NjQ1IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjg0MDVENDFBQTcyMDY4MTE4OEM2QTc2MDFCMzgxRkM1Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+eL5wKQAAAVlJREFUeNpi/P//P8NAAiaGAQajDhgWDtAaSAf4AfF5IPYg2wRQNiQT6wLxx/8Q8AGI1ckxh1zLRYH4zn9UcBuIBUg1i9woCAXic0D8HMp/BI2KSHpGAQhvgfp+KblmDJlsKAjEXiSa7Q7E/NRwgAYQnwRiWxId4ADEx4FYBZ8iRgK1ISh/r4D65AQQH0STDwFiZSC+BsSb0eTsgdgCiN8DcTgQ7yY1EXoC8Z//1AG/gdgFmz0seHy/E4h7gLgcyn8BxE/R1KhAQ+cdEN9Hk5MGYgmQH4G4E4j3kpsNY4H4OxC3k5gNQeq/AXE4PvNZiEhMi4H4NhCbkZgIn0AT7ll8iliINOwEFJMCpg6J9gALmfq8oYlMDikxpkET6VZ61AUmQPwVLauB+Hr0qo5BOAKI/0EtB9GB9GwPwHAL1AG15JpBqQNARXkGlCbLDMbRntGoA0a8AwACDAA8jrVLWIU9BAAAAABJRU5ErkJggg== ) ;}
[role="toolbar"] .pack-icon-delete {background-image : url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4NDA1RDQxQUE3MjA2ODExODhDNkE3NjAxQjM4MUZDNSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpDOUMyRkJBM0REODYxMUUxQjVDNEE4QjczREVCRENENCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpDOUMyRkJBMkREODYxMUUxQjVDNEE4QjczREVCRENENCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjk4MjBGQzc5QjkyMDY4MTE4OEM2QjcwQTAwNDE1NjQ1IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjg0MDVENDFBQTcyMDY4MTE4OEM2QTc2MDFCMzgxRkM1Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+PB4wWgAAANFJREFUeNpi/P//P8NAAsZRBww1B7ACsSMQuwCxJhBzAPEPIL4OxHuAeD8Q/6amA2SB2AyImaB8OSDuAeIdQHwASZ0HEDsAcQkQP4KK/QPiU0D8GK8LQA7AgQOA+Od/ygBIvx8eO/CGwB0gVqZCNIPMUSUnCt5RMa0JDdlcsBuJ7YrGJ1bMlRIHIEsyovGJFWPEZwETwwCDUQeMOmDUAaMOGHXAkHJAGgViVKkNya7yKQmBkxRafojSEOACYlNoc5xU8BvqgR+jPaNRB+ADAAEGAKG8wdkMfRTuAAAAAElFTkSuQmCC ) ;}
html , body {width : 100% ; height : 100% ;}
html , body , #gmap {margin : 0 ; padding : 0 ; font-family : sans-serif ;}
#gmap {position : absolute ; top : 0 ; left : 0 ; width : 100% ; bottom : 66px ;}
#geoButton {position : absolute ; bottom : 15px ; right : 15px ; width : 40px ; height : 40px ;}
#status {position : absolute ; top : 5px ; right : 5px ; width : 132px ; height : 130px ; padding-left : 5px ; font-size : 9pt ; line-height : 150% ; color : white ; font-weight : bold ; background-color : black ; overflow : hidden ; opacity : 0.7 ;}
Die eigentliche Arbeit wird vom JavaScript ausgeführt, ich habe mich hier bemüht den Code kurz zu halten und keine weiteren Bibliotheken einzubinden um das Projekt überschaubar zu halten.
app.js JavaScript (9,34 kByte) 22.03.2020 22:34
'use strict' ;
function App() {
this .body = null ; this .gmap = null ; this .stat = null ; this .btnMark = null ; this .btnCenter = null ; this .wakeLock = null ; this .watchID = null ; this .counter = 0 ; this .map = null ; this .marker = null ; this .poly = null ; this .lastPos = {}; this .marks = []; this .track = []; this .gps = null ; }
App.prototype = {
start: function () {
function formatNum (s, n) {
s = String (s);
while (s.length < n) {
s = "0" + s;
}
return s;
}
function formatDeg(n) {
var deg = Math .floor( n), minutes, seconds, cents;
n = (n - Math .floor( n)) * 60 ;
minutes = Math .floor( n);
n = (n - Math .floor( n)) * 60 ;
seconds = Math .floor( n);
n = (n - Math .floor( n)) * 100 ;
cents = Math .floor( n);
return deg + "°" + formatNum(minutes, 2 ) + "'" + formatNum(seconds, 2 ) + "." + formatNum(cents, 2 ) + '"' ;
}
var self = this ;
this .btnMark.className = "pack-icon-mark active" ;
this .stat.innerHTML = 'get it ...' ;
this .poly.setMap(this .map);
this .lastPos = {'lat' : 0 , 'lon' : 0 };
this .wakeLock = navigator .requestWakeLock( 'gps' );
this .watchID = navigator .geolocation.watchPosition(
function (pos) {
var lat, lon, alt, speed, posMark, latlng, path;
++ self.counter;
lat = pos.coords.latitude;
lon = pos.coords.longitude;
alt = pos.coords.altitude;
speed = pos.coords.speed;
if (((self.lastPos.lat !== lat) || (self.lastPos.lon !== lon)) && (pos.coords.accuracy < 32 )) {
self.marks.push({'lat' : lat, 'lon' : lon});
self.lastPos = {'lat' : lat, 'lon' : lon, 'alt' : alt, 'speed' : speed, 'accuracy' : pos.coords.accuracy};
self.track.push({'lat' : lat, 'lon' : lon, 'alt' : alt, 'ts' : pos.timestamp});
}
if (! document .hidden && (self.marks.length > 0 )) {
path = self.poly.getPath();
while (self.marks.length > 0 ) {
posMark = self.marks.shift();
lat = posMark.lat;
lon = posMark.lon;
latlng = new google.maps.LatLng(lat, lon);
path.push(latlng);
}
if (latlng) {
self.marker.setPosition(latlng);
self.marker.setVisible(true );
if (self.btnCenter.className === "pack-icon-move active" ) {
self.map.panTo(latlng);
}
}
}
self.stat.innerHTML = "lat: " + ((self.lastPos.lat === null ) ? "no lat" : formatDeg(Math .abs( self.lastPos.lat)) + (self.lastPos.lat < 0 ? "S" : "N" )) + "<br />" +
"lon: " + ((self.lastPos.lon === null ) ? "no lon" : formatDeg(Math .abs( self.lastPos.lon)) + (self.lastPos.lon < 0 ? "W" : "E" )) + "<br />" +
"alt: " + ((self.lastPos.alt === null ) ? "no alt" : Math .round( self.lastPos.alt - 47.5 ) + "m NN" ) + "<br />" +
"speed: " + ((self.lastPos.speed !== null && ! isNaN(self.lastPos.speed)) ? (self.lastPos.speed * 3.6 ).toFixed(0 ) + "km/h" : "no speed" ) + "<br />" +
"accuracy: ±" + self.lastPos.accuracy + "m" + "<br />" +
"count: " + self.track.length + "/" + self.counter;
},
function (error) {
++ self.counter;
self.stat.innerHTML = 'error' + '<br />' + 'try to get it ...' ;
},
{
enableHighAccuracy: true ,
maximumAge: 3000 ,
timeout: 3000
}
);
navigator .vibrate( 200 );
},
stop: function () {
this .btnMark.className = "pack-icon-mark" ;
navigator .geolocation.clearWatch( this .watchID);
this .wakeLock.unlock();
this .marker.setVisible(false );
},
clear: function () {
var path = this .poly.getPath();
path.clear();
this .track = [];
this .marks = [];
this .counter = 0 ;
},
save: function () {
var self = this , dateStr = new Date ().toISOString(), gpx = [], blob, sdcard, request;
gpx.push('<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n' );
gpx.push('<gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="www.gocher.me">\n' );
gpx.push('<metadata><link href="http://www.gocher.me"><text>gocher.me</text></link>\n' );
gpx.push('<time>' + dateStr + '</time></metadata>\n' );
gpx.push('<trk>\n' );
gpx.push(' <trkseg>\n' );
this .track.forEach(
function (pos, i) {
gpx.push(' <trkpt lat="' + pos.lat + '" lon="' + pos.lon + '">' +
((pos.alt !== undefined) ? ('<ele>' + pos.alt + '</ele>' ) : '' ) +
'<time>' + new Date (pos.ts).toISOString() + '</time>' +
'</trkpt>\n' );
});
gpx.push(' </trkseg>\n' );
gpx.push('</trk>\n' );
gpx.push('</gpx>\n' );
blob = new Blob (gpx, {'type' : 'application/gpx+xml' });
sdcard = navigator .getDeviceStorage( "sdcard" );
request = sdcard.addNamed(blob, 'tracks/' + dateStr.replace('/' , '-' ).replace(':' , '-' ) + '.gpx' );
request.onsuccess = function () {
var name = this .result;
alert('File "' + name + '" successfully wrote on the sdcard storage area' );
};
request.onerror = function () {
if (this .error.name === 'SecurityError' ) {
blob.name = dateStr.replace('/' , '-' ).replace(':' , '-' ) + '.gpx' ;
self.stat.appendChild(document .createElement( 'br' ));
var elem = document .createElement( 'a' ),
gpxUrl = URL .createObjectURL( blob);
elem.setAttribute('href' , gpxUrl);
elem.setAttribute('download' , blob.name);
self.stat.appendChild(elem);
elem.appendChild(document .createTextNode( 'download' ));
} else {
alert('Unable to write the file: ' + this .error.name);
}
};
},
addToolbar: function () {
function addButton(className, caption, ul) {
var li, btn;
li = document .createElement( 'li' );
ul.appendChild(li);
btn = document .createElement( 'button' );
li.appendChild(btn);
btn.className = className;
btn.appendChild(document .createTextNode( caption));
return btn;
}
var self = this , toolbar, ul, btnDelete, btnShare;
toolbar = document .createElement( 'div' );
toolbar.setAttribute('role' , "toolbar" );
this .body.appendChild(toolbar);
ul = document .createElement( 'ul' );
toolbar.appendChild(ul);
btnDelete = addButton("pack-icon-delete" , "Delete" , ul);
btnDelete.onclick = function () {
self.clear();
};
ul = document .createElement( 'ul' );
toolbar.appendChild(ul);
this .btnMark = addButton("pack-icon-mark" , "Mark" , ul);
this .btnMark.onclick = function () {
if (self.btnMark.className === "pack-icon-mark" ) {
self.start();
} else {
self.stop();
}
};
this .btnCenter = addButton("pack-icon-move" , "Move" , ul);
this .btnCenter.onclick = function () {
if (self.btnCenter.className === "pack-icon-move" ) {
self.btnCenter.className = "pack-icon-move active" ;
} else {
self.btnCenter.className = "pack-icon-move" ;
}
};
btnShare = addButton("pack-icon-share" , "Share" , ul);
btnShare.onclick = function () {
self.save();
};
},
init: function () {
this .body = document .getElementsByTagName( 'body' )[0 ];
this .gmap = document .createElement( 'div' );
this .gmap.setAttribute('id' , 'gmap' );
this .body.appendChild(this .gmap);
this .stat = document .createElement( "div" );
this .stat.setAttribute('id' , 'status' );
this .body.appendChild(this .stat);
this .addToolbar();
this .map = new google.maps.Map (
this .gmap, {
zoom: 17 ,
zoomControl: false ,
streetViewControl: false ,
scrollwheel: false ,
mapTypeControl: true ,
keyboardShortcuts: false ,
mapMaker: false ,
noClear: true ,
overviewMapControl: false ,
rotateControl: false ,
disableDefaultUI: true ,
center: new google.maps.LatLng(51.528710 , 6.289250 ),
mapTypeId: google.maps.MapTypeId.TERRAIN
}
);
var symbol = {
path: 'M0 0 a4 4 0 1 1 0 0.0001 z' ,
fillColor: 'red' ,
fillOpacity: 0.6 ,
scale: 1 ,
strokeColor: 'black' ,
strokeWeight: 1
};
this .marker = new google.maps.Marker({
position: new google.maps.LatLng(0 , 0 ),
map: this .map,
icon: symbol
});
this .poly = new google.maps.Polyline({
strokeColor: '#FF0000' ,
strokeOpacity: 0.5 ,
strokeWeight: 3
});
this .start();
}
};
window .addEventListener( 'DOMContentLoaded' , function () {
var app = new App();
app.init();
});