Big refactor to attempt to merge all the waypoints code into one place. At least 3 apps had basically identical interface.html files, and yet wpmoto had a great map-based editor.

master
Gordon Williams 2022-07-26 16:14:04 +01:00
parent e9fdb1dee5
commit 91728c147f
39 changed files with 485 additions and 828 deletions

View File

@ -3,4 +3,4 @@
0.03: Add Waypoint Editor 0.03: Add Waypoint Editor
0.04: Fix great circle formula 0.04: Fix great circle formula
0.05: Use locale for speed and distance + fix Vector font sizes 0.05: Use locale for speed and distance + fix Vector font sizes
0.06: Move waypoints.json (and editor) to 'waypoints' app

View File

@ -51,10 +51,10 @@ function drawCompass(course) {
//displayed heading //displayed heading
var heading = 0; var heading = 0;
function newHeading(m,h){ function newHeading(m,h){
var s = Math.abs(m - h); var s = Math.abs(m - h);
var delta = (m>h)?1:-1; var delta = (m>h)?1:-1;
if (s>=180){s=360-s; delta = -delta;} if (s>=180){s=360-s; delta = -delta;}
if (s<2) return h; if (s<2) return h;
var hd = h + delta*(1 + Math.round(s/5)); var hd = h + delta*(1 + Math.round(s/5));
if (hd<0) hd+=360; if (hd<0) hd+=360;
@ -125,7 +125,7 @@ function drawN(){
g.setColor(0,0,0); g.setColor(0,0,0);
g.fillRect(10,230,60,239); g.fillRect(10,230,60,239);
g.setColor(1,1,1); g.setColor(1,1,1);
g.drawString("Sats " + satellites.toString(),10,230); g.drawString("Sats " + satellites.toString(),10,230);
} }
var savedfix; var savedfix;
@ -193,11 +193,11 @@ var SCREENACCESS = {
}, },
release:function(){ release:function(){
this.withApp=true; this.withApp=true;
startdraw(); startdraw();
setButtons(); setButtons();
} }
} }
Bangle.on('lcdPower',function(on) { Bangle.on('lcdPower',function(on) {
if (!SCREENACCESS.withApp) return; if (!SCREENACCESS.withApp) return;
if (on) { if (on) {
@ -207,7 +207,7 @@ Bangle.on('lcdPower',function(on) {
} }
}); });
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}]; var waypoints = require("waypoints").load();
wp=waypoints[0]; wp=waypoints[0];
function nextwp(inc){ function nextwp(inc){
@ -223,7 +223,7 @@ function doselect(){
if (selected && wpindex!=0 && waypoints[wpindex].lat===undefined && savedfix.fix) { if (selected && wpindex!=0 && waypoints[wpindex].lat===undefined && savedfix.fix) {
waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon}; waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon};
wp = waypoints[wpindex]; wp = waypoints[wpindex];
require("Storage").writeJSON("waypoints.json", waypoints); require("waypoints").save(waypoints);
} }
selected=!selected; selected=!selected;
drawN(); drawN();

View File

@ -6,6 +6,6 @@ function drawN(){var a=loc.speed(speed);buf.setColor(1);buf.setFont("6x8",2);buf
0,30);buf.setColor(selected?1:2);buf.drawString(wp.name,140,0);buf.setColor(1);buf.drawString(a,60,0);buf.drawString(loc.distance(dist),60,30);flip(buf,Yoff+130);g.setFont("6x8",1);g.setColor(0,0,0);g.fillRect(10,230,60,239);g.setColor(1,1,1);g.drawString("Sats "+satellites.toString(),10,230)}var savedfix; 0,30);buf.setColor(selected?1:2);buf.drawString(wp.name,140,0);buf.setColor(1);buf.drawString(a,60,0);buf.drawString(loc.distance(dist),60,30);flip(buf,Yoff+130);g.setFont("6x8",1);g.setColor(0,0,0);g.fillRect(10,230,60,239);g.setColor(1,1,1);g.drawString("Sats "+satellites.toString(),10,230)}var savedfix;
function onGPS(a){savedfix=a;void 0!==a&&(course=isNaN(a.course)?course:Math.round(a.course),speed=isNaN(a.speed)?speed:a.speed,satellites=a.satellites);candraw&&(void 0!==a&&1==a.fix&&(dist=distance(a,wp),isNaN(dist)&&(dist=0),brg=bearing(a,wp),isNaN(brg)&&(brg=0)),drawN())}var intervalRef;function stopdraw(){candraw=!1;intervalRef&&clearInterval(intervalRef)} function onGPS(a){savedfix=a;void 0!==a&&(course=isNaN(a.course)?course:Math.round(a.course),speed=isNaN(a.speed)?speed:a.speed,satellites=a.satellites);candraw&&(void 0!==a&&1==a.fix&&(dist=distance(a,wp),isNaN(dist)&&(dist=0),brg=bearing(a,wp),isNaN(brg)&&(brg=0)),drawN())}var intervalRef;function stopdraw(){candraw=!1;intervalRef&&clearInterval(intervalRef)}
function startTimers(){candraw=!0;intervalRefSec=setInterval(function(){heading=newHeading(course,heading);course!=heading&&drawCompass(heading)},200)}function drawAll(){g.setColor(1,.5,.5);g.fillPoly([120,Yoff+50,110,Yoff+70,130,Yoff+70]);g.setColor(1,1,1);drawN();drawCompass(heading)}function startdraw(){g.clear();Bangle.drawWidgets();startTimers();drawAll()} function startTimers(){candraw=!0;intervalRefSec=setInterval(function(){heading=newHeading(course,heading);course!=heading&&drawCompass(heading)},200)}function drawAll(){g.setColor(1,.5,.5);g.fillPoly([120,Yoff+50,110,Yoff+70,130,Yoff+70]);g.setColor(1,1,1);drawN();drawCompass(heading)}function startdraw(){g.clear();Bangle.drawWidgets();startTimers();drawAll()}
function setButtons(){setWatch(nextwp.bind(null,-1),BTN1,{repeat:!0,edge:"falling"});setWatch(doselect,BTN2,{repeat:!0,edge:"falling"});setWatch(nextwp.bind(null,1),BTN3,{repeat:!0,edge:"falling"})}var SCREENACCESS={withApp:!0,request:function(){this.withApp=!1;stopdraw();clearWatch()},release:function(){this.withApp=!0;startdraw();setButtons()}};Bangle.on("lcdPower",function(a){SCREENACCESS.withApp&&(a?startdraw():stopdraw())});var waypoints=require("Storage").readJSON("waypoints.json")||[{name:"NONE"}]; function setButtons(){setWatch(nextwp.bind(null,-1),BTN1,{repeat:!0,edge:"falling"});setWatch(doselect,BTN2,{repeat:!0,edge:"falling"});setWatch(nextwp.bind(null,1),BTN3,{repeat:!0,edge:"falling"})}var SCREENACCESS={withApp:!0,request:function(){this.withApp=!1;stopdraw();clearWatch()},release:function(){this.withApp=!0;startdraw();setButtons()}};Bangle.on("lcdPower",function(a){SCREENACCESS.withApp&&(a?startdraw():stopdraw())});var waypoints=require("waypoints").load();
wp=waypoints[0];function nextwp(a){selected&&(wpindex+=a,wpindex>=waypoints.length&&(wpindex=0),0>wpindex&&(wpindex=waypoints.length-1),wp=waypoints[wpindex],drawN())}function doselect(){selected&&0!=wpindex&&void 0===waypoints[wpindex].lat&&savedfix.fix&&(waypoints[wpindex]={name:"@"+wp.name,lat:savedfix.lat,lon:savedfix.lon},wp=waypoints[wpindex],require("Storage").writeJSON("waypoints.json",waypoints));selected=!selected;drawN()}g.clear();Bangle.setLCDBrightness(1);Bangle.loadWidgets();Bangle.drawWidgets(); wp=waypoints[0];function nextwp(a){selected&&(wpindex+=a,wpindex>=waypoints.length&&(wpindex=0),0>wpindex&&(wpindex=waypoints.length-1),wp=waypoints[wpindex],drawN())}function doselect(){selected&&0!=wpindex&&void 0===waypoints[wpindex].lat&&savedfix.fix&&(waypoints[wpindex]={name:"@"+wp.name,lat:savedfix.lat,lon:savedfix.lon},wp=waypoints[wpindex],require("waypoints").save(waypoints));selected=!selected;drawN()}g.clear();Bangle.setLCDBrightness(1);Bangle.loadWidgets();Bangle.drawWidgets();
Bangle.setGPSPower(1);drawAll();startTimers();Bangle.on("GPS",onGPS);setButtons(); Bangle.setGPSPower(1);drawAll();startTimers();Bangle.on("GPS",onGPS);setButtons();

View File

@ -1,16 +1,15 @@
{ {
"id": "gpsnav", "id": "gpsnav",
"name": "GPS Navigation", "name": "GPS Navigation",
"version": "0.05", "version": "0.06",
"description": "Displays GPS Course and Speed, + Directions to waypoint and waypoint recording, now with waypoint editor", "description": "Displays GPS Course and Speed, + Directions to waypoint and waypoint recording, now with waypoint editor",
"icon": "icon.png", "icon": "icon.png",
"tags": "tool,outdoors,gps", "tags": "tool,outdoors,gps",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS"],
"readme": "README.md", "readme": "README.md",
"interface": "waypoints.html", "dependencies" : { "waypoints":"type" },
"storage": [ "storage": [
{"name":"gpsnav.app.js","url":"app.min.js"}, {"name":"gpsnav.app.js","url":"app.min.js"},
{"name":"gpsnav.img","url":"app-icon.js","evaluate":true} {"name":"gpsnav.img","url":"app-icon.js","evaluate":true}
], ]
"data": [{"name":"waypoints.json","url":"waypoints.json"}]
} }

View File

@ -1,170 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
</head>
<body>
<h4>List of waypoints</h4>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Lat.</th>
<th>Long.</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="waypoints">
</tbody>
</table>
<br>
<h4>Add a new waypoint</h4>
<form id="add_waypoint_form">
<div class="columns">
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" type="text" id="add_waypoint_name" placeholder="Name">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_latitude" placeholder="Lat">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_longtitude" placeholder="Long">
</div>
</div>
<div class="columns">
<div class="column col-3 col-xs-8">
<button id="add_name_button" class="btn btn-primary btn-sm">Add Name Only</button>
</div>
<div class="column col-3 col-xs-8">
<button id="add_waypoint_button" class="btn btn-primary btn-sm">Add Waypoint</button>
</div>
</div>
</form>
<br>
<button id="Download" class="btn btn-error">Reload</button> <button id="Upload" class="btn btn-primary">Upload</button>
<script src="../../core/lib/interface.js"></script>
<script>
var waypoints = []
var $name = document.getElementById('add_waypoint_name')
var $form = document.getElementById('add_waypoint_form')
var $button = document.getElementById('add_waypoint_button')
var $name_button = document.getElementById('add_name_button')
var $latitude = document.getElementById('add_latitude')
var $longtitude = document.getElementById('add_longtitude')
var $list = document.getElementById('waypoints')
function compare(a, b){
var x = a.name.toLowerCase();
var y = b.name.toLowerCase();
if (x=="none") {return -1};
if (y=="none") {return 1};
if (x < y) {return -1;}
if (x > y) {return 1;}
return 0;
}
$button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
var lat = parseFloat($latitude.value).toPrecision(5);
var lon = parseFloat($longtitude.value).toPrecision(5);
waypoints.push({
name, lat,lon,
});
waypoints.sort(compare);
renderWaypoints()
$name.value = ''
$latitude.value = (0).toPrecision(5);
$longtitude.value = (0).toPrecision(5);
});
$name_button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
waypoints.push({
name
});
waypoints.sort(compare);
renderWaypoints()
$name.value = ''
$latitude.value = 0.0000
$longtitude.value = 0.0000
});
function removeWaypoint(index){
$name.value = waypoints[index].name
$latitude.value = waypoints[index].lat
$longtitude.value = waypoints[index].lon
waypoints = waypoints.filter((p,i) => i!==index)
renderWaypoints()
}
function renderWaypoints(){
$list.innerHTML = ''
waypoints.forEach((waypoint,index) => {
var $waypoint = document.createElement('tr')
if (index==0){
$waypoint.innerHTML = `<td>${waypoint.name}</td>`
} else if(waypoint.lat==undefined){
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>------</td><td>-----</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
} else {
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>${waypoint.lat}</td><td>${waypoint.lon}</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
}
$list.appendChild($waypoint)
})
$name.focus()
}
function downloadJSONfile(fileid, callback) {
Puck.write(`\x10(function() {
var pts = require("Storage").readJSON("${fileid}")||[{name:"NONE"}];
Bluetooth.print(JSON.stringify(pts));
})()\n`,contents=>{
var storedpts = JSON.parse(contents);
callback(storedpts);
});
}
function uploadFile(fileid, contents) {
Puck.write(`\x10(function() {
require("Storage").write("${fileid}",'${contents}');
Bluetooth.print("OK");
})()\n`,ret=>{
console.log("uploadFile",ret);
});
}
function gotStored(pts){
waypoints = pts;
renderWaypoints();
}
function onInit() {
downloadJSONfile("waypoints.json", gotStored);
}
document.getElementById("Download").addEventListener("click", function() {
downloadJSONfile("waypoints.json", gotStored);
});
document.getElementById("Upload").addEventListener("click", function() {
var data = JSON.stringify(waypoints);
uploadFile("waypoints.json",data);
});
</script>
</body>
</html>

View File

@ -1,20 +0,0 @@
[
{
"name":"NONE"
},
{
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
},
{ "name":"WP0" },
{ "name":"WP1" },
{ "name":"WP2" },
{ "name":"WP3" },
{ "name":"WP4" }
]

View File

@ -11,3 +11,4 @@
0.11: Detect when waypoints.json is not present, error E-WPT 0.11: Detect when waypoints.json is not present, error E-WPT
0.12: Added stepo2 as a replacement for stepo and digi 0.12: Added stepo2 as a replacement for stepo and digi
0.13: Added long press BTN2 toggle gpsrec status in GPS clock 0.13: Added long press BTN2 toggle gpsrec status in GPS clock
0.14: Move waypoints.json (and editor) to 'waypoints' app

View File

@ -60,7 +60,7 @@ The following buttons depend on which face is currently in use
![](screenshot_stepo.jpg) ![](screenshot_stepo.jpg)
- now replaced by Stepo2 but still available if you install manually - now replaced by Stepo2 but still available if you install manually
- Requires one of the pedominter widgets to be installed - Requires one of the pedominter widgets to be installed
- Displays the time in large font - Displays the time in large font
- Display current step count in a doughnut gauge - Display current step count in a doughnut gauge
- Show step count in the middle of the doughnut gauge - Show step count in the middle of the doughnut gauge
@ -208,14 +208,8 @@ which will obviously limit this.
### Waypoint Editor ### Waypoint Editor
Clicking on the download icon of gpsnav in the app loader invokes the Clicking on the download icon of `Waypoints` in the app loader invokes the
waypoint editor. The editor downloads and displays the current waypoint editor. See the `Waypoints` app for more information.
`waypoints.json` file. Clicking the `Edit` button beside an entry
causes the entry to be deleted from the list and displayed in the
edit boxes. It can be restored - by clicking the `Add waypoint`
button. A new markable entry is created by using the `Add name`
button. The edited `waypoints.json` file is uploaded to the Bangle by
clicking the `Upload` button.
### Calibration of the Compass ### Calibration of the Compass

View File

@ -23,7 +23,7 @@ function nextFace(){
iface += 1 iface += 1
iface = iface % FACES.length; iface = iface % FACES.length;
face = FACES[iface](); face = FACES[iface]();
g.clear(); g.clear();
g.reset(); g.reset();
face.init(gpsObj, swObj, hrmObj, tripObject); face.init(gpsObj, swObj, hrmObj, tripObject);
@ -64,7 +64,7 @@ function buttonReleased(btn) {
clearInterval(pressTimer); clearInterval(pressTimer);
pressTimer = undefined; pressTimer = undefined;
} }
if ( dur >= 1.5 ) { if ( dur >= 1.5 ) {
switch(btn) { switch(btn) {
case 1: case 1:
@ -165,11 +165,11 @@ GPS.prototype.getLastFix = function() {
GPS.prototype.determineGPSState = function() { GPS.prototype.determineGPSState = function() {
this.log_debug("determineGPSState"); this.log_debug("determineGPSState");
gpsPowerState = Bangle.isGPSOn(); gpsPowerState = Bangle.isGPSOn();
//this.log_debug("last_fix.fix " + this.last_fix.fix); //this.log_debug("last_fix.fix " + this.last_fix.fix);
//this.log_debug("gpsPowerState " + this.gpsPowerState); //this.log_debug("gpsPowerState " + this.gpsPowerState);
//this.log_debug("last_fix.satellites " + this.last_fix.satellites); //this.log_debug("last_fix.satellites " + this.last_fix.satellites);
if (!gpsPowerState) { if (!gpsPowerState) {
this.gpsState = this.GPS_OFF; this.gpsState = this.GPS_OFF;
this.resetLastFix(); this.resetLastFix();
@ -178,9 +178,9 @@ GPS.prototype.determineGPSState = function() {
} else { } else {
this.gpsState = this.GPS_SATS; this.gpsState = this.GPS_SATS;
} }
this.log_debug("gpsState=" + this.gpsState); this.log_debug("gpsState=" + this.gpsState);
if (this.gpsState !== this.GPS_OFF) { if (this.gpsState !== this.GPS_OFF) {
if (this.listenerCount === 0) { if (this.listenerCount === 0) {
Bangle.on('GPS', processFix); Bangle.on('GPS', processFix);
@ -196,9 +196,9 @@ GPS.prototype.determineGPSState = function() {
} }
}; };
GPS.prototype.getGPSTime = function() { GPS.prototype.getGPSTime = function() {
var time; var time;
if (this.last_fix !== undefined && this.last_fix.time !== undefined && this.last_fix.time.toUTCString !== undefined && if (this.last_fix !== undefined && this.last_fix.time !== undefined && this.last_fix.time.toUTCString !== undefined &&
(this.gpsState == this.GPS_SATS || this.gpsState == this.GPS_RUNNING)) { (this.gpsState == this.GPS_SATS || this.gpsState == this.GPS_RUNNING)) {
time = this.last_fix.time.toUTCString().split(" "); time = this.last_fix.time.toUTCString().split(" ");
@ -216,7 +216,7 @@ GPS.prototype.toggleGPSPower = function() {
this.gpsPowerState = Bangle.isGPSOn(); this.gpsPowerState = Bangle.isGPSOn();
this.gpsPowerState = !this.gpsPowerState; this.gpsPowerState = !this.gpsPowerState;
Bangle.setGPSPower((this.gpsPowerState ? 1 : 0), 'kitchen'); Bangle.setGPSPower((this.gpsPowerState ? 1 : 0), 'kitchen');
this.resetLastFix(); this.resetLastFix();
this.determineGPSState(); this.determineGPSState();
@ -247,11 +247,11 @@ GPS.prototype.processFix = function(fix) {
//this.log_debug("GPS:processFix()"); //this.log_debug("GPS:processFix()");
//this.log_debug(fix); //this.log_debug(fix);
this.last_fix.time = fix.time; this.last_fix.time = fix.time;
if (this.gpsState == this.GPS_TIME) { if (this.gpsState == this.GPS_TIME) {
this.gpsState = this.GPS_SATS; this.gpsState = this.GPS_SATS;
} }
if (fix.fix) { if (fix.fix) {
//this.log_debug("Got fix - setting state to GPS_RUNNING"); //this.log_debug("Got fix - setting state to GPS_RUNNING");
this.gpsState = this.GPS_RUNNING; this.gpsState = this.GPS_RUNNING;
@ -271,10 +271,10 @@ GPS.prototype.formatTime = function(now) {
GPS.prototype.timeSince = function(t) { GPS.prototype.timeSince = function(t) {
var hms = t.split(":"); var hms = t.split(":");
var now = new Date(); var now = new Date();
var sn = 3600*(now.getHours()) + 60*(now.getMinutes()) + 1*(now.getSeconds()); var sn = 3600*(now.getHours()) + 60*(now.getMinutes()) + 1*(now.getSeconds());
var st = 3600*(hms[0]) + 60*(hms[1]) + 1*(hms[2]); var st = 3600*(hms[0]) + 60*(hms[1]) + 1*(hms[2]);
return (sn - st); return (sn - st);
}; };
@ -313,7 +313,7 @@ GPS.prototype.getWPdistance = function() {
GPS.prototype.getWPbearing = function() { GPS.prototype.getWPbearing = function() {
//log_debug(this.last_fix); //log_debug(this.last_fix);
//log_debug(this.wp_current); //log_debug(this.wp_current);
if (this.wp_current.name === "E-WPT" || this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0) if (this.wp_current.name === "E-WPT" || this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
return 0; return 0;
else else
@ -321,7 +321,7 @@ GPS.prototype.getWPbearing = function() {
} }
GPS.prototype.loadFirstWaypoint = function() { GPS.prototype.loadFirstWaypoint = function() {
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}]; var waypoints = require("waypoints").load();
this.wp_index = 0; this.wp_index = 0;
this.wp_current = waypoints[this.wp_index]; this.wp_current = waypoints[this.wp_index];
log_debug(this.wp_current); log_debug(this.wp_current);
@ -345,10 +345,10 @@ GPS.prototype.markWaypoint = function() {
return; return;
log_debug("GPS::markWaypoint()"); log_debug("GPS::markWaypoint()");
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}]; var waypoints = require("waypoints").load();
this.wp_current = waypoints[this.wp_index]; this.wp_current = waypoints[this.wp_index];
if (this.waypointHasLocation()) { if (this.waypointHasLocation()) {
waypoints[this.wp_index] = {name:this.wp_current.name, lat:0, lon:0}; waypoints[this.wp_index] = {name:this.wp_current.name, lat:0, lon:0};
} else { } else {
@ -356,12 +356,12 @@ GPS.prototype.markWaypoint = function() {
} }
this.wp_current = waypoints[this.wp_index]; this.wp_current = waypoints[this.wp_index];
require("Storage").writeJSON("waypoints.json", waypoints); require("waypoints").save(waypoints);
log_debug("GPS::markWaypoint() written"); log_debug("GPS::markWaypoint() written");
} }
GPS.prototype.nextWaypoint = function(inc) { GPS.prototype.nextWaypoint = function(inc) {
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}]; var waypoints = require("waypoints").load();
this.wp_index+=inc; this.wp_index+=inc;
if (this.wp_index>=waypoints.length) this.wp_index=0; if (this.wp_index>=waypoints.length) this.wp_index=0;
if (this.wp_index<0) this.wp_index = waypoints.length-1; if (this.wp_index<0) this.wp_index = waypoints.length-1;
@ -520,7 +520,7 @@ function STOPWATCH() {
this.redrawLaps = true; this.redrawLaps = true;
this.redrawTime = true; this.redrawTime = true;
} }
STOPWATCH.prototype.log_debug = function(o) { STOPWATCH.prototype.log_debug = function(o) {
//console.log(o); //console.log(o);
} }
@ -531,7 +531,7 @@ STOPWATCH.prototype.timeToText = function(t) {
let secs = Math.floor(t/1000)%60; let secs = Math.floor(t/1000)%60;
let text; let text;
if (hrs === 0) if (hrs === 0)
text = ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2); text = ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
else else
text = (""+hrs) + ":" + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2); text = (""+hrs) + ":" + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
@ -551,7 +551,7 @@ STOPWATCH.prototype.stopStart = function() {
if (this.running) if (this.running)
this.tStart = Date.now() + this.tStart - this.tCurrent; this.tStart = Date.now() + this.tStart - this.tCurrent;
this.tTotal = Date.now() + this.tTotal - this.tCurrent; this.tTotal = Date.now() + this.tTotal - this.tCurrent;
this.tCurrent = Date.now(); this.tCurrent = Date.now();
this.redrawButtons = true; this.redrawButtons = true;
@ -623,7 +623,7 @@ STOPWATCH.prototype.drawLaptimes = function() {
g.setFont("Vector",24); g.setFont("Vector",24);
g.setFontAlign(-1,-1); g.setFontAlign(-1,-1);
g.clearRect(4, 205, 239, 229); // clear the last line of the lap times g.clearRect(4, 205, 239, 229); // clear the last line of the lap times
let laps = 0; let laps = 0;
for (let i in this.lapTimes) { for (let i in this.lapTimes) {
g.drawString(this.lapTimes.length-i + ": " + this.timeToText(this.lapTimes[i]), 4, this.timeY + 40 + i*24); g.drawString(this.lapTimes.length-i + ": " + this.timeToText(this.lapTimes[i]), 4, this.timeY + 40 + i*24);
@ -645,7 +645,7 @@ STOPWATCH.prototype.drawTime = function() {
g.setFont("Vector",38); g.setFont("Vector",38);
g.setFontAlign(0,0); g.setFontAlign(0,0);
g.clearRect(0, this.timeY-21, 200, this.timeY+21); g.clearRect(0, this.timeY-21, 200, this.timeY+21);
g.setColor(0xFFC0); g.setColor(0xFFC0);
g.drawString(txtTotal, xTotal, this.timeY); g.drawString(txtTotal, xTotal, this.timeY);
// current lap time // current lap time
@ -691,7 +691,7 @@ function HRM() {
this.bpm = 0; this.bpm = 0;
this.confidence = 0; this.confidence = 0;
} }
HRM.prototype.log_debug = function(o) { HRM.prototype.log_debug = function(o) {
//console.log(o); //console.log(o);
} }
@ -782,7 +782,7 @@ Debug Object
function DEBUG() { function DEBUG() {
this.logfile = require("Storage").open("debug.log","a"); this.logfile = require("Storage").open("debug.log","a");
} }
DEBUG.prototype.log = function(msg) { DEBUG.prototype.log = function(msg) {
let timestamp = new Date().toString().split(" ")[4]; let timestamp = new Date().toString().split(" ")[4];
let line = timestamp + ", " + msg + "\n"; let line = timestamp + ", " + msg + "\n";

View File

@ -1,14 +1,14 @@
{ {
"id": "kitchen", "id": "kitchen",
"name": "Kitchen Combo", "name": "Kitchen Combo",
"version": "0.13", "version": "0.14",
"description": "Combination of the Stepo, Walkersclock, Arrow and Waypointer apps into a multiclock format. 'Everything but the kitchen sink'", "description": "Combination of the Stepo, Walkersclock, Arrow and Waypointer apps into a multiclock format. 'Everything but the kitchen sink'",
"icon": "kitchen.png", "icon": "kitchen.png",
"type": "clock", "type": "clock",
"tags": "tool,outdoors,gps", "tags": "tool,outdoors,gps",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS"],
"readme": "README.md", "readme": "README.md",
"interface": "waypoints.html", "dependencies" : { "waypoints":"type" },
"storage": [ "storage": [
{"name":"kitchen.app.js","url":"kitchen.app.js"}, {"name":"kitchen.app.js","url":"kitchen.app.js"},
{"name":"stepo2.kit.js","url":"stepo2.kit.js"}, {"name":"stepo2.kit.js","url":"stepo2.kit.js"},
@ -16,6 +16,5 @@
{"name":"gps.kit.js","url":"gps.kit.js"}, {"name":"gps.kit.js","url":"gps.kit.js"},
{"name":"compass.kit.js","url":"compass.kit.js"}, {"name":"compass.kit.js","url":"compass.kit.js"},
{"name":"kitchen.img","url":"kitchen.icon.js","evaluate":true} {"name":"kitchen.img","url":"kitchen.icon.js","evaluate":true}
], ]
"data": [{"name":"waypoints.json","url":"waypoints.json"}]
} }

View File

@ -1,170 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
</head>
<body>
<h4>List of waypoints</h4>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Lat.</th>
<th>Long.</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="waypoints">
</tbody>
</table>
<br>
<h4>Add a new waypoint</h4>
<form id="add_waypoint_form">
<div class="columns">
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" type="text" id="add_waypoint_name" placeholder="Name">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_latitude" placeholder="Lat">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_longtitude" placeholder="Long">
</div>
</div>
<div class="columns">
<div class="column col-3 col-xs-8">
<button id="add_name_button" class="btn btn-primary btn-sm">Add Name Only</button>
</div>
<div class="column col-3 col-xs-8">
<button id="add_waypoint_button" class="btn btn-primary btn-sm">Add Waypoint</button>
</div>
</div>
</form>
<br>
<button id="Download" class="btn btn-error">Reload</button> <button id="Upload" class="btn btn-primary">Upload</button>
<script src="../../core/lib/interface.js"></script>
<script>
var waypoints = []
var $name = document.getElementById('add_waypoint_name')
var $form = document.getElementById('add_waypoint_form')
var $button = document.getElementById('add_waypoint_button')
var $name_button = document.getElementById('add_name_button')
var $latitude = document.getElementById('add_latitude')
var $longtitude = document.getElementById('add_longtitude')
var $list = document.getElementById('waypoints')
function compare(a, b){
var x = a.name.toLowerCase();
var y = b.name.toLowerCase();
if (x=="none") {return -1};
if (y=="none") {return 1};
if (x < y) {return -1;}
if (x > y) {return 1;}
return 0;
}
$button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
var lat = parseFloat($latitude.value).toPrecision(5);
var lon = parseFloat($longtitude.value).toPrecision(5);
waypoints.push({
name, lat,lon,
});
waypoints.sort(compare);
renderWaypoints()
$name.value = ''
$latitude.value = (0).toPrecision(5);
$longtitude.value = (0).toPrecision(5);
});
$name_button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
waypoints.push({
name
});
waypoints.sort(compare);
renderWaypoints()
$name.value = ''
$latitude.value = 0.0000
$longtitude.value = 0.0000
});
function removeWaypoint(index){
$name.value = waypoints[index].name
$latitude.value = waypoints[index].lat
$longtitude.value = waypoints[index].lon
waypoints = waypoints.filter((p,i) => i!==index)
renderWaypoints()
}
function renderWaypoints(){
$list.innerHTML = ''
waypoints.forEach((waypoint,index) => {
var $waypoint = document.createElement('tr')
if (index==0){
$waypoint.innerHTML = `<td>${waypoint.name}</td>`
} else if(waypoint.lat==undefined){
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>------</td><td>-----</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
} else {
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>${waypoint.lat}</td><td>${waypoint.lon}</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
}
$list.appendChild($waypoint)
})
$name.focus()
}
function downloadJSONfile(fileid, callback) {
Puck.write(`\x10(function() {
var pts = require("Storage").readJSON("${fileid}")||[{name:"NONE"}];
Bluetooth.print(JSON.stringify(pts));
})()\n`,contents=>{
var storedpts = JSON.parse(contents);
callback(storedpts);
});
}
function uploadFile(fileid, contents) {
Puck.write(`\x10(function() {
require("Storage").write("${fileid}",'${contents}');
Bluetooth.print("OK");
})()\n`,ret=>{
console.log("uploadFile",ret);
});
}
function gotStored(pts){
waypoints = pts;
renderWaypoints();
}
function onInit() {
downloadJSONfile("waypoints.json", gotStored);
}
document.getElementById("Download").addEventListener("click", function() {
downloadJSONfile("waypoints.json", gotStored);
});
document.getElementById("Upload").addEventListener("click", function() {
var data = JSON.stringify(waypoints);
uploadFile("waypoints.json",data);
});
</script>
</body>
</html>

View File

@ -1,20 +0,0 @@
[
{
"name":"NONE"
},
{
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
},
{ "name":"WP0" },
{ "name":"WP1" },
{ "name":"WP2" },
{ "name":"WP3" },
{ "name":"WP4" }
]

View File

@ -2,7 +2,7 @@
You can switch between three display modes. One showing speed and altitude (A), one showing speed and distance to waypoint (D) and a large dispay of time and selected waypoint. You can switch between three display modes. One showing speed and altitude (A), one showing speed and distance to waypoint (D) and a large dispay of time and selected waypoint.
Within the [A]ltitude and [D]istance displays modes one figure is displayed on the watch face using the largest possible characters depending on the number of digits. The other is in a smaller characters below that. Both are always visible. You can display the current or maximum observed speed/altitude values. Current time is always displayed. Within the [A]ltitude and [D]istance displays modes one figure is displayed on the watch face using the largest possible characters depending on the number of digits. The other is in a smaller characters below that. Both are always visible. You can display the current or maximum observed speed/altitude values. Current time is always displayed.
The waypoints list is the same as that used with the [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app so the same set of waypoints can be used across both apps. Refer to that app for waypoint file information. The waypoints list is the same as that used with the [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app so the same set of waypoints can be used across both apps. Refer to that app for waypoint file information.
@ -36,7 +36,7 @@ BTN4 : Left Display Tap : Swaps which figure is in the large display. You can ha
## App Settings ## App Settings
Select the desired display units. Speed can be as per the default locale, kph, knots, mph or m/s. Distance can be km, miles or nautical miles. Altitude can be feet or metres. Select one of three colour schemes. Default (three colours), high contrast (all white on black) or night ( all red on black ). Select the desired display units. Speed can be as per the default locale, kph, knots, mph or m/s. Distance can be km, miles or nautical miles. Altitude can be feet or metres. Select one of three colour schemes. Default (three colours), high contrast (all white on black) or night ( all red on black ).
## Kalman Filter ## Kalman Filter
@ -67,13 +67,11 @@ This app will work quite happily on its own but will use the [GPS Setup App](htt
When using the GPS Setup App this app switches the GPS to SuperE (default) mode while the display is lit and showing fix information. This ensures that that fixes are updated every second or so. 10 seconds after the display is blanked by the watch this app will switch the GPS to PSMOO mode and will only attempt to get a fix every two minutes. This improves power saving while the display is off and the delay gives an opportunity to restore the display before the GPS power mode is switched. When using the GPS Setup App this app switches the GPS to SuperE (default) mode while the display is lit and showing fix information. This ensures that that fixes are updated every second or so. 10 seconds after the display is blanked by the watch this app will switch the GPS to PSMOO mode and will only attempt to get a fix every two minutes. This improves power saving while the display is off and the delay gives an opportunity to restore the display before the GPS power mode is switched.
The MAX values continue to be collected with the display off so may appear a little odd after the intermittent fixes of the low power mode. The MAX values continue to be collected with the display off so may appear a little odd after the intermittent fixes of the low power mode.
## Waypoints ## Waypoints
Waypoints are used in [D]istance mode. Create a file waypoints.json and write to storage on the Bangle.js using the IDE. The first 6 characters of the name are displayed in Speed+[D]istance mode. Waypoints are used in Distance and VMG modes. See the `Waypoints` app for information on how to create/edit waypoints. The first 6 characters of the name are displayed in Speed+[D]istance mode.
The [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app in the App Loader has a really nice waypoints file editor. (Must be connected to your Bangle.JS and then click on the Download icon.)
Sample waypoints.json (My sailing waypoints) Sample waypoints.json (My sailing waypoints)
@ -149,5 +147,3 @@ Developed for my use in sailing, cycling and motorcycling. If you find this soft
Many thanks to Gordon Williams. Awesome job. Many thanks to Gordon Williams. Awesome job.
Special thanks also to @jeffmer, for the [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app and @hughbarney for the Low power GPS code development and Wouter Bulten for the Kalman filter code. Special thanks also to @jeffmer, for the [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app and @hughbarney for the Low power GPS code development and Wouter Bulten for the Kalman filter code.

View File

@ -209,7 +209,7 @@ function nxtWp(inc){
} }
function loadWp() { function loadWp() {
var w = require("Storage").readJSON('waypoints.json')||[{name:"NONE"}]; var w = require("waypoints").load();
if (cfg.wp>=w.length) cfg.wp=0; if (cfg.wp>=w.length) cfg.wp=0;
if (cfg.wp<0) cfg.wp = w.length-1; if (cfg.wp<0) cfg.wp = w.length-1;
savSettings(); savSettings();

View File

@ -8,12 +8,12 @@
"type": "app", "type": "app",
"tags": "tool,outdoors", "tags": "tool,outdoors",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"dependencies" : { "waypoints":"type" },
"readme": "README.md", "readme": "README.md",
"allow_emulator": true, "allow_emulator": true,
"storage": [ "storage": [
{"name":"speedalt.app.js","url":"app.js"}, {"name":"speedalt.app.js","url":"app.js"},
{"name":"speedalt.img","url":"app-icon.js","evaluate":true}, {"name":"speedalt.img","url":"app-icon.js","evaluate":true},
{"name":"speedalt.settings.js","url":"settings.js"} {"name":"speedalt.settings.js","url":"settings.js"}
], ]
"data": [{"name":"speedalt.json"}]
} }

View File

@ -15,3 +15,4 @@
0.15: Droidscript mirroring prog automatically uses last connection address. Auto connects when run. 0.15: Droidscript mirroring prog automatically uses last connection address. Auto connects when run.
0.16: Add configuration item Wpt File Suffix. A one character suffix to append to the waypoints.json file. A number of other apps also use this file name. Using the file name suffix allows the speedalt2 waypoints to be retained if one of these other apps is installed for a different use. 0.16: Add configuration item Wpt File Suffix. A one character suffix to append to the waypoints.json file. A number of other apps also use this file name. Using the file name suffix allows the speedalt2 waypoints to be retained if one of these other apps is installed for a different use.
0.17: Use default Bangle formatter for booleans 0.17: Use default Bangle formatter for booleans
0.18: Move waypoints.json to 'waypoints' app (with editor)

View File

@ -25,7 +25,7 @@ Touch functions as BTN1 short press.
## App Settings ## App Settings
Select the desired display units. Speed can be as per the default locale, kph, knots, mph or m/s. Distance can be km, miles or nautical miles. Altitude can be feet or metres. Select one of three colour schemes. Default (three colours), high contrast (all white on black) or night ( all red on black ). Select the desired display units. Speed can be as per the default locale, kph, knots, mph or m/s. Distance can be km, miles or nautical miles. Altitude can be feet or metres. Select one of three colour schemes. Default (three colours), high contrast (all white on black) or night ( all red on black ).
## Kalman Filter ## Kalman Filter
@ -43,7 +43,7 @@ This app will work quite happily on its own but will use the [GPS Setup App](htt
When using the GPS Setup App this app switches the GPS to SuperE (default) mode while the display is lit and showing fix information. This ensures that that fixes are updated every second or so. 10 seconds after the display is blanked by the watch this app will switch the GPS to PSMOO mode and will only attempt to get a fix every two minutes. This improves power saving while the display is off and the delay gives an opportunity to restore the display before the GPS power mode is switched. When using the GPS Setup App this app switches the GPS to SuperE (default) mode while the display is lit and showing fix information. This ensures that that fixes are updated every second or so. 10 seconds after the display is blanked by the watch this app will switch the GPS to PSMOO mode and will only attempt to get a fix every two minutes. This improves power saving while the display is off and the delay gives an opportunity to restore the display before the GPS power mode is switched.
The MAX values continue to be collected with the display off so may appear a little odd after the intermittent fixes of the low power mode. The MAX values continue to be collected with the display off so may appear a little odd after the intermittent fixes of the low power mode.
## Velocity Made Good - VMG ## Velocity Made Good - VMG
@ -63,9 +63,9 @@ The Droidscript script file is called : **GPS Adv Sports II.js**
**Important Gotcha :** For the BLE comms to find and connect to the Bangle.js it must be paired with the Android device but **NOT** connected. The Bangle.js must have been set in the Bluetooth settings as connectable. **Important Gotcha :** For the BLE comms to find and connect to the Bangle.js it must be paired with the Android device but **NOT** connected. The Bangle.js must have been set in the Bluetooth settings as connectable.
Start/Stop buttons tell the Bangle.js to start or stop sending BLE data packets to the Android device. While stopped the Bangle.js reverts to full power saving mode when the screen is asleep. Start/Stop buttons tell the Bangle.js to start or stop sending BLE data packets to the Android device. While stopped the Bangle.js reverts to full power saving mode when the screen is asleep.
When running a blue 'led' will flash each time a data packet is recieved to refresh the android display. When running a blue 'led' will flash each time a data packet is recieved to refresh the android display.
An orange 'led' will flash for each reconnection attempt if no data is received for 30 seconds. It will keep trying to reconnect so you can restart the Bangle, run another Bangle app or temprarily turn off bluetooth. The android mirror display will automatically reconnect when the GPS Adv Sports II app is running on the Bangle again. ( Designed to leave the Android device running as the display mirror in a sealed case all day while retaining the ability to do other functions on the Bangle.js and returning to the GPS Speed Alt II app. ) An orange 'led' will flash for each reconnection attempt if no data is received for 30 seconds. It will keep trying to reconnect so you can restart the Bangle, run another Bangle app or temprarily turn off bluetooth. The android mirror display will automatically reconnect when the GPS Adv Sports II app is running on the Bangle again. ( Designed to leave the Android device running as the display mirror in a sealed case all day while retaining the ability to do other functions on the Bangle.js and returning to the GPS Speed Alt II app. )
@ -74,14 +74,12 @@ Android Screen Mirroring:<br>
## Waypoints ## Waypoints
Waypoints are used in Distance and VMG modes. Create a file waypoints.json and write to storage on the Bangle.js using the IDE. The first 6 characters of the name are displayed in Speed+[D]istance mode. Waypoints are used in Distance and VMG modes. See the `Waypoints` app for information on how to create/edit waypoints. The first 6 characters of the name are displayed in Speed+[D]istance mode.
The [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app in the App Loader has a really nice waypoints file editor. (Must be connected to your Bangle.JS and then click on the Download icon.)
By default the waypoints file is called waypoints.json By default the waypoints file is called waypoints.json
**Note** : The waypoints.json file is used by a number of different gps apps. The setting 'Wpt File Suffix' allows one of waypoints1.json, waypoints2.json or waypoints3.json to be used instead. This allows the other apps to be used with a different set of waypoints without losing the speedalt2 waypoint set. **Note** : The waypoints.json file is used by a number of different gps apps. The setting 'Wpt File Suffix' allows one of waypoints.1.json, waypoints.2.json or waypoints.3.json to be used instead. This allows the other apps to be used with a different set of waypoints without losing the speedalt2 waypoint set.
Sample waypoints.json (My sailing waypoints) Sample waypoints.json (My sailing waypoints)
<pre> <pre>
@ -156,4 +154,3 @@ Developed for my use in sailing, cycling and motorcycling. If you find this soft
Many thanks to Gordon Williams. Awesome job. Many thanks to Gordon Williams. Awesome job.
Special thanks also to @jeffmer, for the [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app and @hughbarney for the Low power GPS code development and Wouter Bulten for the Kalman filter code. Special thanks also to @jeffmer, for the [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app and @hughbarney for the Low power GPS code development and Wouter Bulten for the Kalman filter code.

View File

@ -185,7 +185,7 @@ let LED = // LED as minimal and only definition (as instance / singleton)
, reset: function() { this.set(false); } // turn off , reset: function() { this.set(false); } // turn off
, write: function(v) { this.set(v); } // turn on w/ no arg or truey, else off , write: function(v) { this.set(v); } // turn on w/ no arg or truey, else off
, toggle: function() { this.set( ! this.isOn); } // toggle the LED , toggle: function() { this.set( ! this.isOn); } // toggle the LED
}, LED1 = LED; // LED1 as 'synonym' for LED }, LED1 = LED; // LED1 as 'synonym' for LED
var lf = {fix:0,satellites:0}; var lf = {fix:0,satellites:0};
@ -210,7 +210,7 @@ function nxtWp(){
} }
function loadWp() { function loadWp() {
var w = require("Storage").readJSON('waypoints'+cfg.wptSfx+'.json')||[{name:"NONE"}]; var w = require("waypoints").load(cfg.wptSfx);
if (cfg.wp>=w.length) cfg.wp=0; if (cfg.wp>=w.length) cfg.wp=0;
if (cfg.wp<0) cfg.wp = w.length-1; if (cfg.wp<0) cfg.wp = w.length-1;
savSettings(); savSettings();
@ -239,7 +239,7 @@ function bearing(a,b){
function distance(a,b){ function distance(a,b){
var x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2)); var x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2));
var y = radians(b.lat-a.lat); var y = radians(b.lat-a.lat);
// Distance in metres // Distance in metres
var d = Math.sqrt(x*x + y*y) * 6371000; var d = Math.sqrt(x*x + y*y) * 6371000;
return d; return d;
@ -251,38 +251,38 @@ function drawScrn(dat) {
buf.clear(); buf.clear();
buf.setBgColor(0); buf.setBgColor(0);
var n; var n;
n = dat.val.toString(); n = dat.val.toString();
var s=50; // Font size var s=50; // Font size
var l=n.length; var l=n.length;
if ( l <= 7 ) s=55; if ( l <= 7 ) s=55;
if ( l <= 6 ) s=60; if ( l <= 6 ) s=60;
if ( l <= 5 ) s=80; if ( l <= 5 ) s=80;
if ( l <= 4 ) s=100; if ( l <= 4 ) s=100;
if ( l <= 3 ) s=120; if ( l <= 3 ) s=120;
buf.setFontAlign(0,0); //Centre buf.setFontAlign(0,0); //Centre
buf.setColor(1); buf.setColor(1);
buf.setFontVector(s); buf.setFontVector(s);
buf.drawString(n,126,52); buf.drawString(n,126,52);
// Primary Units // Primary Units
buf.setFontAlign(-1,1); //left, bottom buf.setFontAlign(-1,1); //left, bottom
buf.setColor(2); buf.setColor(2);
buf.setFontVector(35); buf.setFontVector(35);
buf.drawString(dat.unit,5,164); buf.drawString(dat.unit,5,164);
drawMax(dat.max); // MAX display indicator drawMax(dat.max); // MAX display indicator
drawWP(dat.wp); // Waypoint name drawWP(dat.wp); // Waypoint name
drawSats(dat.sats); drawSats(dat.sats);
g.reset(); g.reset();
g.drawImage(img,0,40); g.drawImage(img,0,40);
LED1.write(!pwrSav); LED1.write(!pwrSav);
} }
@ -295,7 +295,7 @@ function drawPosn(dat) {
var x, y; var x, y;
x=210; x=210;
y=0; y=0;
buf.setFontAlign(1,-1); buf.setFontAlign(1,-1);
buf.setFontVector(60); buf.setFontVector(60);
buf.setColor(1); buf.setColor(1);
@ -320,45 +320,45 @@ function drawPosn(dat) {
function drawClock() { function drawClock() {
if (!canDraw) return; if (!canDraw) return;
buf.clear(); buf.clear();
buf.setBgColor(0); buf.setBgColor(0);
var x, y; var x, y;
x=185; x=185;
y=0; y=0;
buf.setFontAlign(1,-1); buf.setFontAlign(1,-1);
buf.setFontVector(94); buf.setFontVector(94);
time = require("locale").time(new Date(),1); time = require("locale").time(new Date(),1);
buf.setColor(1); buf.setColor(1);
buf.drawString(time.substring(0,2),x,y); buf.drawString(time.substring(0,2),x,y);
buf.drawString(time.substring(3,5),x,y+80); buf.drawString(time.substring(3,5),x,y+80);
g.reset(); g.reset();
g.drawImage(img,0,40); g.drawImage(img,0,40);
LED1.write(!pwrSav); LED1.write(!pwrSav);
} }
function drawWP(wp) { function drawWP(wp) {
buf.setColor(3); buf.setColor(3);
buf.setFontAlign(0,1); //left, bottom buf.setFontAlign(0,1); //left, bottom
buf.setFontVector(40); buf.setFontVector(40);
buf.drawString(wp,120,132); buf.drawString(wp,120,132);
} }
function drawSats(sats) { function drawSats(sats) {
buf.setColor(3); buf.setColor(3);
buf.setFont("6x8", 2); buf.setFont("6x8", 2);
buf.setFontAlign(1,1); //right, bottom buf.setFontAlign(1,1); //right, bottom
buf.drawString(sats,240,160); buf.drawString(sats,240,160);
} }
function drawMax(max) { function drawMax(max) {
buf.setFontVector(30); buf.setFontVector(30);
buf.setColor(2); buf.setColor(2);
buf.setFontAlign(0,1); //centre, bottom buf.setFontAlign(0,1); //centre, bottom
buf.drawString(max,120,164); buf.drawString(max,120,164);
} }
@ -369,7 +369,7 @@ if ( emulator ) {
fix.speed = 10 + (Math.random()*5); fix.speed = 10 + (Math.random()*5);
fix.alt = 354 + (Math.random()*50); fix.alt = 354 + (Math.random()*50);
fix.lat = -38.92; fix.lat = -38.92;
fix.lon = 175.7613350; fix.lon = 175.7613350;
fix.course = 245; fix.course = 245;
fix.satellites = 12; fix.satellites = 12;
fix.time = new Date(); fix.time = new Date();
@ -378,7 +378,7 @@ if ( emulator ) {
var m; var m;
var sp = '---'; var sp = '---';
var al = sp; var al = sp;
var di = sp; var di = sp;
var brg = ''; // bearing var brg = ''; // bearing
@ -390,15 +390,15 @@ if ( emulator ) {
var lon = '---.--'; var lon = '---.--';
var sats = sp; var sats = sp;
var vmg = sp; var vmg = sp;
// Waypoint name // Waypoint name
var wpName = wp.name; var wpName = wp.name;
if ( wpName == undefined || wpName == 'NONE' ) wpName = ''; if ( wpName == undefined || wpName == 'NONE' ) wpName = '';
wpName = wpName.substring(0,8); wpName = wpName.substring(0,8);
if (fix.fix) lf = fix; if (fix.fix) lf = fix;
if (lf.fix) { if (lf.fix) {
// Smooth data // Smooth data
@ -408,10 +408,10 @@ if ( emulator ) {
lf.smoothed = 1; lf.smoothed = 1;
if ( maxN <= 15 ) maxN++; if ( maxN <= 15 ) maxN++;
} }
// Bearing to waypoint // Bearing to waypoint
brg = bearing(lf,wp); brg = bearing(lf,wp);
// Current course // Current course
crs = lf.course; crs = lf.course;
@ -447,19 +447,19 @@ if ( emulator ) {
if ( di >= 100 ) di = parseFloat(di).toFixed(1); if ( di >= 100 ) di = parseFloat(di).toFixed(1);
if ( di >= 1000 ) di = parseFloat(di).toFixed(0); if ( di >= 1000 ) di = parseFloat(di).toFixed(0);
if (isNaN(di)) di = '------'; if (isNaN(di)) di = '------';
// Age of last fix (secs) // Age of last fix (secs)
age = Math.max(0,Math.round(getTime())-(lf.time.getTime()/1000)); age = Math.max(0,Math.round(getTime())-(lf.time.getTime()/1000));
// Lat / Lon // Lat / Lon
ns = 'N'; ns = 'N';
if ( lf.lat < 0 ) ns = 'S'; if ( lf.lat < 0 ) ns = 'S';
lat = Math.abs(lf.lat.toFixed(2)); lat = Math.abs(lf.lat.toFixed(2));
ew = 'E'; ew = 'E';
if ( lf.lon < 0 ) ew = 'W'; if ( lf.lon < 0 ) ew = 'W';
lon = Math.abs(lf.lon.toFixed(2)); lon = Math.abs(lf.lon.toFixed(2));
// Sats // Sats
if ( age > 10 ) { if ( age > 10 ) {
sats = 'Age:'+Math.round(age); sats = 'Age:'+Math.round(age);
@ -573,7 +573,7 @@ if ( emulator ) {
ew:ew ew:ew
}); });
} }
if ( cfg.modeA == 5 ) { if ( cfg.modeA == 5 ) {
// Large clock // Large clock
drawClock(); drawClock();
@ -585,14 +585,14 @@ function prevScrn() {
cfg.modeA = cfg.modeA-1; cfg.modeA = cfg.modeA-1;
if ( cfg.modeA < 0 ) cfg.modeA = 5; if ( cfg.modeA < 0 ) cfg.modeA = 5;
savSettings(); savSettings();
onGPS(lf); onGPS(lf);
} }
function nextScrn() { function nextScrn() {
cfg.modeA = cfg.modeA+1; cfg.modeA = cfg.modeA+1;
if ( cfg.modeA > 5 ) cfg.modeA = 0; if ( cfg.modeA > 5 ) cfg.modeA = 0;
savSettings(); savSettings();
onGPS(lf); onGPS(lf);
} }
// Next function on a screen // Next function on a screen
@ -610,7 +610,7 @@ function nextFunc(dur) {
function updateClock() { function updateClock() {
if (!canDraw) return; if (!canDraw) return;
if ( cfg.modeA != 5 ) return; if ( cfg.modeA != 5 ) return;
drawClock(); drawClock();
if ( emulator ) {maxSpd++;maxAlt++;} if ( emulator ) {maxSpd++;maxAlt++;}
} }
@ -661,10 +661,10 @@ function setButtons(){
var dur = e.time - e.lastTime; var dur = e.time - e.lastTime;
nextFunc(dur); nextFunc(dur);
}, BTN1, { edge:"falling",repeat:true}); }, BTN1, { edge:"falling",repeat:true});
// Power saving on/off // Power saving on/off
setWatch(function(e){ setWatch(function(e){
pwrSav=!pwrSav; pwrSav=!pwrSav;
if ( pwrSav ) { if ( pwrSav ) {
var s = require('Storage').readJSON('setting.json',1)||{}; var s = require('Storage').readJSON('setting.json',1)||{};
var t = s.timeout||10; var t = s.timeout||10;
@ -676,7 +676,7 @@ function setButtons(){
} }
LED1.write(!pwrSav); LED1.write(!pwrSav);
}, BTN2, {repeat:true,edge:"falling"}); }, BTN2, {repeat:true,edge:"falling"});
// BTN3 - next screen // BTN3 - next screen
setWatch(function(e){ setWatch(function(e){
nextScrn(); nextScrn();
@ -685,7 +685,7 @@ function setButtons(){
Bangle.on('lcdPower',function(on) { Bangle.on('lcdPower',function(on) {
if (!SCREENACCESS.withApp) return; if (!SCREENACCESS.withApp) return;
if (on) startDraw(); if (on) startDraw();
else stopDraw(); else stopDraw();
}); });
@ -702,7 +702,7 @@ Bangle.on('touch', function(button){
// == Main Prog // == Main Prog
// Read settings. // Read settings.
let cfg = require('Storage').readJSON('speedalt2.json',1)||{}; let cfg = require('Storage').readJSON('speedalt2.json',1)||{};
cfg.spd = cfg.spd||1; // Multiplier for speed unit conversions. 0 = use the locale values for speed cfg.spd = cfg.spd||1; // Multiplier for speed unit conversions. 0 = use the locale values for speed
@ -713,13 +713,13 @@ cfg.dist = cfg.dist||1000;// Multiplier for distnce unit conversions.
cfg.dist_unit = cfg.dist_unit||'km'; // Displayed altitude units cfg.dist_unit = cfg.dist_unit||'km'; // Displayed altitude units
cfg.colour = cfg.colour||0; // Colour scheme. cfg.colour = cfg.colour||0; // Colour scheme.
cfg.wp = cfg.wp||0; // Last selected waypoint for dist cfg.wp = cfg.wp||0; // Last selected waypoint for dist
cfg.modeA = cfg.modeA||0; // 0=Speed 1=Alt 2=Dist 3 = vmg 4=Position 5=Clock cfg.modeA = cfg.modeA||0; // 0=Speed 1=Alt 2=Dist 3 = vmg 4=Position 5=Clock
cfg.primSpd = cfg.primSpd||0; // 1 = Spd in primary, 0 = Spd in secondary cfg.primSpd = cfg.primSpd||0; // 1 = Spd in primary, 0 = Spd in secondary
cfg.spdFilt = cfg.spdFilt==undefined?true:cfg.spdFilt; cfg.spdFilt = cfg.spdFilt==undefined?true:cfg.spdFilt;
cfg.altFilt = cfg.altFilt==undefined?true:cfg.altFilt; cfg.altFilt = cfg.altFilt==undefined?true:cfg.altFilt;
cfg.touch = cfg.touch==undefined?true:cfg.touch; cfg.touch = cfg.touch==undefined?true:cfg.touch;
cfg.wptSfx = cfg.wptSfx==undefined?'':cfg.wptSfx; cfg.wptSfx = cfg.wptSfx==undefined?'':cfg.wptSfx;
if ( cfg.spdFilt ) var spdFilter = new KalmanFilter({R: 0.1 , Q: 1 }); if ( cfg.spdFilt ) var spdFilter = new KalmanFilter({R: 0.1 , Q: 1 });
if ( cfg.altFilt ) var altFilter = new KalmanFilter({R: 0.01, Q: 2 }); if ( cfg.altFilt ) var altFilter = new KalmanFilter({R: 0.01, Q: 2 });
@ -749,7 +749,7 @@ var SCREENACCESS = {
withApp:true, withApp:true,
request:function(){this.withApp=false;stopDraw();}, request:function(){this.withApp=false;stopDraw();},
release:function(){this.withApp=true;startDraw();} release:function(){this.withApp=true;startDraw();}
}; };
var gpssetup; var gpssetup;
try { try {

View File

@ -2,12 +2,13 @@
"id": "speedalt2", "id": "speedalt2",
"name": "GPS Adventure Sports II", "name": "GPS Adventure Sports II",
"shortName":"GPS Adv Sport II", "shortName":"GPS Adv Sport II",
"version":"0.17", "version":"0.18",
"description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.", "description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",
"tags": "tool,outdoors", "tags": "tool,outdoors",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS"],
"dependencies" : { "waypoints":"type" },
"readme": "README.md", "readme": "README.md",
"allow_emulator": true, "allow_emulator": true,
"storage": [ "storage": [
@ -16,10 +17,6 @@
{"name":"speedalt2.settings.js","url":"settings.js"} {"name":"speedalt2.settings.js","url":"settings.js"}
], ],
"data": [ "data": [
{"name":"speedalt2.json"}, {"name":"speedalt2.json"}
{"name":"waypoints.json"},
{"name":"waypoints1.json"},
{"name":"waypoints2.json"},
{"name":"waypoints3.json"}
] ]
} }

View File

@ -1,3 +1,4 @@
0.01: New app! 0.01: New app!
0.02: Make Bangle.js 2 compatible 0.02: Make Bangle.js 2 compatible
0.03: Silently use built in heading when no magnav calibration file is present 0.03: Silently use built in heading when no magnav calibration file is present
0.04: Move waypoints.json (and editor) to 'waypoints' app

View File

@ -61,58 +61,10 @@ The app indicates that WP2 is now marked by adding the prefix @ to
it's name. The distance should be small as shown in the screen shot it's name. The distance should be small as shown in the screen shot
as you have just marked your current location. as you have just marked your current location.
## Waypoint JSON file ## Setting Waypoints
When the app is loaded from the app loader, a file named
`waypoints.json` is loaded along with the javascript etc. The file
has the following contents:
```
[
{
"name":"NONE"
},
{
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
},
{ "name":"WP0" },
{ "name":"WP1" },
{ "name":"WP2" },
{ "name":"WP3" },
{ "name":"WP4" }
]
```
The file contains the initial NONE waypoint which is useful if you
just want to display course and speed. The next two entries are
waypoints to No 10 Downing Street and to Stone Henge - obtained from
Google Maps. The last five entries are entries which can be *marked*.
You add and delete entries using the Web IDE to load and then save
the file from and to watch storage. The app itself does not limit the
number of entries although it does load the entire file into RAM
which will obviously limit this.
## Waypoint Editor
Clicking on the download icon of gpsnav in the app loader invokes the
waypoint editor. The editor downloads and displays the current
`waypoints.json` file. Clicking the `Edit` button beside an entry
causes the entry to be deleted from the list and displayed in the
edit boxes. It can be restored - by clicking the `Add waypoint`
button. A new markable entry is created by using the `Add name`
button. The edited `waypoints.json` file is uploaded to the Bangle by
clicking the `Upload` button.
Check out the documentation for the `Waypoints` app. This provides
the ability to set waypoints from your browser.
## Calibration of the Compass ## Calibration of the Compass

View File

@ -50,7 +50,7 @@ function drawCompass(course) {
if(!candraw) return; if(!candraw) return;
if (Math.abs(previous.course - course) < 9) return; // reduce number of draws due to compass jitter if (Math.abs(previous.course - course) < 9) return; // reduce number of draws due to compass jitter
previous.course = course; previous.course = course;
buf1.setColor(1); buf1.setColor(1);
buf1.fillCircle(buf1.getWidth()/2,buf1.getHeight()/2,79*scale); buf1.fillCircle(buf1.getWidth()/2,buf1.getHeight()/2,79*scale);
buf1.setColor(0); buf1.setColor(0);
@ -63,10 +63,10 @@ function drawCompass(course) {
/***** COMPASS CODE ***********/ /***** COMPASS CODE ***********/
var heading = 0; var heading = 0;
function newHeading(m,h){ function newHeading(m,h){
var s = Math.abs(m - h); var s = Math.abs(m - h);
var delta = (m>h)?1:-1; var delta = (m>h)?1:-1;
if (s>=180){s=360-s; delta = -delta;} if (s>=180){s=360-s; delta = -delta;}
if (s<2) return h; if (s<2) return h;
var hd = h + delta*(1 + Math.round(s/5)); var hd = h + delta*(1 + Math.round(s/5));
if (hd<0) hd+=360; if (hd<0) hd+=360;
@ -147,9 +147,9 @@ function drawN(){
var bs = wp_bearing.toString(); var bs = wp_bearing.toString();
bs = wp_bearing<10?"00"+bs : wp_bearing<100 ?"0"+bs : bs; bs = wp_bearing<10?"00"+bs : wp_bearing<100 ?"0"+bs : bs;
var dst = loc.distance(dist); var dst = loc.distance(dist);
// -1=left (default), 0=center, 1=right // -1=left (default), 0=center, 1=right
// show distance on the left // show distance on the left
if (previous.dst !== dst) { if (previous.dst !== dst) {
previous.dst = dst; previous.dst = dst;
@ -159,7 +159,7 @@ function drawN(){
buf2.drawString(dst,0,0); buf2.drawString(dst,0,0);
flip2_bw(0, g.getHeight()-40*scale); flip2_bw(0, g.getHeight()-40*scale);
} }
// bearing, place in middle at bottom of compass // bearing, place in middle at bottom of compass
if (previous.bs !== bs) { if (previous.bs !== bs) {
previous.bs = bs; previous.bs = bs;
@ -192,7 +192,7 @@ function onGPS(fix) {
if (fix!==undefined){ if (fix!==undefined){
satellites = fix.satellites; satellites = fix.satellites;
} }
if (candraw) { if (candraw) {
if (fix!==undefined && fix.fix==1){ if (fix!==undefined && fix.fix==1){
dist = distance(fix,wp); dist = distance(fix,wp);
@ -240,7 +240,7 @@ function setButtons(){
else { doselect(); } else { doselect(); }
}); });
} }
Bangle.on('lcdPower',function(on) { Bangle.on('lcdPower',function(on) {
if (on) { if (on) {
clear_previous(); clear_previous();
@ -250,7 +250,7 @@ Bangle.on('lcdPower',function(on) {
} }
}); });
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}]; var waypoints = require("waypoints").load();
wp=waypoints[0]; wp=waypoints[0];
function nextwp(inc){ function nextwp(inc){
@ -266,7 +266,7 @@ function doselect(){
if (selected && wpindex!=0 && waypoints[wpindex].lat===undefined && savedfix.fix) { if (selected && wpindex!=0 && waypoints[wpindex].lat===undefined && savedfix.fix) {
waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon}; waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon};
wp = waypoints[wpindex]; wp = waypoints[wpindex];
require("Storage").writeJSON("waypoints.json", waypoints); require("waypoints").save(waypoints);
} }
selected=!selected; selected=!selected;
drawN(); drawN();

View File

@ -1,16 +1,15 @@
{ {
"id": "waypointer", "id": "waypointer",
"name": "Way Pointer", "name": "Way Pointer",
"version": "0.03", "version": "0.04",
"description": "Navigate to a waypoint using the GPS for bearing and compass to point way, uses the same waypoint interface as GPS Navigation", "description": "Navigate to a waypoint using the GPS for bearing and compass to point way, uses the same waypoint interface as GPS Navigation",
"icon": "waypointer.png", "icon": "waypointer.png",
"tags": "tool,outdoors,gps", "tags": "tool,outdoors,gps",
"supports": ["BANGLEJS", "BANGLEJS2"], "supports": ["BANGLEJS", "BANGLEJS2"],
"dependencies" : { "waypoints":"type" },
"readme": "README.md", "readme": "README.md",
"interface": "waypoints.html",
"storage": [ "storage": [
{"name":"waypointer.app.js","url":"app.js"}, {"name":"waypointer.app.js","url":"app.js"},
{"name":"waypointer.img","url":"icon.js","evaluate":true} {"name":"waypointer.img","url":"icon.js","evaluate":true}
], ]
"data": [{"name":"waypoints.json","url":"waypoints.json"}]
} }

View File

@ -1,170 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
</head>
<body>
<h4>List of waypoints</h4>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Lat.</th>
<th>Long.</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="waypoints">
</tbody>
</table>
<br>
<h4>Add a new waypoint</h4>
<form id="add_waypoint_form">
<div class="columns">
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" type="text" id="add_waypoint_name" placeholder="Name">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_latitude" placeholder="Lat">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_longtitude" placeholder="Long">
</div>
</div>
<div class="columns">
<div class="column col-3 col-xs-8">
<button id="add_name_button" class="btn btn-primary btn-sm">Add Name Only</button>
</div>
<div class="column col-3 col-xs-8">
<button id="add_waypoint_button" class="btn btn-primary btn-sm">Add Waypoint</button>
</div>
</div>
</form>
<br>
<button id="Download" class="btn btn-error">Reload</button> <button id="Upload" class="btn btn-primary">Upload</button>
<script src="../../core/lib/interface.js"></script>
<script>
var waypoints = []
var $name = document.getElementById('add_waypoint_name')
var $form = document.getElementById('add_waypoint_form')
var $button = document.getElementById('add_waypoint_button')
var $name_button = document.getElementById('add_name_button')
var $latitude = document.getElementById('add_latitude')
var $longtitude = document.getElementById('add_longtitude')
var $list = document.getElementById('waypoints')
function compare(a, b){
var x = a.name.toLowerCase();
var y = b.name.toLowerCase();
if (x=="none") {return -1};
if (y=="none") {return 1};
if (x < y) {return -1;}
if (x > y) {return 1;}
return 0;
}
$button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
var lat = parseFloat($latitude.value);
var lon = parseFloat($longtitude.value);
waypoints.push({
name, lat,lon,
});
waypoints.sort(compare);
renderWaypoints()
$name.value = ''
$latitude.value = (0).toPrecision(5);
$longtitude.value = (0).toPrecision(5);
});
$name_button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
waypoints.push({
name
});
waypoints.sort(compare);
renderWaypoints()
$name.value = ''
$latitude.value = 0.0000
$longtitude.value = 0.0000
});
function removeWaypoint(index){
$name.value = waypoints[index].name
$latitude.value = waypoints[index].lat
$longtitude.value = waypoints[index].lon
waypoints = waypoints.filter((p,i) => i!==index)
renderWaypoints()
}
function renderWaypoints(){
$list.innerHTML = ''
waypoints.forEach((waypoint,index) => {
var $waypoint = document.createElement('tr')
if (index==0){
$waypoint.innerHTML = `<td>${waypoint.name}</td>`
} else if(waypoint.lat==undefined){
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>------</td><td>-----</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
} else {
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>${waypoint.lat}</td><td>${waypoint.lon}</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
}
$list.appendChild($waypoint)
})
$name.focus()
}
function downloadJSONfile(fileid, callback) {
Puck.write(`\x10(function() {
var pts = require("Storage").readJSON("${fileid}")||[{name:"NONE"}];
Bluetooth.print(JSON.stringify(pts));
})()\n`,contents=>{
var storedpts = JSON.parse(contents);
callback(storedpts);
});
}
function uploadFile(fileid, contents) {
Puck.write(`\x10(function() {
require("Storage").write("${fileid}",'${contents}');
Bluetooth.print("OK");
})()\n`,ret=>{
console.log("uploadFile",ret);
});
}
function gotStored(pts){
waypoints = pts;
renderWaypoints();
}
function onInit() {
downloadJSONfile("waypoints.json", gotStored);
}
document.getElementById("Download").addEventListener("click", function() {
downloadJSONfile("waypoints.json", gotStored);
});
document.getElementById("Upload").addEventListener("click", function() {
var data = JSON.stringify(waypoints);
uploadFile("waypoints.json",data);
});
</script>
</body>
</html>

View File

@ -1,20 +0,0 @@
[
{
"name":"NONE"
},
{
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
},
{ "name":"WP0" },
{ "name":"WP1" },
{ "name":"WP2" },
{ "name":"WP3" },
{ "name":"WP4" }
]

1
apps/waypoints/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New App!

56
apps/waypoints/README.md Normal file
View File

@ -0,0 +1,56 @@
# Waypoints
This app provides a common way to set up the `waypoints.json` file,
which several other apps rely on for navigation.
## Waypoint JSON file
When the app is loaded from the app loader, a file named
`waypoints.json` is loaded along with the javascript etc. The file
has the following contents:
```
[
{
"name":"NONE"
},
{
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
},
{ "name":"WP0" },
{ "name":"WP1" },
{ "name":"WP2" },
{ "name":"WP3" },
{ "name":"WP4" }
]
```
The file contains the initial NONE waypoint which is useful if you
just want to display course and speed. The next two entries are
waypoints to No 10 Downing Street and to Stone Henge - obtained from
Google Maps. The last five entries are entries which can be *marked*.
You add and delete entries using the Web IDE to load and then save
the file from and to watch storage. The app itself does not limit the
number of entries although it does load the entire file into RAM
which will obviously limit this.
## Waypoint Editor
Clicking on the download icon of `Waypoints` in the app loader invokes the
waypoint editor. The editor downloads and displays the current
`waypoints.json` file. Clicking the `Edit` button beside an entry
causes the entry to be deleted from the list and displayed in the
edit boxes. It can be restored - by clicking the `Add waypoint`
button. A new markable entry is created by using the `Add name`
button. The edited `waypoints.json` file is uploaded to the Bangle by
clicking the `Upload` button.

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA=="))

34
apps/waypoints/app.js Normal file
View File

@ -0,0 +1,34 @@
// place your const, vars, functions or classes here
// clear the screen
g.clear();
var n = 0;
// redraw the screen
function draw() {
g.reset().clearRect(Bangle.appRect);
g.setFont("6x8").setFontAlign(0,0).drawString("Up / Down",g.getWidth()/2,g.getHeight()/2 - 20);
g.setFont("Vector",60).setFontAlign(0,0).drawString(n,g.getWidth()/2,g.getHeight()/2 + 30);
}
// Respond to user input
Bangle.setUI({mode: "updown"}, function(dir) {
if (dir<0) {
n--;
draw();
} else if (dir>0) {
n++;
draw();
} else {
n = 0;
draw();
}
});
// First draw...
draw();
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();

BIN
apps/waypoints/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -4,6 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="stylesheet" href="../../css/spectre.min.css"> <link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css"> <link rel="stylesheet" href="../../css/spectre-icons.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="anonymous"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css"> <link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css">
@ -11,6 +12,8 @@
html, body { height: 100% } html, body { height: 100% }
.flex-col { display:flex; flex-direction:column; height:100% } .flex-col { display:flex; flex-direction:column; height:100% }
#map { width:100%; height:100% } #map { width:100%; height:100% }
#tab-map { width:100%; height:100% }
#tab-list { width:100%; height:100% }
/* https://stackoverflow.com/a/58686215 */ /* https://stackoverflow.com/a/58686215 */
.arrow-icon { .arrow-icon {
@ -23,6 +26,7 @@
transform-origin: center center; transform-origin: center center;
font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif; font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif;
} }
</style> </style>
</head> </head>
<body> <body>
@ -32,8 +36,57 @@
<span id="status"></span> <span id="status"></span>
<span id="routestatus"></span> <span id="routestatus"></span>
</div> </div>
<div>
<ul class="tab tab-block">
<li class="tab-item active" id="tabitem-map">
<a href="#">Map</a>
</li>
<li class="tab-item" id="tabitem-list">
<a href="#">List</a>
</li>
</ul>
</div>
<div style="flex: 1"> <div style="flex: 1">
<div id="tab-map">
<div id="map"></div> <div id="map"></div>
</div>
<div id="tab-list" style="display:none">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Lat.</th>
<th>Long.</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="waypoints">
</tbody>
</table>
<br>
<h4>Add a new waypoint</h4>
<form id="add_waypoint_form">
<div class="columns">
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" type="text" id="add_waypoint_name" placeholder="Name">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_latitude" placeholder="Lat">
</div>
<div class="column col-3 col-xs-8">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_longtitude" placeholder="Long">
</div>
</div>
<div class="columns">
<div class="column col-3 col-xs-8">
<button id="add_name_button" class="btn btn-primary btn-sm">Add Name Only</button>
</div>
<div class="column col-3 col-xs-8">
<button id="add_waypoint_button" class="btn btn-primary btn-sm">Add Waypoint</button>
</div>
</div>
</form>
</div>
</div> </div>
</div> </div>
@ -44,8 +97,23 @@
<script src="../../core/lib/interface.js"></script> <script src="../../core/lib/interface.js"></script>
<script> <script>
var map;
var waypoints = []; var waypoints = [];
// ========================================================================== tabs
document.getElementById('tabitem-map').addEventListener('click',function() {
document.getElementById('tabitem-map').classList.remove("active");
document.getElementById('tabitem-list').classList.add("active");
document.getElementById('tab-map').style.display="block";
document.getElementById('tab-list').style.display="none";
});
document.getElementById('tabitem-list').addEventListener('click',function() {
document.getElementById('tabitem-map').classList.add("active");
document.getElementById('tabitem-list').classList.remove("active");
document.getElementById('tab-map').style.display="none";
document.getElementById('tab-list').style.display="block";
});
// ========================================================================== MAP
var map;
var mapmarkers = L.layerGroup(); var mapmarkers = L.layerGroup();
var searchresult = L.layerGroup(); var searchresult = L.layerGroup();
var dynamicarrow = L.layerGroup(); var dynamicarrow = L.layerGroup();
@ -100,9 +168,6 @@
L.featureGroup(getArrows(latlngs, 'black', 2, map)).addTo(dynamicarrow) L.featureGroup(getArrows(latlngs, 'black', 2, map)).addTo(dynamicarrow)
map.addLayer(dynamicarrow); map.addLayer(dynamicarrow);
}); });
clean();
renderAllWaypoints();
/*** status ***/ /*** status ***/
function clean() { function clean() {
@ -152,11 +217,11 @@
dirty(); dirty();
} }
function renderWaypoints(wps, isroute, parentidx) { function renderWaypointsMap(wps, isroute, parentidx) {
var latlngs = []; var latlngs = [];
for (var i = 0; i < wps.length; i++) { for (var i = 0; i < wps.length; i++) {
if (wps[i].route) { if (wps[i].route) {
renderWaypoints(wps[i].route, true, i); renderWaypointsMap(wps[i].route, true, i);
continue; continue;
} }
if (wps[i].lat == null || wps[i].lon == null) if (wps[i].lat == null || wps[i].lon == null)
@ -182,7 +247,8 @@
function renderAllWaypoints() { function renderAllWaypoints() {
mapmarkers.clearLayers(); mapmarkers.clearLayers();
renderWaypoints(waypoints, false, 0); renderWaypointsMap(waypoints, false, 0);
renderWaypointsList();
map.addLayer(mapmarkers); map.addLayer(mapmarkers);
} }
@ -234,51 +300,18 @@
/*** Bangle.js ***/ /*** Bangle.js ***/
function downloadJSONfile(fileid, callback) {
Puck.write(`\x10(function() {
var pts = require("Storage").readJSON("${fileid}")||[{name:"NONE"}];
Bluetooth.print(JSON.stringify(pts));
})()\n`, contents => {
var storedpts = JSON.parse(contents);
callback(storedpts);
clean();
});
}
function uploadFile(fileid, contents) {
Puck.write(`\x10(function() {
require("Storage").write("${fileid}",'${contents}');
Bluetooth.print("OK");
})()\n`, ret => {
console.log("uploadFile", ret);
if (ret == "OK")
clean();
});
}
function gotStored(pts) { function gotStored(pts) {
waypoints = pts; waypoints = pts;
var latlngs = waypoints.map(p => [p.lat, p.lon]); var latlngs = waypoints.filter(p => isFinite(p.lat)&&isFinite(p.lon)).map(p => [p.lat, p.lon]);
var poly = L.polygon(latlngs); var poly = L.polygon(latlngs);
map.fitBounds(poly.getBounds()); var bounds = poly.getBounds();
if (bounds.isValid())
map.fitBounds(bounds);
renderAllWaypoints(); renderAllWaypoints();
} }
function onInit() {
downloadJSONfile("waypoints.json", gotStored);
}
$('#download').on('click', function() {
downloadJSONfile("waypoints.json", gotStored);
});
$('#upload').click(function() {
var data = JSON.stringify(waypoints);
uploadFile("waypoints.json",data);
});
$('#statusarea').click(closeRoute); $('#statusarea').click(closeRoute);
/*** map arrows ***/ /*** map arrows ***/
@ -348,5 +381,136 @@
this.x = (round ? Math.round(x) : x); this.x = (round ? Math.round(x) : x);
this.y = (round ? Math.round(y) : y); this.y = (round ? Math.round(y) : y);
} }
// ========================================================================== LIST
var $name = document.getElementById('add_waypoint_name')
var $form = document.getElementById('add_waypoint_form')
var $button = document.getElementById('add_waypoint_button')
var $name_button = document.getElementById('add_name_button')
var $latitude = document.getElementById('add_latitude')
var $longtitude = document.getElementById('add_longtitude')
var $list = document.getElementById('waypoints')
function compare(a, b){
var x = a.name.toLowerCase();
var y = b.name.toLowerCase();
if (x=="none") {return -1};
if (y=="none") {return 1};
if (x < y) {return -1;}
if (x > y) {return 1;}
return 0;
}
$button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
var lat = parseFloat($latitude.value);
var lon = parseFloat($longtitude.value);
waypoints.push({
name, lat,lon,
});
waypoints.sort(compare);
renderAllWaypoints()
$name.value = ''
$latitude.value = (0).toPrecision(5);
$longtitude.value = (0).toPrecision(5);
});
$name_button.addEventListener('click', event => {
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
waypoints.push({
name
});
waypoints.sort(compare);
renderAllWaypoints()
$name.value = ''
$latitude.value = 0.0000
$longtitude.value = 0.0000
});
function removeWaypoint(index){
$name.value = waypoints[index].name
$latitude.value = waypoints[index].lat
$longtitude.value = waypoints[index].lon
waypoints = waypoints.filter((p,i) => i!==index)
renderAllWaypoints()
}
function renderWaypointsList(){
$list.innerHTML = ''
waypoints.forEach((waypoint,index) => {
var $waypoint = document.createElement('tr')
if (index==0){
$waypoint.innerHTML = `<td>${waypoint.name}</td><td></td><td></td>`
} else if(waypoint.lat==undefined){
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>------</td><td>-----</td>`;
} else {
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>${waypoint.lat.toFixed(6)}</td><td>${waypoint.lon.toFixed(6)}</td>`;
}
$waypoint.innerHTML += `<td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-delete"></i></button></td>`;
$list.appendChild($waypoint)
})
$name.focus()
}
function renderWaypoints() {
renderWaypointsList();
renderWaypointsMap();
}
// ========================================================================== UPLOAD/DOWNLOAD
function downloadJSONfile(fileid, callback) {
// TODO: use interface.js-provided stuff?
Puck.write(`\x10(function() {
var pts = require("Storage").readJSON("${fileid}")||[{name:"NONE"}];
Bluetooth.print(JSON.stringify(pts));
})()\n`, contents => {
if (contents=='[{name:"NONE"}]') contents="[]";
var storedpts = JSON.parse(contents);
callback(storedpts);
clean();
});
}
function uploadFile(fileid, contents) {
// TODO: use interface.js-provided stuff?
Puck.write(`\x10(function() {
require("Storage").write("${fileid}",'${contents}');
Bluetooth.print("OK");
})()\n`, ret => {
console.log("uploadFile", ret);
if (ret == "OK")
clean();
});
}
function onInit() {
downloadJSONfile("waypoints.json", gotStored);
}
$('#download').on('click', function() {
downloadJSONfile("waypoints.json", gotStored);
});
$('#upload').click(function() {
var data = JSON.stringify(waypoints);
uploadFile("waypoints.json",data);
});
// ========================================================================== FINALLY...
clean();
renderAllWaypoints();
</script> </script>
</body> </body>
</html>

7
apps/waypoints/lib.js Normal file
View File

@ -0,0 +1,7 @@
exports.load = (num) => {
return require("Storage").readJSON(`waypoints${num?`.${num}`:""}.json`)||[{name:"NONE"}];
};
exports.save = (waypoints,num) => {
require("Storage").writeJSON(`waypoints${num?`.${num}`:""}.json`, waypoints);
};

View File

@ -0,0 +1,20 @@
{ "id": "waypoints",
"name": "Waypoints",
"version":"0.01",
"description": "Provides 'waypoints.json' used by various navigation apps, as well as a way to edit it from the App Loader with maps or a list",
"icon": "app.png",
"tags": "tool,outdoors,gps",
"type": "waypoints",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"interface": "interface.html",
"storage": [
{"name":"waypoints","url":"lib.js"}
],
"data": [
{"name":"waypoints.json","url":"waypoints.json"},
{"name":"waypoints.1.json"},
{"name":"waypoints.2.json"},
{"name":"waypoints.3.json"}
]
}

View File

@ -0,0 +1,12 @@
[
{
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
}
]

View File

@ -1,2 +1,3 @@
... ...
0.02: First update with ChangeLog Added 0.02: First update with ChangeLog Added
0.03: Move waypoints.json (and editor) to 'waypoints' app

View File

@ -1,6 +1,6 @@
var loc = require("locale"); var loc = require("locale");
var waypoints = require("Storage").readJSON("waypoints.json") || []; var waypoints = require("waypoints").load();
var wp = waypoints[0]; var wp = waypoints[0];
if (wp == undefined) wp = {name:"NONE"}; if (wp == undefined) wp = {name:"NONE"};
var wp_bearing = 0; var wp_bearing = 0;
@ -196,7 +196,7 @@ function addCurrentWaypoint() {
} }
function saveWaypoints() { function saveWaypoints() {
require("Storage").writeJSON("waypoints.json", waypoints); require("waypoints").save(waypoints);
} }
function deleteWaypoint(w) { function deleteWaypoint(w) {

View File

@ -2,17 +2,16 @@
"id": "wpmoto", "id": "wpmoto",
"name": "Waypointer Moto", "name": "Waypointer Moto",
"shortName": "Waypointer Moto", "shortName": "Waypointer Moto",
"version": "0.02", "version": "0.03",
"description": "Waypoint-based motorcycle navigation aid", "description": "Waypoint-based motorcycle navigation aid",
"icon": "wpmoto.png", "icon": "wpmoto.png",
"tags": "tool,outdoors,gps", "tags": "tool,outdoors,gps",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot-menu.png"},{"url":"screenshot-delete.png"}], "screenshots": [{"url":"screenshot.png"},{"url":"screenshot-menu.png"},{"url":"screenshot-delete.png"}],
"readme": "README.md", "readme": "README.md",
"interface": "wpmoto.html", "dependencies" : { "waypoints":"type" },
"storage": [ "storage": [
{"name":"wpmoto.app.js","url":"app.js"}, {"name":"wpmoto.app.js","url":"app.js"},
{"name":"wpmoto.img","url":"icon.js","evaluate":true} {"name":"wpmoto.img","url":"icon.js","evaluate":true}
], ]
"data": [{"name":"waypoints.json","url":"waypoints.json"}]
} }

View File

@ -1,5 +0,0 @@
[
{
"name":"NONE"
},
]

View File

@ -77,12 +77,13 @@ const APP_KEYS = [
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports']; const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports'];
const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate']; const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
const SUPPORTS_DEVICES = ["BANGLEJS","BANGLEJS2"]; // device IDs allowed for 'supports' const SUPPORTS_DEVICES = ["BANGLEJS","BANGLEJS2"]; // device IDs allowed for 'supports'
const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","textinput","scheduler","notify","locale","settings"]; // values allowed for "type" field const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","textinput","scheduler","notify","locale","settings","waypoints"]; // values allowed for "type" field
const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info
const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ]; const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
const GRANDFATHERED_ICONS = ["s7clk", "snek", "astral", "alpinenav", "slomoclock", "arrow", "pebble", "rebble"]; const GRANDFATHERED_ICONS = ["s7clk", "snek", "astral", "alpinenav", "slomoclock", "arrow", "pebble", "rebble"];
const INTERNAL_FILES_IN_APP_TYPE = { // list of app types and files they SHOULD provide... const INTERNAL_FILES_IN_APP_TYPE = { // list of app types and files they SHOULD provide...
'textinput' : ['textinput'], 'textinput' : ['textinput'],
'waypoints' : ['waypoints'],
// notify? // notify?
}; };