GPS Touch: swipe left, right to change display
parent
ff5fda9dff
commit
c139651b4f
15
apps.json
15
apps.json
|
|
@ -4037,5 +4037,20 @@
|
|||
{"name":"vernierrespirate.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"vernierrespirate.json"}]
|
||||
},
|
||||
{
|
||||
"id": "gpstouch",
|
||||
"name": "GPS Touch",
|
||||
"version": "0.01",
|
||||
"description": "A touch based GPS watch, shows OS map reference",
|
||||
"icon": "gpstouch.png",
|
||||
"tags": "tools,app",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"geotools","url":"geotools.js"},
|
||||
{"name":"gpstouch.app.js","url":"gpstouch.app.js"},
|
||||
{"name":"gpstouch.img","url":"gpstouch.icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
# GPS Touch
|
||||
|
||||
## Screenshots
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
/**
|
||||
*
|
||||
* A module of Geo functions for use with gps fixes
|
||||
*
|
||||
* let geo = require("geotools");
|
||||
* let os = geo.gpsToOSGrid(fix);
|
||||
* let ref = geo.gpsToOSMapRef(fix);
|
||||
*
|
||||
*/
|
||||
|
||||
Number.prototype.toRad = function() { return this*Math.PI/180; };
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
/* Ordnance Survey Grid Reference functions (c) Chris Veness 2005-2014 */
|
||||
/* - www.movable-type.co.uk/scripts/gridref.js */
|
||||
/* - www.movable-type.co.uk/scripts/latlon-gridref.html */
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
function OsGridRef(easting, northing) {
|
||||
this.easting = 0|easting;
|
||||
this.northing = 0|northing;
|
||||
}
|
||||
OsGridRef.latLongToOsGrid = function(point) {
|
||||
var lat = point.lat.toRad();
|
||||
var lon = point.lon.toRad();
|
||||
|
||||
var a = 6377563.396, b = 6356256.909; // Airy 1830 major & minor semi-axes
|
||||
var F0 = 0.9996012717; // NatGrid scale factor on central meridian
|
||||
var lat0 = (49).toRad(), lon0 = (-2).toRad(); // NatGrid true origin is 49�N,2�W
|
||||
var N0 = -100000, E0 = 400000; // northing & easting of true origin, metres
|
||||
var e2 = 1 - (b*b)/(a*a); // eccentricity squared
|
||||
var n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n;
|
||||
|
||||
var cosLat = Math.cos(lat), sinLat = Math.sin(lat);
|
||||
var nu = a*F0/Math.sqrt(1-e2*sinLat*sinLat); // transverse radius of curvature
|
||||
var rho = a*F0*(1-e2)/Math.pow(1-e2*sinLat*sinLat, 1.5); // meridional radius of curvature
|
||||
var eta2 = nu/rho-1;
|
||||
|
||||
var Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (lat-lat0);
|
||||
var Mb = (3*n + 3*n*n + (21/8)*n3) * Math.sin(lat-lat0) * Math.cos(lat+lat0);
|
||||
var Mc = ((15/8)*n2 + (15/8)*n3) * Math.sin(2*(lat-lat0)) * Math.cos(2*(lat+lat0));
|
||||
var Md = (35/24)*n3 * Math.sin(3*(lat-lat0)) * Math.cos(3*(lat+lat0));
|
||||
var M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc
|
||||
|
||||
var cos3lat = cosLat*cosLat*cosLat;
|
||||
var cos5lat = cos3lat*cosLat*cosLat;
|
||||
var tan2lat = Math.tan(lat)*Math.tan(lat);
|
||||
var tan4lat = tan2lat*tan2lat;
|
||||
|
||||
var I = M + N0;
|
||||
var II = (nu/2)*sinLat*cosLat;
|
||||
var III = (nu/24)*sinLat*cos3lat*(5-tan2lat+9*eta2);
|
||||
var IIIA = (nu/720)*sinLat*cos5lat*(61-58*tan2lat+tan4lat);
|
||||
var IV = nu*cosLat;
|
||||
var V = (nu/6)*cos3lat*(nu/rho-tan2lat);
|
||||
var VI = (nu/120) * cos5lat * (5 - 18*tan2lat + tan4lat + 14*eta2 - 58*tan2lat*eta2);
|
||||
|
||||
var dLon = lon-lon0;
|
||||
var dLon2 = dLon*dLon, dLon3 = dLon2*dLon, dLon4 = dLon3*dLon, dLon5 = dLon4*dLon, dLon6 = dLon5*dLon;
|
||||
|
||||
var N = I + II*dLon2 + III*dLon4 + IIIA*dLon6;
|
||||
var E = E0 + IV*dLon + V*dLon3 + VI*dLon5;
|
||||
|
||||
return new OsGridRef(E, N);
|
||||
};
|
||||
|
||||
/*
|
||||
* converts northing, easting to standard OS grid reference.
|
||||
*
|
||||
* [digits=10] - precision (10 digits = metres)
|
||||
* to_map_ref(8, 651409, 313177); => 'TG 5140 1317'
|
||||
* to_map_ref(0, 651409, 313177); => '651409,313177'
|
||||
*
|
||||
*/
|
||||
function to_map_ref(digits, easting, northing) {
|
||||
if (![ 0,2,4,6,8,10,12,14,16 ].includes(Number(digits))) throw new RangeError(`invalid precision '${digits}'`); // eslint-disable-line comma-spacing
|
||||
|
||||
let e = easting;
|
||||
let n = northing;
|
||||
|
||||
// use digits = 0 to return numeric format (in metres) - note northing may be >= 1e7
|
||||
if (digits == 0) {
|
||||
const format = { useGrouping: false, minimumIntegerDigits: 6, maximumFractionDigits: 3 };
|
||||
const ePad = e.toLocaleString('en', format);
|
||||
const nPad = n.toLocaleString('en', format);
|
||||
return `${ePad},${nPad}`;
|
||||
}
|
||||
|
||||
// get the 100km-grid indices
|
||||
const e100km = Math.floor(e / 100000), n100km = Math.floor(n / 100000);
|
||||
|
||||
// translate those into numeric equivalents of the grid letters
|
||||
let l1 = (19 - n100km) - (19 - n100km) % 5 + Math.floor((e100km + 10) / 5);
|
||||
let l2 = (19 - n100km) * 5 % 25 + e100km % 5;
|
||||
|
||||
// compensate for skipped 'I' and build grid letter-pairs
|
||||
if (l1 > 7) l1++;
|
||||
if (l2 > 7) l2++;
|
||||
const letterPair = String.fromCharCode(l1 + 'A'.charCodeAt(0), l2 + 'A'.charCodeAt(0));
|
||||
|
||||
// strip 100km-grid indices from easting & northing, and reduce precision
|
||||
e = Math.floor((e % 100000) / Math.pow(10, 5 - digits / 2));
|
||||
n = Math.floor((n % 100000) / Math.pow(10, 5 - digits / 2));
|
||||
|
||||
// pad eastings & northings with leading zeros
|
||||
e = e.toString().padStart(digits/2, '0');
|
||||
n = n.toString().padStart(digits/2, '0');
|
||||
|
||||
return `${letterPair} ${e} ${n}`;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Module exports section, example code below
|
||||
*
|
||||
* let geo = require("geotools");
|
||||
* let os = geo.gpsToOSGrid(fix);
|
||||
* let ref = geo.gpsToOSMapRef(fix);
|
||||
*/
|
||||
|
||||
// get easting and northings
|
||||
exports.gpsToOSGrid = function(gps_fix) {
|
||||
return OsGridRef.latLongToOsGrid(gps_fix);
|
||||
}
|
||||
|
||||
// string with an OS Map grid reference
|
||||
exports.gpsToOSMapRef = function(gps_fix) {
|
||||
let os = OsGridRef.latLongToOsGrid(last_fix);
|
||||
return to_map_ref(6, os.easting, os.northing);
|
||||
}
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
const h = g.getHeight();
|
||||
const w = g.getWidth();
|
||||
let last_fix;
|
||||
|
||||
function resetLastFix() {
|
||||
last_fix = {
|
||||
fix: 0,
|
||||
alt: 0,
|
||||
lat: 0,
|
||||
lon: 0,
|
||||
speed: 0,
|
||||
time: 0,
|
||||
course: 0,
|
||||
satellites: 0
|
||||
};
|
||||
}
|
||||
|
||||
function processFix(fix) {
|
||||
last_fix.time = fix.time;
|
||||
|
||||
if (fix.fix) {
|
||||
if (!last_fix.fix) {
|
||||
// we dont need to suppress this in quiet mode as it is user initiated
|
||||
Bangle.buzz(); // buzz on first position
|
||||
}
|
||||
last_fix = fix;
|
||||
}
|
||||
}
|
||||
|
||||
function draw() {
|
||||
var d = new Date();
|
||||
var da = d.toString().split(" ");
|
||||
var time = da[4].substr(0,5);
|
||||
var hh = da[4].substr(0,2);
|
||||
var mm = da[4].substr(3,2);
|
||||
|
||||
g.reset();
|
||||
drawTop(d,hh,mm);
|
||||
drawInfo();
|
||||
}
|
||||
|
||||
function drawTop(d,hh,mm) {
|
||||
g.setFont("Vector", w/3);
|
||||
g.setFontAlign(0, 0);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillRect(0, 24, w, ((h-24)/2) + 24);
|
||||
g.setColor(g.theme.fg);
|
||||
|
||||
g.setFontAlign(1,0); // right aligned
|
||||
g.drawString(hh, (w/2) - 6, ((h-24)/4) + 24);
|
||||
g.setFontAlign(-1,0); // left aligned
|
||||
g.drawString(mm, (w/2) + 6, ((h-24)/4) + 24);
|
||||
|
||||
// for the colon
|
||||
g.setFontAlign(0,0); // centre aligned
|
||||
if (d.getSeconds()&1) g.drawString(":", w/2, ((h-24)/4) + 24);
|
||||
}
|
||||
|
||||
function drawInfo() {
|
||||
if (infoData[infoMode] && infoData[infoMode].calc) {
|
||||
g.setFont("Vector", w/7);
|
||||
g.setFontAlign(0, 0);
|
||||
|
||||
if (infoData[infoMode].get_color)
|
||||
g.setColor(infoData[infoMode].get_color());
|
||||
else
|
||||
g.setColor(g.theme.bgH);
|
||||
g.fillRect(0, ((h-24)/2) + 24 + 1, w, h);
|
||||
|
||||
if (infoData[infoMode].is_control)
|
||||
g.setColor("#fff");
|
||||
else
|
||||
g.setColor(g.theme.fg);
|
||||
|
||||
g.drawString((infoData[infoMode].calc()), w/2, (3*(h-24)/4) + 24);
|
||||
}
|
||||
}
|
||||
|
||||
const infoData = {
|
||||
ID_LAT: {
|
||||
calc: () => 'Lat: ' + last_fix.lat.toFixed(4),
|
||||
},
|
||||
ID_LON: {
|
||||
calc: () => 'Lon: ' + last_fix.lon.toFixed(4),
|
||||
},
|
||||
ID_SPEED: {
|
||||
calc: () => 'Speed: ' + last_fix.speed.toFixed(1),
|
||||
},
|
||||
ID_ALT: {
|
||||
calc: () => 'Alt: ' + last_fix.alt.toFixed(0),
|
||||
},
|
||||
ID_COURSE: {
|
||||
calc: () => 'Course: '+ last_fix.course.toFixed(0),
|
||||
},
|
||||
ID_SATS: {
|
||||
calc: () => 'Satelites: ' + last_fix.satellites,
|
||||
},
|
||||
ID_TIME: {
|
||||
calc: () => formatTime(last_fix.time),
|
||||
},
|
||||
OS_REF: {
|
||||
calc: () => 'NZ 208 987',
|
||||
},
|
||||
GPS_POWER: {
|
||||
calc: () => (Bangle.isGPSOn()) ? 'GPS On' : 'GPS Off',
|
||||
action: () => toggleGPS(),
|
||||
get_color: () => Bangle.isGPSOn() ? '#f00' : '#00f',
|
||||
is_control: true,
|
||||
},
|
||||
GPS_LOGGER: {
|
||||
calc: () => 'Logger ' + loggerStatus(),
|
||||
action: () => toggleLogger(),
|
||||
get_color: () => loggerStatus() == "ON" ? '#f00' : '#00f',
|
||||
is_control: true,
|
||||
},
|
||||
};
|
||||
|
||||
function toggleGPS() {
|
||||
Bangle.setGPSPower(Bangle.isGPSOn() ? 0 : 1, 'gpstouch');
|
||||
// add or remove listenner
|
||||
if (Bangle.isGPSOn())
|
||||
Bangle.on('GPS', processFix);
|
||||
else
|
||||
Bangle.removeListener("GPS", processFix);
|
||||
resetLastFix();
|
||||
}
|
||||
|
||||
function loggerStatus() {
|
||||
var settings = require("Storage").readJSON("gpsrec.json",1)||{};
|
||||
if (settings == {}) return "Install";
|
||||
return settings.recording ? "ON" : "OFF";
|
||||
}
|
||||
|
||||
function toggleLogger() {
|
||||
var settings = require("Storage").readJSON("gpsrec.json",1)||{};
|
||||
if (settings == {}) return;
|
||||
|
||||
settings.recording = !settings.recording;
|
||||
require("Storage").write("gpsrec.json", settings);
|
||||
|
||||
if (WIDGETS["gpsrec"])
|
||||
WIDGETS["gpsrec"].reload();
|
||||
|
||||
// not sure if safe to register a listenner again
|
||||
if (Bangle.isGPSOn())
|
||||
Bangle.on('GPS', processFix);
|
||||
}
|
||||
|
||||
function formatTime(now) {
|
||||
try {
|
||||
var fd = now.toUTCString().split(" ");
|
||||
return fd[4];
|
||||
} catch (e) {
|
||||
return "00:00:00";
|
||||
}
|
||||
}
|
||||
|
||||
const infoList = Object.keys(infoData).sort();
|
||||
let infoMode = infoList[0];
|
||||
|
||||
function nextInfo() {
|
||||
let idx = infoList.indexOf(infoMode);
|
||||
if (idx > -1) {
|
||||
if (idx === infoList.length - 1) infoMode = infoList[0];
|
||||
else infoMode = infoList[idx + 1];
|
||||
}
|
||||
}
|
||||
|
||||
function prevInfo() {
|
||||
let idx = infoList.indexOf(infoMode);
|
||||
if (idx > -1) {
|
||||
if (idx === 0) infoMode = infoList[infoList.length - 1];
|
||||
else infoMode = infoList[idx - 1];
|
||||
}
|
||||
}
|
||||
|
||||
Bangle.on('swipe', dir => {
|
||||
if (dir == 1) prevInfo() else nextInfo();
|
||||
draw();
|
||||
});
|
||||
|
||||
let prevTouch = 0;
|
||||
|
||||
Bangle.on('touch', function(button, xy) {
|
||||
let dur = 1000*(getTime() - prevTouch);
|
||||
prevTouch = getTime();
|
||||
|
||||
if (dur <= 1000 && xy.y < h/2 && infoData[infoMode].is_control) {
|
||||
Bangle.buzz();
|
||||
if (infoData[infoMode] && infoData[infoMode].action) {
|
||||
infoData[infoMode].action();
|
||||
draw();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Stop updates when LCD is off, restart when on
|
||||
Bangle.on('lcdPower', on => {
|
||||
if (secondInterval)
|
||||
clearInterval(secondInterval);
|
||||
secondInterval = undefined;
|
||||
if (on)
|
||||
secondInterval = setInterval(draw, 1000);
|
||||
draw();
|
||||
});
|
||||
|
||||
|
||||
resetLastFix();
|
||||
|
||||
// add listenner if already powered on, plus tag app
|
||||
if (Bangle.isGPSOn()) {
|
||||
Bangle.setGPSPower(1, 'gpstouch');
|
||||
Bangle.on('GPS', processFix);
|
||||
}
|
||||
|
||||
g.clear();
|
||||
var secondInterval = setInterval(draw, 1000);
|
||||
draw();
|
||||
// Show launcher when button pressed
|
||||
Bangle.setUI("clock");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4UA///j+EAYO/uYDB//wCYcPBA4AFh/ABZMDBbkX6gLIgtX6tQBY9VBYNVBY0BBYdABYsFqoACEgQLDitVtWpqtUBYtVq2q1WVGAQLErQLB0oLFHQNqBYIkBHgMDIwYKBAAJIDIweqz/2BYJtDBYI6Bv/9HgILHYwILGh4gBBYWfbooLF6AjPBYW//wLGL4Wv/RfGNZaDIBYibEBYizIBYjLDBYzXBd4TXCBZ60BBYRqEBZpUBBYRSFJAQLCA4b7BHgQLFgYLGIwYLEgoLBHQYLEgILBHQYLEgALBAoYLFi/UBZMHBZUD6ALKApQAFBbHwBZMP/4ABBwgIDA="))
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Loading…
Reference in New Issue