Implement Waypointer Moto

pull/1350/head
James Stanley 2022-01-22 22:25:40 +00:00
parent 058d39a285
commit 8c4d97e547
22 changed files with 376 additions and 358 deletions

View File

@ -1,176 +1,158 @@
# Waypointer - navigate to waypoints
# Waypointer Moto
The app is aimed at navigation whilst walking. Please note that it
would be foolish in the extreme to rely on this as your only
navigation aid!
Waypointer Moto is a GPS navigation aid intended to be attached to
the handlebars of a motorcycle.
It uses the GPS to find out which direction it's
travelling and shows the direction and distance to the destination
"as the crow flies". It gives you an indication of where to go,
but exploring and navigating the environment is left up to the user.
Please refer to the section on calibration of the compass. This
should be done each time the app is going to be used.
![](watch-on-bike.jpeg)
The main part of the display is a compass arrow that points in the
direction you need to walk in. Once you have selected a waypoint a
bearing from your current position (received from a GPS fix) is
calculated and the compass is set to point in that direction. If the
arrow is pointing to the left, turning left should straighten the arrow
up so that it is pointing straight ahead.
(Please note that it would be foolish in the extreme to rely on this
as your only navigation aid! Make sure you read this entire document
before using the app for navigation so that you know the drawbacks
and shortcomings.)
## App usage
![](waypointer_screenshot.jpg)
### Main screen
The large digits are the bearing from the current position. On the
left is the distance to the waypoint in local units. The top of the
display is a circular compass which displays the direction you will
need to travel in to reach the selected waypoint. The blue text is
the name of the current waypoint. NONE means that there is no
waypoint set and so bearing and distance will remain at 0. To select
a waypoint, press BTN2 (middle) and wait for the blue text to turn
white. Then use BTN1 and BTN3 to select a waypoint. The waypoint
choice is fixed by pressing BTN2 again. In the screen shot below a
waypoint giving the location of Stone Henge has been selected.
![](screenshot.png)
The screenshot above shows that Stone Henge is 259.9 miles from the
current location. To travel towards Stone Henge I need to turn
slightly left until the arrow is pointing straight ahead. As you
continue to walk in the pointed direction you should see the distance
to the waypoint reduce. The frequency of updates will depend on
which settings you have used in the GPS.
The main screen shows the direction arrow, the distance to the waypoint,
and the name of the selected waypoint.
At the top of the screen you can see two widgets. These are the [GPS
Power
Widget](https://github.com/espruino/BangleApps/tree/master/apps/widgps)
and the [Compass Power Indicator Widget]. These can be installed
seperately and provide you a indication of when the GPS and Compass
are switched on and drawing power.
It also shows the status of the GPS fix in the colour of the arrow:
* Red: no GPS fix at all
* Yellow: GPS location, but no GPS course (probably you're moving too slowly);
in this case the direction of travel comes from the compass bearing instead
of the GPS course, but note that the compass is unreliable
* White: GPS fix includes both location and course, and the GPS course is used
to determine the direction of travel
## Marking Waypoints
### Select a waypoint
The app lets you mark your current location as follows. There are
vacant slots in the waypoint file which can be allocated a
location. In the distributed waypoint file these are labelled WP0 to
WP4. Select one of these - WP2 is shown below.
![](screenshot-menu.png)
![](wp2_screenshot.jpg)
Press the middle button (`BTN2`) to enter the menu, choose a waypoint using
the up/down arrows, and use the middle button again to select a waypoint and
return to the main screen.
Bearing and distance are both zero as WP2 has currently no GPS
location associated with it. To mark the location, press BTN2.
### Add a waypoint
![](wp2_saved.jpg)
Press the middle button (`BTN2`) to enter the menu, and select the "+ Here"
option. This will add a waypoint named "WP*n*" marking your current location,
where "*n*" is the next unused number.
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.
### Delete a waypoint
## Waypoint JSON file
![](screenshot-delete.png)
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:
Select a waypoint using the menu. Once the waypoint is selected and you're
back on the main screen, press either the top or bottom button (`BTN1` or
`BTN3`). Confirm that you want to delete the waypoint with the middle
button (`BTN2`).
## Waypoint editor
```
[
{
"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" }
]
```
With the Bangle.js app loader connected to the watch, find the
Waypointer Moto app and click on the floppy disk icon:
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*.
![](floppy-disk.png)
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.
This will load up the waypoint editor:
![](editor.png)
## Waypoint Editor
### Add a waypoint
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.
Use the map to find your destination. Clicking on the map will
populate the latitude/longitude input boxes with the coordinates
of the point you clicked on. Type in a name for the waypoint and
click "Add Waypoint". Click "Upload" to send the updated list of
waypoints to the watch.
### Edit a waypoint
## Calibration of the Compass
Click on the pencil icon next to the waypoint you wish to edit.
This will remove the waypoint from the list and populate the
input boxes.
Edit the coordinates by hand, or by clicking on the map. Edit
the name if you want. Click "Add Waypoint" to save the waypoint
back to the list. Click "Upload" to send the updated list of
waypoints to the watch.
The Compass should be calibrated before using the App to navigate to
a waypoint (or a series of waypoints). To do this use either the
Arrow Compass or the [Navigation
Compass](https://github.com/espruino/BangleApps/tree/master/apps/magnav).
Open the compass app and clicking on BTN3. The calibration process
takes 30 seconds during which you should move the watch slowly
through figures of 8. It is important that during calibration the
watch is fully rotated around each of it axes. If the app does give
the correct direction heading or is not stable with respect to tilt
and roll - redo the calibration by pressing *BTN3*. Calibration data
is recorded in a storage file named `magnav.json`.
### Delete a waypoint
Click on the pencil icon next to the waypoint you wish to edit.
This will remove the waypoint from the list.
Click "Upload" to send the updated list of waypoints to the watch.
## Advantages and Disadvantages
## Mounting the watch on the bike
This approach has some advantages and disadvantages. First following
the arrow is fairly easy to do and once the bearing has been
established it does not matter if there is not another GPS fix for a
while as the compass will continue to point in the general direction.
Second the GPS will only supply a course to the waypoint (a bearing)
once you are travelling above 8m/s or 28kph. This is not a practical
walking speed. 5kmph is considered a marching pace.
There is a 3d-printable "artificial wrist" which will fit over a 7/8"
handlebar and allow the watch strap to tighten up.
Alternatively, in a pinch you can strap the watch around a glove or a sponge
or anything else that will pad out the space so that the watch is a tight
fit.
One disadvantage is that the compass is not very accurate. I have
observed it being 20-30 degrees off when compared to a hiking
compass. Sometime its is necessary to walk in the opposite direction
for a bit to establish the correct direction to go in. The accuracy
of the compass is impacted by the magnetic clamps on the charging
cable, so it is particularly important that you recalibtrate the
compass after the watch has been charged. That said I have found I
am successfully able to follow a chain of waypoints as a route.
The 3d-printed part should be a snug fit on the handlebar so that it does
not flop around. If it is too loose, line it with a layer or 2 of tape.
[Download the handlebar mount STL »](handlebar-mount.stl)
[Download the handlebar mount FreeCAD source »](handlebar-mount.FCStd)
![](handlebar-mount.png)
![](handlebar-mount.jpeg)
## Comparison to Way Pointer
Compared to the original Way Pointer app, Waypointer Moto:
* removes the numerical display of compass bearing
* makes the distance text bigger
* uses a higher-resolution arrow icon
* has a visual indication of the GPS status (the arrow colour)
* uses GPS course instead of compass bearing
* has OpenStreetMap integration in the waypoint editor
* uses Bangle.js menus to select waypoints instead of custom UI
* can add new waypoints from inside the app without requiring a blank slot
* can delete waypoints from inside the app without needing the PC
* still uses the same `waypoints.json` file
## Gotchas
Waypointer Moto derives your current heading from the GPS course
rather than the compass, whenever GPS course is available.
The compass bearing is based on the angle the watch is held, but
the GPS course is based on the direction it's *travelling*. If the
watch is not aligned with the direction of travel of the vehicle
then the arrow will not point in the correct direction.
When travelling too slowly, there is no GPS course information, so the
app reverts to using the compass (and draws it in yellow), but
the compass is not very reliable, and I
have especially found it not to be reliable when placed on a motorcyle,
maybe because of all the metal in the immediate vicinity. So if
the arrow is not drawn in white, then you should probably not trust
it. If you're not sure, just ride in a straight line until the arrow
turns white again.
## Possible Future Enhancements
- Buzz when the GPS establishes its first fix.
- Add a small LED to show the status of the GPS during the phase of
establishing a first fix.
- Add an option to calibrate the Compass without having to use the
Arrow Compass or the Navigation Compass.
- Investigate the accuracy of the Compass and how it changes
throughout the day after the watch battery has been fully charged.
- Investigate the possibility of setting the GPS in low speed mode so
that a current course value can be obtained.
- Buzz when you arrive within 20m of a waypoint to signify arrival
- "routes" with multiple waypoints; automatically step from one
waypoint to the next when you get near to it
- some way to manually input coordinates directly on the watch
- make the text & arrow more legible in direct sunlight
- integrate a charging connector into the handlebar mount
- upstream the map integration to the other waypoint apps
## Acknowledgements
The majority of the code in this application is a merge of
Waypointer Moto is a project by [James Stanley](https://incoherency.co.uk/). It is a derivative of [Adam Schmalhofer's](https://github.com/adamschmalhofer) Way Pointer app, which is in turn a derivative of
[jeffmer's](https://github.com/jeffmer/JeffsBangleAppsDev) GPS
Navigation and Compass Navigation Applications.
Navigation and Compass Navigation apps.

View File

@ -1,71 +1,106 @@
var pal_by = new Uint16Array([0x0000,0xFFC0],0,1); // black, yellow
var pal_bw = new Uint16Array([0x0000,0xffff],0,1); // black, white
var pal_bb = new Uint16Array([0x0000,0x07ff],0,1); // black, blue
// having 3 2 color pallette keeps the memory requirement lower
var buf1 = Graphics.createArrayBuffer(160,160,1, {msb:true});
var buf2 = Graphics.createArrayBuffer(80,40,1, {msb:true});
var arrow_img = require("heatshrink").decompress(atob("lEowIPMjAEDngEDvwED/4DCgP/wAEBgf/4AEBg//8AEBh//+AEBj///AEBn///gEBv///wmCAAImCAAIoBFggE/AkaaEABo="));
function flip1(x,y) {
g.drawImage({width:160,height:160,bpp:1,buffer:buf1.buffer, palette:pal_by},x,y);
buf1.clear();
}
function flip2_bw(x,y) {
g.drawImage({width:80,height:40,bpp:1,buffer:buf2.buffer, palette:pal_bw},x,y);
buf2.clear();
}
function flip2_bb(x,y) {
g.drawImage({width:80,height:40,bpp:1,buffer:buf2.buffer, palette:pal_bb},x,y);
buf2.clear();
}
var candraw = true;
var wp_bearing = 0;
var direction = 0;
var wpindex=0;
var loc = require("locale");
var selected = false;
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
var wp = waypoints[0];
var wp_bearing = 0;
var candraw = true;
var direction = 0;
var dist = 0;
var savedfix;
var previous = {
bs: '',
dst: '',
wp_name: '',
course: 0,
course: 180,
selected: false,
};
// clear the attributes that control the display refresh
function clear_previous() {
previous.bs = '-';
previous.dst = '-';
previous.wp_name = '-';
previous.course = -999;
/*** Drawing ***/
var pal_by = new Uint16Array([0x0000,0xFFC0],0,1); // black, yellow
var pal_bw = new Uint16Array([0x0000,0xffff],0,1); // black, white
var pal_bb = new Uint16Array([0x0000,0x07ff],0,1); // black, blue
var pal_br = new Uint16Array([0x0000,0xf800],0,1); // black, red
var pal_compass = pal_by;
var buf = Graphics.createArrayBuffer(160,160,1, {msb:true});
var arrow_img = require("heatshrink").decompress(atob("vF4wJC/AEMYBxs8Bxt+Bxv/BpkB/+ABxcD//ABxcH//gBxcP//wBxcf//4Bxc///8Bxd///+OxgABOxgABPBR2BAAJ4KOwIABPBR2BAAJ4KOwIABPBR2BAAJ4KOwIABPBQNCPBR2DPBR2DPBR2DPBR2DPBR2DPBR2DPBR2DPBQNEPBB2FPBB2FPBB2FPBB2FPBB2FPBB2FPBB2FPBANGPAx2HPAx2HPAx2HPAx2HPAx2HPAx2HeJTeJB34O/B34O/B34O/B34O/B34O/B34O/B34O/B34OTAH4AT"));
function flip1(x,y,palette) {
g.drawImage({width:160,height:160,bpp:1,buffer:buf.buffer, palette:palette},x,y);
buf.clear();
}
function flip2_bw(x,y) {
g.drawImage({width:160,height:40,bpp:1,buffer:buf.buffer, palette:pal_bw},x,y);
buf.clear();
}
function flip2_bb(x,y) {
g.drawImage({width:160,height:40,bpp:1,buffer:buf.buffer, palette:pal_bb},x,y);
buf.clear();
}
function drawCompass(course) {
if(!candraw) return;
if (Math.abs(previous.course - course) < 9) return; // reduce number of draws due to compass jitter
if (!candraw) return;
previous.course = course;
buf1.setColor(1);
buf1.fillCircle(80,80,79,79);
buf1.setColor(0);
buf1.fillCircle(80,80,69,69);
buf1.setColor(1);
buf1.drawImage(arrow_img, 80, 80, {scale:3, rotate:radians(course)} );
flip1(40, 30);
buf.setColor(1);
buf.fillCircle(80,80, 79);
buf.setColor(0);
buf.fillCircle(80,80, 69);
buf.setColor(1);
buf.drawImage(arrow_img, 80, 80, {rotate:radians(course)} );
var palette = pal_br;
if (savedfix !== undefined && savedfix.fix !== 0) palette = pal_compass;
flip1(40, 30, palette);
}
/***** COMPASS CODE ***********/
function drawN(force){
if (!candraw) return;
buf.setFont("Vector",24);
var dst = loc.distance(dist);
// distance on left
if (force || previous.dst !== dst) {
previous.dst = dst;
buf.setColor(1);
buf.setFontAlign(-1, -1);
buf.setFont("Vector",40);
buf.drawString(dst,0,0);
flip2_bw(8, 200);
}
// waypoint name on right
if (force || previous.wp_name !== wp.name) {
previous.wp_name = wp.name;
buf.setColor(1);
buf.setFontAlign(1, -1);
buf.setFont("Vector", 15);
buf.drawString(wp.name, 80, 0);
flip2_bw(160, 220);
}
}
function drawAll(force) {
if (!candraw) return;
g.setColor(1,1,1);
drawN(force);
drawCompass(direction);
}
/*** 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;
@ -93,10 +128,18 @@ function tiltfixread(O,S){
return psi;
}
// Note actual mag is 360-m, error in firmware
function read_compass() {
var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
heading = newHeading(d,heading);
function read_heading() {
if (savedfix !== undefined && !isNaN(savedfix.course)) {
Bangle.setCompassPower(0);
heading = savedfix.course;
pal_compass = pal_bw;
} else {
var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
Bangle.setCompassPower(1);
heading = newHeading(d,heading);
pal_compass = pal_by;
}
direction = wp_bearing - heading;
if (direction < 0) direction += 360;
if (direction > 360) direction -= 360;
@ -104,12 +147,7 @@ function read_compass() {
}
/***** END Compass ***********/
var speed = 0;
var satellites = 0;
var wp;
var dist=0;
/*** Maths ***/
function radians(a) {
return a*Math.PI/180;
@ -125,8 +163,7 @@ function bearing(a,b){
var alat = radians(a.lat);
var blat = radians(b.lat);
var y = Math.sin(delta) * Math.cos(blat);
var x = Math.cos(alat)*Math.sin(blat) -
Math.sin(alat)*Math.cos(blat)*Math.cos(delta);
var x = Math.cos(alat)*Math.sin(blat) - Math.sin(alat)*Math.cos(blat)*Math.cos(delta);
return Math.round(degrees(Math.atan2(y, x)));
}
@ -136,133 +173,106 @@ function distance(a,b){
return Math.round(Math.sqrt(x*x + y*y) * 6371000);
}
/*** Waypoints ***/
function drawN(){
buf2.setFont("Vector",24);
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
buf2.setColor(1);
buf2.setFontAlign(-1,-1);
buf2.setFont("Vector", 20);
buf2.drawString(dst,0,0);
flip2_bw(0, 200);
}
// bearing, place in middle at bottom of compass
if (previous.bs !== bs) {
previous.bs = bs;
buf2.setColor(1);
buf2.setFontAlign(0, -1);
buf2.setFont("Vector",38);
buf2.drawString(bs,40,0);
flip2_bw(80, 200);
function addCurrentWaypoint() {
var wpnum = 0;
var ok = false;
// XXX: O(n^2) search for lowest unused WP number
while (!ok) {
ok = true;
for (var i = 0; i < waypoints.length && ok; i++) {
if (waypoints[i].name == ("WP"+wpnum)) {
wpnum++;
ok = false;
}
}
}
// waypoint name on right
if (previous.wp_name !== wp.name || previous.selected !== selected) {
previous.selected = selected;
buf2.setColor(1);
buf2.setFontAlign(1,-1); // right, bottom
buf2.setFont("Vector", 20);
buf2.drawString(wp.name, 80, 0);
if (selected)
flip2_bw(160, 200);
else
flip2_bb(160, 200);
}
waypoints.push({
name: "WP" + wpnum,
lat: savedfix.lat,
lon: savedfix.lon,
});
wp = waypoints[waypoints.length-1];
saveWaypoints();
}
var savedfix;
function saveWaypoints() {
require("Storage").writeJSON("waypoints.json", waypoints);
}
function onGPS(fix) {
savedfix = fix;
if (fix!==undefined){
satellites = fix.satellites;
}
if (candraw) {
if (fix!==undefined && fix.fix==1){
dist = distance(fix,wp);
if (isNaN(dist)) dist = 0;
wp_bearing = bearing(fix,wp);
if (isNaN(wp_bearing)) wp_bearing = 0;
drawN();
function deleteWaypoint(w) {
for (var i = 0; i < waypoints.length; i++) {
if (waypoints[i] == w) {
waypoints.splice(i, 1);
saveWaypoints();
wp = {name:"NONE"};
}
}
}
var intervalRef;
/*** Setup ***/
function stopdraw() {
candraw=false;
prev_course = -1;
if(intervalRef) {clearInterval(intervalRef);}
function onGPS(fix) {
savedfix = fix;
if (fix !== undefined && fix.fix == 1){
dist = distance(fix, wp);
if (isNaN(dist)) dist = 0;
wp_bearing = bearing(fix, wp);
if (isNaN(wp_bearing)) wp_bearing = 0;
drawN();
}
}
function startTimers() {
candraw=true;
intervalRefSec = setInterval(function() {
read_compass();
setInterval(function() {
Bangle.setLCDPower(1);
read_heading();
}, 500);
}
function drawAll(){
g.setColor(1,1,1);
drawN();
drawCompass(direction);
function addWaypointToMenu(menu, i) {
menu[waypoints[i].name] = function() {
wp = waypoints[i];
mainScreen();
};
}
function startdraw(){
g.clear();
Bangle.drawWidgets();
startTimers();
candraw=true;
drawAll();
}
function mainScreen() {
E.showMenu();
candraw = true;
drawAll(true);
function setButtons(){
setWatch(nextwp.bind(null,-1), BTN1, {repeat:true,edge:"falling"});
setWatch(doselect, BTN2, {repeat:true,edge:"falling"});
setWatch(nextwp.bind(null,1), BTN3, {repeat:true,edge:"falling"});
}
Bangle.on('lcdPower',function(on) {
if (on) {
clear_previous();
startdraw();
} else {
stopdraw();
}
});
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
wp=waypoints[0];
function nextwp(inc){
if (!selected) return;
wpindex+=inc;
if (wpindex>=waypoints.length) wpindex=0;
if (wpindex<0) wpindex = waypoints.length-1;
wp = waypoints[wpindex];
drawN();
}
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);
}
selected=!selected;
drawN();
Bangle.setUI("updown", function(v) {
if (v === undefined) {
candraw = false;
var menu = {
"": { "title": "-- Waypoints --" },
};
for (let i = 0; i < waypoints.length; i++) {
addWaypointToMenu(menu, i);
}
menu["+ Here"] = function() {
addCurrentWaypoint();
mainScreen();
};
menu["< Back"] = mainScreen;
E.showMenu(menu);
} else {
candraw = false;
E.showPrompt("Delete waypoint: " + wp.name + "?").then(function(confirmed) {
var name = wp.name;
if (confirmed) {
deleteWaypoint(wp);
E.showAlert("Waypoint deleted: " + name).then(mainScreen);
} else {
mainScreen();
}
});
}
});
}
Bangle.on('kill',()=>{
@ -272,12 +282,7 @@ Bangle.on('kill',()=>{
g.clear();
Bangle.setLCDBrightness(1);
Bangle.loadWidgets();
Bangle.drawWidgets();
// load widgets can turn off GPS
Bangle.setGPSPower(1);
Bangle.setCompassPower(1);
drawAll();
startTimers();
Bangle.on('GPS', onGPS);
setButtons();
mainScreen();

BIN
apps/wpmoto/arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
apps/wpmoto/editor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
apps/wpmoto/floppy-disk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("mEwwhC/AFcBiAWViMRDCkBiUhC68RC64AFGxsRC4UiAAY2HOAQAEC4MSn//AAXzGAwWGC4czC4f/mIwEFwIlEBoIXDBQnyGAkRiYWE/8yLAIXBGAhgEFw5WBC4R0BkYaBmRfFF44XCNI6OGGAQlBAAIXIX4yPJaBq/JC5oeHC/4X/C/4X/C/4X/C/4X/C88RiIXUDAIWVAH4AVA="))
require("heatshrink").decompress(atob("mEw4kA///tVK/feuekkEh1dSnnn5P2imlgdr221vvv0E5x9z8dqoEpMf4AqgMQCysRiIYUgMd6IXVqc1iIXXAAo2NiIXBivdAAfVGwxwCAAgXBicmswACsVRGAsRqoAEqIXC0QXDs0lGAkBisrBomBC4WqGAloMIkRqW72wNDlvTC4QwEwIvDLoOr3e7KwkzmsdmczxAABwaQFF4QwEC4SAGR4pfBGAO2kQABlQXIX4wlHL4LQNX5IXNDw4XygdZ96MFF58NvP8C6Mzu93uF9+oXQrQWBAAPU/5kPgoWDu+UoIQJ7vR6IGCm4XEz9MC5HMAAvHC4mc4gWHk93zPsC5F58oWHgQOE//8//M//+5mf9vAFxFwOQef5nu9wbB5nOiNQBoInCCYMCk8FjnM7qkB5lEp3//udRoKlBuueDwMyC4MojnJz2dmEA/1EovDn3d6nEiEHv3pzOc4oXB1nJ0Wo7JVBjnE/td6IxBgtQu/u4ci1vs6EAj+S3eynvBgEP92Z/+XzIXBiF59GOte27goB5OrlGDlWcMAP3SgJvBusBgF/61sycrlPFgvJsWZzMrziGB84XDvlBg+excp9N73P1gbQB5//+lEiMAi93vwaBz4XC1ep5OS2f9gPJlWZ93d+lBqNfzn//N5+lAg+cF4OZF4UA52LlWi3RfBSANEAAVBgEHz+W3VzlepVANczW73ep+L4CgNTnrfBAAPc8drUAOcR4MB/PYwefAwIXCjvRdgIABgvO7Ui1PMX4MA5n+96+BC4cRiIXDKAPOzPvIwIABhsc4oPEDAIGFhqhB4K2BACY+EAH4AG"))

18
apps/wpmoto/metadata.json Normal file
View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
apps/wpmoto/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

View File

@ -2,19 +2,4 @@
{
"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" }
]
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

View File

@ -2,6 +2,8 @@
<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.12.0/css/ol.css" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/ol-geocoder@latest/dist/ol-geocoder.min.css" rel="stylesheet">
</head>
<body>
@ -30,13 +32,10 @@
<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">
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_longitude" 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>
@ -44,21 +43,60 @@
</form>
<br>
<button id="Download" class="btn btn-error">Reload</button> <button id="Upload" class="btn btn-primary">Upload</button>
<br>
<div id="map" class="map" style="width:100%; height:400px"></div>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.12.0/build/ol.js"></script>
<script src="https://cdn.jsdelivr.net/npm/ol-geocoder"></script>
<script src="../../core/lib/interface.js"></script>
<script>
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([37.41, 8.82]),
zoom: 4
})
});
var geocoder = new Geocoder('nominatim', {
provider: 'osm',
lang: 'en-GB',
placeholder: 'Search...',
targetType: 'text-input',
});
map.addControl(geocoder);
geocoder.on('addresschosen', function(e) {
map.getView().animate({
center: e.coordinate,
zoom: Math.max(map.getView().getZoom(),16)
});
var lonlat = ol.proj.toLonLat(e.coordinate);
$longitude.value = lonlat[0];
$latitude.value = lonlat[1];
});
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 $longitude = document.getElementById('add_longitude')
var $list = document.getElementById('waypoints')
map.on('click', function(e) {
var lonlat = ol.proj.toLonLat(e.coordinate);
$longitude.value = lonlat[0];
$latitude.value = lonlat[1];
});
function compare(a, b){
var x = a.name.toLowerCase();
var y = b.name.toLowerCase();
@ -73,8 +111,8 @@
event.preventDefault()
var name = $name.value.trim()
if(!name) return;
var lat = parseFloat($latitude.value).toPrecision(5);
var lon = parseFloat($longtitude.value).toPrecision(5);
var lat = parseFloat($latitude.value).toPrecision(8);
var lon = parseFloat($longitude.value).toPrecision(8);
waypoints.push({
name, lat,lon,
@ -84,31 +122,21 @@
renderWaypoints()
$name.value = ''
$latitude.value = (0).toPrecision(5);
$longtitude.value = (0).toPrecision(5);
$latitude.value = (0).toPrecision(8);
$longitude.value = (0).toPrecision(8);
});
$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
if (waypoints[index].lat !== undefined && waypoints[index].lon !== undefined
&& !isNaN(waypoints[index].lat) && !isNaN(waypoints[index].lon)) {
$latitude.value = waypoints[index].lat
$longitude.value = waypoints[index].lon
map.getView().animate({
center: ol.proj.fromLonLat([waypoints[index].lon, waypoints[index].lat]),
zoom: Math.max(map.getView().getZoom(),16)
});
}
waypoints = waypoints.filter((p,i) => i!==index)
renderWaypoints()
}

BIN
apps/wpmoto/wpmoto.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB