Implement Waypointer Moto

master
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 Waypointer Moto is a GPS navigation aid intended to be attached to
would be foolish in the extreme to rely on this as your only the handlebars of a motorcycle.
navigation aid! 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 ![](watch-on-bike.jpeg)
should be done each time the app is going to be used.
The main part of the display is a compass arrow that points in the (Please note that it would be foolish in the extreme to rely on this
direction you need to walk in. Once you have selected a waypoint a as your only navigation aid! Make sure you read this entire document
bearing from your current position (received from a GPS fix) is before using the app for navigation so that you know the drawbacks
calculated and the compass is set to point in that direction. If the and shortcomings.)
arrow is pointing to the left, turning left should straighten the arrow
up so that it is pointing straight ahead.
## App usage
![](waypointer_screenshot.jpg) ### Main screen
The large digits are the bearing from the current position. On the ![](screenshot.png)
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.
The screenshot above shows that Stone Henge is 259.9 miles from the The main screen shows the direction arrow, the distance to the waypoint,
current location. To travel towards Stone Henge I need to turn and the name of the selected waypoint.
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.
At the top of the screen you can see two widgets. These are the [GPS It also shows the status of the GPS fix in the colour of the arrow:
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.
* 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 ![](screenshot-menu.png)
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.
![](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 ### Add a waypoint
location associated with it. To mark the location, press BTN2.
![](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 ### Delete a waypoint
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 ![](screenshot-delete.png)
When the app is loaded from the app loader, a file named Select a waypoint using the menu. Once the waypoint is selected and you're
`waypoints.json` is loaded along with the javascript etc. The file back on the main screen, press either the top or bottom button (`BTN1` or
has the following contents: `BTN3`). Confirm that you want to delete the waypoint with the middle
button (`BTN2`).
## Waypoint editor
``` With the Bangle.js app loader connected to the watch, find the
[ Waypointer Moto app and click on the floppy disk icon:
{
"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 ![](floppy-disk.png)
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 This will load up the waypoint editor:
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.
![](editor.png)
## Waypoint Editor ### Add a waypoint
Clicking on the download icon of gpsnav in the app loader invokes the Use the map to find your destination. Clicking on the map will
waypoint editor. The editor downloads and displays the current populate the latitude/longitude input boxes with the coordinates
`waypoints.json` file. Clicking the `Edit` button beside an entry of the point you clicked on. Type in a name for the waypoint and
causes the entry to be deleted from the list and displayed in the click "Add Waypoint". Click "Upload" to send the updated list of
edit boxes. It can be restored - by clicking the `Add waypoint` waypoints to the watch.
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.
### 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 ### Delete a waypoint
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`.
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 There is a 3d-printable "artificial wrist" which will fit over a 7/8"
the arrow is fairly easy to do and once the bearing has been handlebar and allow the watch strap to tighten up.
established it does not matter if there is not another GPS fix for a Alternatively, in a pinch you can strap the watch around a glove or a sponge
while as the compass will continue to point in the general direction. or anything else that will pad out the space so that the watch is a tight
Second the GPS will only supply a course to the waypoint (a bearing) fit.
once you are travelling above 8m/s or 28kph. This is not a practical
walking speed. 5kmph is considered a marching pace.
One disadvantage is that the compass is not very accurate. I have The 3d-printed part should be a snug fit on the handlebar so that it does
observed it being 20-30 degrees off when compared to a hiking not flop around. If it is too loose, line it with a layer or 2 of tape.
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.
[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 ## Possible Future Enhancements
- Buzz when the GPS establishes its first fix. - "routes" with multiple waypoints; automatically step from one
waypoint to the next when you get near to it
- Add a small LED to show the status of the GPS during the phase of - some way to manually input coordinates directly on the watch
establishing a first fix. - make the text & arrow more legible in direct sunlight
- integrate a charging connector into the handlebar mount
- Add an option to calibrate the Compass without having to use the - upstream the map integration to the other waypoint apps
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
## Acknowledgements ## 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 [jeffmer's](https://github.com/jeffmer/JeffsBangleAppsDev) GPS
Navigation and Compass Navigation Applications. Navigation and Compass Navigation apps.

View File

@ -1,65 +1,100 @@
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 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 = { var previous = {
bs: '',
dst: '', dst: '',
wp_name: '', wp_name: '',
course: 0, course: 180,
selected: false, selected: false,
}; };
// clear the attributes that control the display refresh /*** Drawing ***/
function clear_previous() {
previous.bs = '-'; var pal_by = new Uint16Array([0x0000,0xFFC0],0,1); // black, yellow
previous.dst = '-'; var pal_bw = new Uint16Array([0x0000,0xffff],0,1); // black, white
previous.wp_name = '-'; var pal_bb = new Uint16Array([0x0000,0x07ff],0,1); // black, blue
previous.course = -999; 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) { function drawCompass(course) {
if (!candraw) return; if (!candraw) return;
if (Math.abs(previous.course - course) < 9) return; // reduce number of draws due to compass jitter
previous.course = course; previous.course = course;
buf1.setColor(1); buf.setColor(1);
buf1.fillCircle(80,80,79,79); buf.fillCircle(80,80, 79);
buf1.setColor(0); buf.setColor(0);
buf1.fillCircle(80,80,69,69); buf.fillCircle(80,80, 69);
buf1.setColor(1); buf.setColor(1);
buf1.drawImage(arrow_img, 80, 80, {scale:3, rotate:radians(course)} ); buf.drawImage(arrow_img, 80, 80, {rotate:radians(course)} );
flip1(40, 30); 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; var heading = 0;
function newHeading(m,h){ function newHeading(m,h){
@ -93,10 +128,18 @@ function tiltfixread(O,S){
return psi; return psi;
} }
// Note actual mag is 360-m, error in firmware function read_heading() {
function read_compass() { if (savedfix !== undefined && !isNaN(savedfix.course)) {
Bangle.setCompassPower(0);
heading = savedfix.course;
pal_compass = pal_bw;
} else {
var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale); var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
Bangle.setCompassPower(1);
heading = newHeading(d,heading); heading = newHeading(d,heading);
pal_compass = pal_by;
}
direction = wp_bearing - heading; direction = wp_bearing - heading;
if (direction < 0) direction += 360; if (direction < 0) direction += 360;
if (direction > 360) direction -= 360; if (direction > 360) direction -= 360;
@ -104,12 +147,7 @@ function read_compass() {
} }
/***** END Compass ***********/ /*** Maths ***/
var speed = 0;
var satellites = 0;
var wp;
var dist=0;
function radians(a) { function radians(a) {
return a*Math.PI/180; return a*Math.PI/180;
@ -125,8 +163,7 @@ function bearing(a,b){
var alat = radians(a.lat); var alat = radians(a.lat);
var blat = radians(b.lat); var blat = radians(b.lat);
var y = Math.sin(delta) * Math.cos(blat); var y = Math.sin(delta) * Math.cos(blat);
var x = Math.cos(alat)*Math.sin(blat) - var x = Math.cos(alat)*Math.sin(blat) - Math.sin(alat)*Math.cos(blat)*Math.cos(delta);
Math.sin(alat)*Math.cos(blat)*Math.cos(delta);
return Math.round(degrees(Math.atan2(y, x))); return Math.round(degrees(Math.atan2(y, x)));
} }
@ -136,59 +173,50 @@ function distance(a,b){
return Math.round(Math.sqrt(x*x + y*y) * 6371000); return Math.round(Math.sqrt(x*x + y*y) * 6371000);
} }
/*** Waypoints ***/
function drawN(){ function addCurrentWaypoint() {
buf2.setFont("Vector",24); var wpnum = 0;
var bs = wp_bearing.toString(); var ok = false;
bs = wp_bearing<10?"00"+bs : wp_bearing<100 ?"0"+bs : bs; // XXX: O(n^2) search for lowest unused WP number
var dst = loc.distance(dist); while (!ok) {
ok = true;
// -1=left (default), 0=center, 1=right for (var i = 0; i < waypoints.length && ok; i++) {
if (waypoints[i].name == ("WP"+wpnum)) {
// show distance on the left wpnum++;
if (previous.dst !== dst) { ok = false;
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);
}
// 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);
} }
} }
var savedfix; waypoints.push({
name: "WP" + wpnum,
lat: savedfix.lat,
lon: savedfix.lon,
});
wp = waypoints[waypoints.length-1];
saveWaypoints();
}
function saveWaypoints() {
require("Storage").writeJSON("waypoints.json", waypoints);
}
function deleteWaypoint(w) {
for (var i = 0; i < waypoints.length; i++) {
if (waypoints[i] == w) {
waypoints.splice(i, 1);
saveWaypoints();
wp = {name:"NONE"};
}
}
}
/*** Setup ***/
function onGPS(fix) { function onGPS(fix) {
savedfix = fix; savedfix = fix;
if (fix!==undefined){
satellites = fix.satellites;
}
if (candraw) {
if (fix !== undefined && fix.fix == 1){ if (fix !== undefined && fix.fix == 1){
dist = distance(fix, wp); dist = distance(fix, wp);
if (isNaN(dist)) dist = 0; if (isNaN(dist)) dist = 0;
@ -197,72 +225,54 @@ function onGPS(fix) {
drawN(); drawN();
} }
} }
}
var intervalRef;
function stopdraw() {
candraw=false;
prev_course = -1;
if(intervalRef) {clearInterval(intervalRef);}
}
function startTimers() { function startTimers() {
candraw=true; setInterval(function() {
intervalRefSec = setInterval(function() { Bangle.setLCDPower(1);
read_compass(); read_heading();
}, 500); }, 500);
} }
function drawAll(){ function addWaypointToMenu(menu, i) {
g.setColor(1,1,1); menu[waypoints[i].name] = function() {
drawN(); wp = waypoints[i];
drawCompass(direction); mainScreen();
};
} }
function startdraw(){ function mainScreen() {
g.clear(); E.showMenu();
Bangle.drawWidgets();
startTimers();
candraw = true; candraw = true;
drawAll(); drawAll(true);
}
function setButtons(){ Bangle.setUI("updown", function(v) {
setWatch(nextwp.bind(null,-1), BTN1, {repeat:true,edge:"falling"}); if (v === undefined) {
setWatch(doselect, BTN2, {repeat:true,edge:"falling"}); candraw = false;
setWatch(nextwp.bind(null,1), BTN3, {repeat:true,edge:"falling"}); var menu = {
"": { "title": "-- Waypoints --" },
};
for (let i = 0; i < waypoints.length; i++) {
addWaypointToMenu(menu, i);
} }
menu["+ Here"] = function() {
Bangle.on('lcdPower',function(on) { addCurrentWaypoint();
if (on) { mainScreen();
clear_previous(); };
startdraw(); menu["< Back"] = mainScreen;
E.showMenu(menu);
} else { } else {
stopdraw(); 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();
} }
}); });
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.on('kill',()=>{ Bangle.on('kill',()=>{
@ -272,12 +282,7 @@ Bangle.on('kill',()=>{
g.clear(); g.clear();
Bangle.setLCDBrightness(1); Bangle.setLCDBrightness(1);
Bangle.loadWidgets();
Bangle.drawWidgets();
// load widgets can turn off GPS
Bangle.setGPSPower(1); Bangle.setGPSPower(1);
Bangle.setCompassPower(1);
drawAll();
startTimers(); startTimers();
Bangle.on('GPS', onGPS); 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":"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> <head>
<link rel="stylesheet" href="../../css/spectre.min.css"> <link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="../../css/spectre-icons.min.css"> <link rel="stylesheet" href="../../css/spectre-icons.min.css">
<link rel="stylesheet" href="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> </head>
<body> <body>
@ -30,13 +32,10 @@
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_latitude" placeholder="Lat"> <input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_latitude" placeholder="Lat">
</div> </div>
<div class="column col-3 col-xs-8"> <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> </div>
<div class="columns"> <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"> <div class="column col-3 col-xs-8">
<button id="add_waypoint_button" class="btn btn-primary btn-sm">Add Waypoint</button> <button id="add_waypoint_button" class="btn btn-primary btn-sm">Add Waypoint</button>
</div> </div>
@ -44,21 +43,60 @@
</form> </form>
<br> <br>
<button id="Download" class="btn btn-error">Reload</button> <button id="Upload" class="btn btn-primary">Upload</button> <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 src="../../core/lib/interface.js"></script>
<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 waypoints = []
var $name = document.getElementById('add_waypoint_name') var $name = document.getElementById('add_waypoint_name')
var $form = document.getElementById('add_waypoint_form') var $form = document.getElementById('add_waypoint_form')
var $button = document.getElementById('add_waypoint_button') var $button = document.getElementById('add_waypoint_button')
var $name_button = document.getElementById('add_name_button')
var $latitude = document.getElementById('add_latitude') var $latitude = document.getElementById('add_latitude')
var $longtitude = document.getElementById('add_longtitude') var $longitude = document.getElementById('add_longitude')
var $list = document.getElementById('waypoints') 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){ function compare(a, b){
var x = a.name.toLowerCase(); var x = a.name.toLowerCase();
var y = b.name.toLowerCase(); var y = b.name.toLowerCase();
@ -73,8 +111,8 @@
event.preventDefault() event.preventDefault()
var name = $name.value.trim() var name = $name.value.trim()
if(!name) return; if(!name) return;
var lat = parseFloat($latitude.value).toPrecision(5); var lat = parseFloat($latitude.value).toPrecision(8);
var lon = parseFloat($longtitude.value).toPrecision(5); var lon = parseFloat($longitude.value).toPrecision(8);
waypoints.push({ waypoints.push({
name, lat,lon, name, lat,lon,
@ -84,31 +122,21 @@
renderWaypoints() renderWaypoints()
$name.value = '' $name.value = ''
$latitude.value = (0).toPrecision(5); $latitude.value = (0).toPrecision(8);
$longtitude.value = (0).toPrecision(5); $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){ function removeWaypoint(index){
$name.value = waypoints[index].name $name.value = waypoints[index].name
if (waypoints[index].lat !== undefined && waypoints[index].lon !== undefined
&& !isNaN(waypoints[index].lat) && !isNaN(waypoints[index].lon)) {
$latitude.value = waypoints[index].lat $latitude.value = waypoints[index].lat
$longtitude.value = waypoints[index].lon $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) waypoints = waypoints.filter((p,i) => i!==index)
renderWaypoints() renderWaypoints()
} }

BIN
apps/wpmoto/wpmoto.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB