2021-01-06 16:05:01 +00:00
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div id="tracks"></div>
|
|
|
|
|
|
|
|
<script src="../../core/lib/interface.js"></script>
|
|
|
|
<script>
|
|
|
|
/* TODO: Calculate cadence from step count */
|
|
|
|
var domTracks = document.getElementById("tracks");
|
|
|
|
|
|
|
|
function saveKML(track,title) {
|
|
|
|
var kml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2">
|
|
|
|
<Document>
|
|
|
|
<Schema id="schema">
|
|
|
|
<gx:SimpleArrayField name="heartrate" type="int">
|
|
|
|
<displayName>Heart Rate</displayName>
|
|
|
|
</gx:SimpleArrayField>
|
|
|
|
<gx:SimpleArrayField name="steps" type="int">
|
|
|
|
<displayName>Step Count</displayName>
|
|
|
|
</gx:SimpleArrayField>
|
|
|
|
<gx:SimpleArrayField name="distance" type="float">
|
|
|
|
<displayName>Distance</displayName>
|
|
|
|
</gx:SimpleArrayField>
|
|
|
|
<gx:SimpleArrayField name="cadence" type="int">
|
|
|
|
<displayName>Cadence</displayName>
|
|
|
|
</gx:SimpleArrayField>
|
|
|
|
</Schema>
|
|
|
|
<Folder>
|
|
|
|
<name>Tracks</name>
|
|
|
|
<Placemark>
|
|
|
|
<name>${title}</name>
|
|
|
|
<gx:Track>
|
|
|
|
${track.map(pt=>` <when>${pt.date.toISOString()}</when>\n`).join("")}
|
|
|
|
${track.map(pt=>` <gx:coord>${pt.lon} ${pt.lat} ${pt.alt}</gx:coord>\n`).join("")}
|
|
|
|
<ExtendedData>
|
|
|
|
<SchemaData schemaUrl="#schema">
|
|
|
|
<gx:SimpleArrayData name="heartrate">
|
|
|
|
${track.map(pt=>` <gx:value>${pt.heartrate}</gx:value>\n`).join("")}
|
|
|
|
</gx:SimpleArrayData>
|
|
|
|
<gx:SimpleArrayData name="steps">
|
|
|
|
${track.map(pt=>` <gx:value>${pt.steps}</gx:value>\n`).join("")}
|
|
|
|
</gx:SimpleArrayData>
|
|
|
|
<gx:SimpleArrayData name="distance">
|
|
|
|
${track.map(pt=>` <gx:value>${pt.distance}</gx:value>\n`).join("")}
|
|
|
|
</gx:SimpleArrayData>
|
|
|
|
</SchemaData>
|
|
|
|
</ExtendedData>
|
|
|
|
</gx:Track>
|
|
|
|
</Placemark>
|
|
|
|
</Folder>
|
|
|
|
</Document>
|
|
|
|
</kml>`;
|
|
|
|
var a = document.createElement("a"),
|
|
|
|
file = new Blob([kml], {type: "application/vnd.google-earth.kml+xml"});
|
|
|
|
var url = URL.createObjectURL(file);
|
|
|
|
a.href = url;
|
|
|
|
a.download = title+".kml";
|
|
|
|
document.body.appendChild(a);
|
|
|
|
a.click();
|
|
|
|
setTimeout(function() {
|
|
|
|
document.body.removeChild(a);
|
|
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
}, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
function saveGPX(track, title) {
|
|
|
|
var gpx = `<?xml version="1.0" encoding="UTF-8"?>
|
|
|
|
<gpx creator="Bangle.js" version="1.1" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3">
|
|
|
|
<metadata>
|
|
|
|
<time>${track[0].date.toISOString()}</time>
|
|
|
|
</metadata>
|
|
|
|
<trk>
|
|
|
|
<name>${title}</name>
|
|
|
|
<trkseg>`;
|
|
|
|
track.forEach(pt=>{
|
|
|
|
gpx += `
|
|
|
|
<trkpt lat="${pt.lat}" lon="${pt.lon}">
|
|
|
|
<ele>${pt.alt}</ele>
|
|
|
|
<time>${pt.date.toISOString()}</time>
|
|
|
|
<extensions>
|
|
|
|
<gpxtpx:TrackPointExtension>
|
|
|
|
<gpxtpx:hr>${pt.heartrate}</gpxtpx:hr>
|
|
|
|
<gpxtpx:distance>${pt.distance}</gpxtpx:distance>
|
|
|
|
${/* <gpxtpx:cad>65</gpxtpx:cad> */""}
|
|
|
|
</gpxtpx:TrackPointExtension>
|
|
|
|
</extensions>
|
|
|
|
</trkpt>`;
|
|
|
|
});
|
|
|
|
gpx += `
|
|
|
|
</trkseg>
|
|
|
|
</trk>
|
|
|
|
</gpx>`;
|
|
|
|
var a = document.createElement("a"),
|
|
|
|
file = new Blob([gpx], {type: "application/gpx+xml"});
|
|
|
|
var url = URL.createObjectURL(file);
|
|
|
|
a.href = url;
|
|
|
|
a.download = title+".gpx";
|
|
|
|
document.body.appendChild(a);
|
|
|
|
a.click();
|
|
|
|
setTimeout(function() {
|
|
|
|
document.body.removeChild(a);
|
|
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
}, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
function trackLineToObject(l, hasFileName) {
|
|
|
|
// "timestamp,latitude,longitude,altitude,duration,distance,heartrate,steps\n"
|
|
|
|
var t = l.trim().split(",");
|
|
|
|
var n = hasFileName ? 1 : 0;
|
|
|
|
var o = {
|
2021-02-01 16:03:27 +00:00
|
|
|
invalid : t.length < 8,
|
2021-01-06 16:05:01 +00:00
|
|
|
date : new Date(parseInt(t[n+0])),
|
|
|
|
lat : parseFloat(t[n+1]),
|
|
|
|
lon : parseFloat(t[n+2]),
|
|
|
|
alt : parseFloat(t[n+3]),
|
|
|
|
duration : parseFloat(t[n+4]),
|
|
|
|
distance : parseFloat(t[n+5]),
|
|
|
|
heartrate : parseInt(t[n+6]),
|
|
|
|
steps : parseInt(t[n+7]),
|
|
|
|
};
|
|
|
|
if (hasFileName)
|
|
|
|
o.filename = t[0];
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
|
|
|
function downloadTrack(trackid, callback) {
|
|
|
|
Util.showModal("Downloading Track...");
|
|
|
|
Util.readStorageFile(trackid, data=>{
|
|
|
|
Util.hideModal();
|
|
|
|
var trackLines = data.trim().split("\n");
|
|
|
|
trackLines.shift(); // remove first line, which is column header
|
|
|
|
// should be:
|
|
|
|
// "timestamp,latitude,longitude,altitude,duration,distance,heartrate,steps\n"
|
|
|
|
var track = trackLines.map(l=>trackLineToObject(l,false));
|
|
|
|
callback(track);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
function getTrackList() {
|
|
|
|
Util.showModal("Loading Tracks...");
|
|
|
|
domTracks.innerHTML = "";
|
2021-02-04 08:11:21 +00:00
|
|
|
Puck.eval(`require("Storage").list(/banglerun_.*\\x01/).map(fn=>{fn=fn.slice(0,-1);var f=require("Storage").open(fn,"r");f.readLine();return fn+","+f.readLine()})`,trackLines=>{
|
2021-01-06 16:05:01 +00:00
|
|
|
var html = `<div class="container">
|
|
|
|
<div class="columns">\n`;
|
|
|
|
trackLines.forEach(l => {
|
|
|
|
var track = trackLineToObject(l, true /*has filename*/);
|
|
|
|
html += `
|
|
|
|
<div class="column col-12">
|
|
|
|
<div class="card-header">
|
|
|
|
<div class="card-title h5">Track ${track.filename}</div>
|
2021-02-01 16:03:27 +00:00
|
|
|
<div class="card-subtitle text-gray">${track.invalid ? "No Data":track.date.toString().substr(0,24)}</div>
|
2021-01-06 16:05:01 +00:00
|
|
|
</div>
|
2021-02-01 16:03:27 +00:00
|
|
|
${track.invalid?``:`<div class="card-image">
|
2021-01-06 16:05:01 +00:00
|
|
|
<iframe
|
|
|
|
width="100%"
|
|
|
|
height="250"
|
|
|
|
frameborder="0" style="border:0"
|
|
|
|
src="https://www.google.com/maps/embed/v1/place?key=AIzaSyBxTcwrrVOh2piz7EmIs1Xn4FsRxJWeVH4&q=${track.lat},${track.lon}&zoom=10" allowfullscreen>
|
|
|
|
</iframe>
|
|
|
|
</div>
|
2021-02-01 16:03:27 +00:00
|
|
|
<div class="card-body"></div>`}
|
|
|
|
<div class="card-footer">${track.invalid?``:`
|
2021-01-06 16:05:01 +00:00
|
|
|
<button class="btn btn-primary" trackid="${track.filename}" task="downloadkml">Download KML</button>
|
2021-02-01 16:03:27 +00:00
|
|
|
<button class="btn btn-primary" trackid="${track.filename}" task="downloadgpx">Download GPX</button>`}
|
2021-01-06 16:05:01 +00:00
|
|
|
<button class="btn btn-default" trackid="${track.filename}" task="delete">Delete</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
});
|
|
|
|
if (trackLines.length==0) {
|
|
|
|
html += `
|
|
|
|
<div class="column col-12">
|
|
|
|
<div class="card-header">
|
|
|
|
<div class="card-title h5">No tracks</div>
|
|
|
|
<div class="card-subtitle text-gray">No GPS tracks found</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
html += `
|
|
|
|
</div>
|
|
|
|
</div>`;
|
|
|
|
domTracks.innerHTML = html;
|
|
|
|
Util.hideModal();
|
|
|
|
var buttons = domTracks.querySelectorAll("button");
|
|
|
|
for (var i=0;i<buttons.length;i++) {
|
|
|
|
buttons[i].addEventListener("click",event => {
|
|
|
|
var button = event.currentTarget;
|
|
|
|
var trackid = button.getAttribute("trackid");
|
|
|
|
var task = button.getAttribute("task");
|
|
|
|
if (task=="delete") {
|
|
|
|
Util.showModal("Deleting Track...");
|
|
|
|
Util.eraseStorageFile(trackid,()=>{
|
|
|
|
Util.hideModal();
|
|
|
|
getTrackList();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (task=="downloadkml") {
|
|
|
|
downloadTrack(trackid, track => saveKML(track, `Bangle.js Track ${trackid}`));
|
|
|
|
}
|
|
|
|
if (task=="downloadgpx") {
|
|
|
|
downloadTrack(trackid, track => saveGPX(track, `Bangle.js Track ${trackid}`));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function onInit() {
|
|
|
|
getTrackList();
|
|
|
|
}
|
|
|
|
|
|
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html>
|