1
0
Fork 0

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

View File

@ -1,16 +1,15 @@
{
"id": "gpsnav",
"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",
"icon": "icon.png",
"tags": "tool,outdoors,gps",
"supports": ["BANGLEJS"],
"readme": "README.md",
"interface": "waypoints.html",
"dependencies" : { "waypoints":"type" },
"storage": [
{"name":"gpsnav.app.js","url":"app.min.js"},
{"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.12: Added stepo2 as a replacement for stepo and digi
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)
- 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
- Display current step count in a doughnut gauge
- Show step count in the middle of the doughnut gauge
@ -208,14 +208,8 @@ 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.
Clicking on the download icon of `Waypoints` in the app loader invokes the
waypoint editor. See the `Waypoints` app for more information.
### Calibration of the Compass

View File

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

View File

@ -1,14 +1,14 @@
{
"id": "kitchen",
"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'",
"icon": "kitchen.png",
"type": "clock",
"tags": "tool,outdoors,gps",
"supports": ["BANGLEJS"],
"readme": "README.md",
"interface": "waypoints.html",
"dependencies" : { "waypoints":"type" },
"storage": [
{"name":"kitchen.app.js","url":"kitchen.app.js"},
{"name":"stepo2.kit.js","url":"stepo2.kit.js"},
@ -16,6 +16,5 @@
{"name":"gps.kit.js","url":"gps.kit.js"},
{"name":"compass.kit.js","url":"compass.kit.js"},
{"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.
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.
@ -36,7 +36,7 @@ BTN4 : Left Display Tap : Swaps which figure is in the large display. You can ha
## 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
@ -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.
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 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.
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.)
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.
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.
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() {
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<0) cfg.wp = w.length-1;
savSettings();

View File

@ -8,12 +8,12 @@
"type": "app",
"tags": "tool,outdoors",
"supports": ["BANGLEJS","BANGLEJS2"],
"dependencies" : { "waypoints":"type" },
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"speedalt.app.js","url":"app.js"},
{"name":"speedalt.img","url":"app-icon.js","evaluate":true},
{"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.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.18: Move waypoints.json to 'waypoints' app (with editor)

View File

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

View File

@ -2,12 +2,13 @@
"id": "speedalt2",
"name": "GPS Adventure Sports 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.",
"icon": "app.png",
"type": "app",
"tags": "tool,outdoors",
"supports": ["BANGLEJS"],
"dependencies" : { "waypoints":"type" },
"readme": "README.md",
"allow_emulator": true,
"storage": [
@ -16,10 +17,6 @@
{"name":"speedalt2.settings.js","url":"settings.js"}
],
"data": [
{"name":"speedalt2.json"},
{"name":"waypoints.json"},
{"name":"waypoints1.json"},
{"name":"waypoints2.json"},
{"name":"waypoints3.json"}
{"name":"speedalt2.json"}
]
}

View File

@ -1,3 +1,4 @@
0.01: New app!
0.02: Make Bangle.js 2 compatible
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
as you have just marked your current location.
## 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 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.
## Setting Waypoints
Check out the documentation for the `Waypoints` app. This provides
the ability to set waypoints from your browser.
## Calibration of the Compass

View File

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

View File

@ -1,16 +1,15 @@
{
"id": "waypointer",
"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",
"icon": "waypointer.png",
"tags": "tool,outdoors,gps",
"supports": ["BANGLEJS", "BANGLEJS2"],
"dependencies" : { "waypoints":"type" },
"readme": "README.md",
"interface": "waypoints.html",
"storage": [
{"name":"waypointer.app.js","url":"app.js"},
{"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">
<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="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">
@ -11,6 +12,8 @@
html, body { height: 100% }
.flex-col { display:flex; flex-direction:column; height:100% }
#map { width:100%; height:100% }
#tab-map { width:100%; height:100% }
#tab-list { width:100%; height:100% }
/* https://stackoverflow.com/a/58686215 */
.arrow-icon {
@ -23,6 +26,7 @@
transform-origin: center center;
font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif;
}
</style>
</head>
<body>
@ -32,8 +36,57 @@
<span id="status"></span>
<span id="routestatus"></span>
</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 id="tab-map">
<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>
@ -44,8 +97,23 @@
<script src="../../core/lib/interface.js"></script>
<script>
var map;
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 searchresult = L.layerGroup();
var dynamicarrow = L.layerGroup();
@ -100,9 +168,6 @@
L.featureGroup(getArrows(latlngs, 'black', 2, map)).addTo(dynamicarrow)
map.addLayer(dynamicarrow);
});
clean();
renderAllWaypoints();
/*** status ***/
function clean() {
@ -152,11 +217,11 @@
dirty();
}
function renderWaypoints(wps, isroute, parentidx) {
function renderWaypointsMap(wps, isroute, parentidx) {
var latlngs = [];
for (var i = 0; i < wps.length; i++) {
if (wps[i].route) {
renderWaypoints(wps[i].route, true, i);
renderWaypointsMap(wps[i].route, true, i);
continue;
}
if (wps[i].lat == null || wps[i].lon == null)
@ -182,7 +247,8 @@
function renderAllWaypoints() {
mapmarkers.clearLayers();
renderWaypoints(waypoints, false, 0);
renderWaypointsMap(waypoints, false, 0);
renderWaypointsList();
map.addLayer(mapmarkers);
}
@ -234,51 +300,18 @@
/*** 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) {
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);
map.fitBounds(poly.getBounds());
var bounds = poly.getBounds();
if (bounds.isValid())
map.fitBounds(bounds);
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);
/*** map arrows ***/
@ -348,5 +381,136 @@
this.x = (round ? Math.round(x) : x);
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>
</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.03: Move waypoints.json (and editor) to 'waypoints' app

View File

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

View File

@ -2,17 +2,16 @@
"id": "wpmoto",
"name": "Waypointer Moto",
"shortName": "Waypointer Moto",
"version": "0.02",
"version": "0.03",
"description": "Waypoint-based motorcycle navigation aid",
"icon": "wpmoto.png",
"tags": "tool,outdoors,gps",
"supports": ["BANGLEJS","BANGLEJS2"],
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot-menu.png"},{"url":"screenshot-delete.png"}],
"readme": "README.md",
"interface": "wpmoto.html",
"dependencies" : { "waypoints":"type" },
"storage": [
{"name":"wpmoto.app.js","url":"app.js"},
{"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 DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
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 VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
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...
'textinput' : ['textinput'],
'waypoints' : ['waypoints'],
// notify?
};