diff --git a/README.md b/README.md index 240163f6c..307323846 100644 --- a/README.md +++ b/README.md @@ -394,6 +394,18 @@ It should also add `app.json` to `data`, to make sure it is cleaned up when the }, ``` +## Modules + +You can include any of [Espruino's modules](https://www.espruino.com/Modules) as +normal with `require("modulename")`. If you want to develop your own module for your +app(s) then you can do that too. Just add the module into the `modules` folder +then you can use it from your app as normal. + +You won't be able to develop apps using your own modules with the IDE, +so instead we'd recommend you write your module to a Storage File called +`modulename` on Bangle.js. You can then develop your app as normal on Bangle.js +from the IDE. + ## Coding hints - use `g.setFont(.., size)` to multiply the font size, eg ("6x8",3) : "18x24" diff --git a/apps.json b/apps.json index c376813f2..a7bb44acd 100644 --- a/apps.json +++ b/apps.json @@ -65,7 +65,7 @@ { "id": "locale", "name": "Languages", "icon": "locale.png", - "version":"0.07", + "version":"0.08", "description": "Translations for different countries", "tags": "tool,system,locale,translate", "type": "locale", @@ -139,9 +139,10 @@ { "id": "gbridge", "name": "Gadgetbridge", "icon": "app.png", - "version":"0.17", + "version":"0.18", "description": "The default notification handler for Gadgetbridge notifications from Android", "tags": "tool,system,android,widget", + "readme": "README.md", "type":"widget", "dependencies": { "notify":"type" }, "storage": [ @@ -362,14 +363,15 @@ { "id": "gpsrec", "name": "GPS Recorder", "icon": "app.png", - "version":"0.13", + "version":"0.16", "interface": "interface.html", "description": "Application that allows you to record a GPS track. Can run in background", "tags": "tool,outdoors,gps,widget", "storage": [ {"name":"gpsrec.app.js","url":"app.js"}, {"name":"gpsrec.img","url":"app-icon.js","evaluate":true}, - {"name":"gpsrec.wid.js","url":"widget.js"} + {"name":"gpsrec.wid.js","url":"widget.js"}, + {"name":"gpsrec.settings.js","url":"settings.js"} ], "data": [ {"name":"gpsrec.json"}, @@ -379,13 +381,13 @@ { "id": "gpsnav", "name": "GPS Navigation", "icon": "icon.png", - "version":"0.04", + "version":"0.05", "description": "Displays GPS Course and Speed, + Directions to waypoint and waypoint recording, now with waypoint editor", "tags": "tool,outdoors,gps", "readme": "README.md", "interface":"waypoints.html", "storage": [ - {"name":"gpsnav.app.js","url":"app.js"}, + {"name":"gpsnav.app.js","url":"app.min.js"}, {"name":"waypoints.json","url":"waypoints.json","evaluate":false}, {"name":"gpsnav.img","url":"app-icon.js","evaluate":true} ] @@ -941,12 +943,12 @@ ] }, { "id": "assistedgps", - "name": "Assisted GPS Update", + "name": "Assisted GPS Update (AGPS)", "icon": "app.png", "version":"0.01", "description": "Downloads assisted GPS data to Bangle.js for faster GPS startup and more accurate fixes", "custom": "custom.html", - "tags": "tool,outdoors", + "tags": "tool,outdoors,agps", "type": "RAM", "storage": [ ] }, @@ -1325,11 +1327,12 @@ "name": "OpenStreetMap", "shortName":"OpenStMap", "icon": "app.png", - "version":"0.03", + "version":"0.05", "description": "[BETA] Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are", "tags": "outdoors,gps", "custom": "custom.html", "storage": [ + {"name":"openstmap","url":"openstmap.js"}, {"name":"openstmap.app.js","url":"app.js"}, {"name":"openstmap.img","url":"app-icon.js","evaluate":true} ] @@ -2290,7 +2293,7 @@ "name": "File manager", "shortName":"FileManager", "icon": "icons8-filing-cabinet-48.png", - "version":"0.01", + "version":"0.02", "description": "Simple file manager, allows user to examine watch storage and display, load or delete individual files", "tags": "tools", "readme": "README.md", @@ -2473,6 +2476,6 @@ "storage": [ {"name":"gmeter.app.js","url":"app.js"}, {"name":"gmeter.img","url":"app-icon.js","evaluate":true} - ] + ] } ] diff --git a/apps/assistedgps/custom.html b/apps/assistedgps/custom.html index e86c660b9..b615f2217 100644 --- a/apps/assistedgps/custom.html +++ b/apps/assistedgps/custom.html @@ -84,15 +84,13 @@ var chunkSize = 128; var js = "\x10Bangle.setGPSPower(1);\n"; // turn GPS on //js += `\x10Bangle.on('GPS-raw',function (d) { if (d.startsWith("\\xB5\\x62\\x05\\x01")) Terminal.println("GPS ACK"); else if (d.startsWith("\\xB5\\x62\\x05\\x00")) Terminal.println("GPS NACK"); })\n`; - js += "\x10E.showMessage('Uploading...','AGPS');function p(n) {g.fillRect(0,g.getHeight()-80,g.getWidth()*n,g.getHeight()-60);}"; //js += "\x10var t=getTime()+1;while(t>getTime());\n"; // wait 1 sec js += `\x10Serial1.write(atob("${btoa(String.fromCharCode.apply(null,UBX_MGA_INI_TIME_UTC()))}"))\n`; // set GPS time - + for (var i=0;is.slice(0,-1)); + var sffilter = f=>!sf.includes(f.slice(0,-1)) || f.endsWith("\1"); + // get files - put '.' last + var fl = STOR.list(/^[^\.]/).filter(sffilter); fl.sort(); - fl = fl.concat(STOR.list(/^\./)); - fl = fl.filter(f => (f.charCodeAt(f.length-1)>31 || f.charCodeAt(f.length-1)<2)); + fl = fl.concat(STOR.list(/^\./).filter(sffilter).sort()); return fl; } diff --git a/apps/gbridge/ChangeLog b/apps/gbridge/ChangeLog index 9a3090f5d..bec2d305a 100644 --- a/apps/gbridge/ChangeLog +++ b/apps/gbridge/ChangeLog @@ -17,3 +17,4 @@ 0.16: Handle dismissing notifications on the phone Nicer display of alarm clock notifications 0.17: Modified music notification for updated 'notify' library +0.18: Added reporting of step count and HRM (new Gadgetbridges can now log this) diff --git a/apps/gbridge/README.md b/apps/gbridge/README.md new file mode 100644 index 000000000..867b736ab --- /dev/null +++ b/apps/gbridge/README.md @@ -0,0 +1,54 @@ +Gadgetbridge +============= + +This widget allows your Bangle.js to communicate with the Gadgetbridge app on an Android phone. + +Download the [latest Gadgetbridge for Android here](https://f-droid.org/packages/nodomain.freeyourgadget.gadgetbridge/). + +This app supports: + +* Displaying Notifications +* Song display and control +* Call answering +* Find My Phone / Find My Bangle +* Activity reporting + +You can also add [the weather widget](https://banglejs.com/apps/#weather) + + +Notifications +------------- + +By default a notification at the top of the screen is displayed. If you'd like a fullscreen notification +(which will involve leaving the current app) then install [Fullscreen Notifications](https://banglejs.com/apps/#notifyfs) + + +Song display and control +------------------------ + +When the Song Display notification is showing on the screen and a song is playing, you +can swipe left or right on the screen to go to the next or previous song. + + +Find My Phone +------------- + +Go to `Settings`, `App/Widget Settings`, `Gadgetbridge`, `Find Phone`, `On` + +If in range and connected your phone should start ringing. + + +Find My Bangle +------------- + +Onyour phone `Settings`, `App/Widget Settings`, `Gadgetbridge`, `Find Phone`, `On` + +If in range and connected your phone should start ringing. + + +Activity reporting +------------------ + +You'll need a Gadgetbridge release *after* version 0.50.0 for Actvity Reporting to be enabled. + +By default heart rate isn't reported, but it can be enabled from `Settings`, `App/Widget Settings`, `Gadgetbridge`, `Record HRM` diff --git a/apps/gbridge/settings.js b/apps/gbridge/settings.js index d1ecb594b..42fa84a3e 100644 --- a/apps/gbridge/settings.js +++ b/apps/gbridge/settings.js @@ -12,7 +12,7 @@ function updateSetting(setting, value) { let settings = require('Storage').readJSON("gbridge.json", true) || {}; settings[setting] = value - require('Storage').write('gbridge.json', settings); + require('Storage').writeJSON('gbridge.json', settings); } function setIcon(visible) { updateSetting('showIcon', visible); @@ -30,6 +30,11 @@ onchange: setIcon }, "Find Phone" : function() { E.showMenu(findPhone); }, + "Record HRM" : { + value: settings().hrm, + format: v => v?"Yes":"No", + onchange: v => updateSetting('hrm', v) + }, "< Back" : back, }; diff --git a/apps/gbridge/widget.js b/apps/gbridge/widget.js index b4c7e1f7b..d616e8816 100644 --- a/apps/gbridge/widget.js +++ b/apps/gbridge/widget.js @@ -1,4 +1,5 @@ (() => { + // Music handling const state = { music: "stop", @@ -10,13 +11,17 @@ scrollPos: 0 }; - + // activity reporting + var currentSteps = 0, lastSentSteps=0; + var activityInterval; + var hrmTimeout; + function settings() { let settings = require('Storage').readJSON("gbridge.json", true) || {}; if (!("showIcon" in settings)) { settings.showIcon = true; } - return settings + return settings; } function gbSend(message) { @@ -143,6 +148,45 @@ setTimeout(_=>Bangle.beep(), 1000); },2000); } + + function handleActivityEvent(event) { + var s = settings(); + // handle setting activity interval + if (s.activityInterval===undefined || + s.activityInterval<30) + s.activityInterval = 3*60; // 3 minutes default + if (event.int) { + if (event.int<30) event.int = 30; // min 30 secs + s.activityInterval = event.int; + require('Storage').writeJSON("gbridge.json", s); + } + // set up interval/HRM to handle activity data + var interval = s.activityInterval; + var realtime = event.hrm || event.stp; + if (activityInterval) + clearInterval(activityInterval); + activityInterval = undefined; + if (s.hrm) Bangle.setHRMPower(1); + if (s.hrm) { + if (realtime) { + // if realtime reporting, leave HRM on and use that to trigger events + hrmTimeout = undefined; + } else { + // else trigger it manually every so often + hrmTimeout = 5; + activityInterval = setInterval(function() { + hrmTimeout = 5; + Bangle.setHRMPower(1); + }, interval*1000); + } + } else { + // no HRM - manually push data + if (realtime) interval=10; + activityInterval = setInterval(function() { + sendActivity(-1); + }, interval*1000); + } + } var _GB = global.GB; global.GB = (event) => { @@ -163,6 +207,9 @@ case "find": handleFindEvent(event); break; + case "act": + handleActivityEvent(event); + break; } if(_GB)setTimeout(_GB,0,event); }; @@ -201,14 +248,39 @@ } } - WIDGETS["gbridgew"] = {area: "tl", width: 24, draw: draw, reload: reload}; - reload(); - function sendBattery() { gbSend({ t: "status", bat: E.getBattery() }); } + + // Send a summary of activity to Gadgetbridge + function sendActivity(hrm) { + var steps = currentSteps - lastSentSteps; + lastSentSteps = 0; + gbSend({ t: "act", stp: steps, hrm:hrm }); + } + // Battery monitor NRF.on("connect", () => setTimeout(sendBattery, 2000)); setInterval(sendBattery, 10*60*1000); sendBattery(); + // Activity monitor + Bangle.on("step", s => { + if (!lastSentSteps) + lastSentSteps = s-1; + currentSteps = s; + }); + Bangle.on('HRM',function(hrm) { + var ok = hrm.confidence>80; + if (hrmTimeout!==undefined) hrmTimeout--; + if (ok || hrmTimeout<=0) { + if (hrmTimeout!==undefined) + Bangle.setHRMPower(0); + sendActivity(hrm.confidence>20 ? hrm.bpm : -1); + } + }); + handleActivityEvent({}); // kicks off activity reporting + + // Finally add widget + WIDGETS["gbridgew"] = {area: "tl", width: 24, draw: draw, reload: reload}; + reload(); })(); diff --git a/apps/gpsnav/ChangeLog b/apps/gpsnav/ChangeLog index ee8b61d7b..b4eaf5472 100644 --- a/apps/gpsnav/ChangeLog +++ b/apps/gpsnav/ChangeLog @@ -2,3 +2,5 @@ 0.02: Add SCREENACCESS interface 0.03: Add Waypoint Editor 0.04: Fix great circle formula +0.05: Use locale for speed and distance + fix Vector font sizes + diff --git a/apps/gpsnav/app.js b/apps/gpsnav/app.js index 3bbc756b7..8903e07fb 100644 --- a/apps/gpsnav/app.js +++ b/apps/gpsnav/app.js @@ -4,18 +4,19 @@ var buf = Graphics.createArrayBuffer(240,50,2,{msb:true}); var candraw = true; function flip(b,y) { - g.drawImage({width:240,height:50,bpp:2,buffer:b.buffer, palette:pal2color},0,y); - b.clear(); + g.drawImage({width:240,height:50,bpp:2,buffer:b.buffer, palette:pal2color},0,y); + b.clear(); } var brg=0; var wpindex=0; const labels = ["N","NE","E","SE","S","SW","W","NW"]; +var loc = require("locale"); function drawCompass(course) { if (!candraw) return; buf.setColor(1); - buf.setFont("Vector",16); + buf.setFont("Vector",24); var start = course-90; if (start<0) start+=360; buf.fillRect(28,45,212,49); @@ -44,27 +45,21 @@ function drawCompass(course) { if (bpos>210) bpos = 226; buf.setColor(2); buf.fillCircle(bpos,40,8); - } + } flip(buf,Yoff); } //displayed heading var heading = 0; -function newHeading(m,h){ - var s = Math.abs(m - h); - var delta = 1; - if (s<2) return h; - if (m > h){ - if (s >= 180) { delta = -1; s = 360 - s;} - } else if (m <= h){ - if (s < 180) delta = -1; - else s = 360 -s; - } - delta = delta * (1 + Math.round(s/15)); - heading+=delta; - if (heading<0) heading += 360; - if (heading>360) heading -= 360; - return heading; +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<2) return h; + var hd = h + delta*(1 + Math.round(s/5)); + if (hd<0) hd+=360; + if (hd>360)hd-= 360; + return hd; } var course =0; @@ -93,27 +88,28 @@ function bearing(a,b){ } function distance(a,b){ - var dsigma = Math.acos(Math.sin(radians(a.lat))*Math.sin(radians(b.lat))+Math.cos(radians(a.lat))*Math.cos(radians(b.lat))*Math.cos(radians(a.lon-b.lon))); - return Math.round(dsigma*6371000); + var x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2)); + var y = radians(b.lat-a.lat); + return Math.round(Math.sqrt(x*x + y*y) * 6371000); } var selected = false; function drawN(){ + var txt = loc.speed(speed); buf.setColor(1); buf.setFont("6x8",2); buf.drawString("o",100,0); buf.setFont("6x8",1); - buf.drawString("kph",220,40); - buf.setFont("Vector",40); + buf.drawString(txt.substring(txt.length-3),220,40); + buf.setFont("Vector",48); var cs = course.toString(); cs = course<10?"00"+cs : course<100 ?"0"+cs : cs; buf.drawString(cs,10,0); - var txt = (speed<10) ? speed.toFixed(1) : Math.round(speed); - buf.drawString(txt,140,4); + buf.drawString(txt.substring(0,txt.length-3),140,4); flip(buf,Yoff+70); buf.setColor(1); - buf.setFont("Vector",20); + buf.setFont("Vector",24); var bs = brg.toString(); bs = brg<10?"00"+bs : brg<100 ?"0"+bs : bs; buf.setColor(3); @@ -123,10 +119,7 @@ function drawN(){ buf.drawString(wp.name,140,0); buf.setColor(1); buf.drawString(bs,60,0); - if (dist<1000) - buf.drawString(dist.toString()+"m",60,30); - else - buf.drawString((dist/1000).toFixed(2)+"Km",60,30); + buf.drawString(loc.distance(dist),60,30); flip(buf,Yoff+130); g.setFont("6x8",1); g.setColor(0,0,0); @@ -165,7 +158,7 @@ function stopdraw() { function startTimers() { candraw=true; intervalRefSec = setInterval(function() { - newHeading(course,heading); + heading = newHeading(course,heading); if (course!=heading) drawCompass(heading); },200); } @@ -192,17 +185,17 @@ function setButtons(){ } var SCREENACCESS = { - withApp:true, - request:function(){ - this.withApp=false; - stopdraw(); - clearWatch(); - }, - release:function(){ - this.withApp=true; - startdraw(); - setButtons(); - } + withApp:true, + request:function(){ + this.withApp=false; + stopdraw(); + clearWatch(); + }, + release:function(){ + this.withApp=true; + startdraw(); + setButtons(); + } } Bangle.on('lcdPower',function(on) { @@ -227,10 +220,10 @@ function nextwp(inc){ } function doselect(){ - if (selected && 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); + 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(); diff --git a/apps/gpsnav/app.min.js b/apps/gpsnav/app.min.js new file mode 100644 index 000000000..7771e2b98 --- /dev/null +++ b/apps/gpsnav/app.min.js @@ -0,0 +1,11 @@ +var Yoff=40,pal2color=new Uint16Array([0,65535,2047,50712],0,2),buf=Graphics.createArrayBuffer(240,50,2,{msb:!0}),candraw=!0;function flip(a,c){g.drawImage({width:240,height:50,bpp:2,buffer:a.buffer,palette:pal2color},0,c);a.clear()}var brg=0,wpindex=0,labels="N NE E SE S SW W NW".split(" "),loc=require("locale"); +function drawCompass(a){if(candraw){buf.setColor(1);buf.setFont("Vector",24);var c=a-90;0>c&&(c+=360);buf.fillRect(28,45,212,49);var b=30,d=15-c%15;15>d?b+=d:d=0;for(var e=d;e<=180-d;e+=15){var f=c+e;0==f%90?(buf.drawString(labels[Math.floor(f/45)%8],b-8,0),buf.fillRect(b-2,25,b+2,45)):0==f%45?(buf.drawString(labels[Math.floor(f/45)%8],b-12,0),buf.fillRect(b-2,30,b+2,45)):0==f%15&&buf.fillRect(b,35,b+1,45);b+=15}0!=wpindex&&(a=brg-a,180a&&(a+=360),a+=120,30>a&&(a=14),210c?1:-1;180<=b&&(b=360-b,d=-d);if(2>b)return c;b=c+d*(1+Math.round(b/5));0>b&&(b+=360);360course?"00"+c:100>course?"0"+c:c;buf.drawString(c,10,0);buf.drawString(a.substring(0,a.length-3),140,4);flip(buf,Yoff+70);buf.setColor(1);buf.setFont("Vector",24);a=brg.toString();a=10>brg?"00"+a:100>brg?"0"+a:a;buf.setColor(3);buf.drawString("Brg: ",0,0);buf.drawString("Dist: ", +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(); +Bangle.setGPSPower(1);drawAll();startTimers();Bangle.on("GPS",onGPS);setButtons(); diff --git a/apps/gpsrec/ChangeLog b/apps/gpsrec/ChangeLog index 01788e08f..8c08a4ec5 100644 --- a/apps/gpsrec/ChangeLog +++ b/apps/gpsrec/ChangeLog @@ -15,3 +15,6 @@ 0.12: Add option to plot on top of OpenStreetMap tiles (when they are installed on the watch) 0.13: Increase GPS recording accuracy by one decimal place Ensure default time period is 10 +0.14: Now use the openstmap lib for map plotting +0.15: Add plotTrack method to allow current track to be plotted on a map (#395) + Add gpsrec app to Settings menu diff --git a/apps/gpsrec/app.js b/apps/gpsrec/app.js index c7de29d32..4fa51e3e1 100644 --- a/apps/gpsrec/app.js +++ b/apps/gpsrec/app.js @@ -2,7 +2,10 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); var settings = require("Storage").readJSON("gpsrec.json",1)||{}; -var qOpenStMap = (require("Storage").list("openstmap.json")>0); +var osm; +try { // if it's installed, use the OpenStreetMap module + osm = require("openstmap"); +} catch (e) {} function getFN(n) { return ".gpsrc"+n.toString(36); @@ -134,7 +137,7 @@ function viewTrack(n, info) { info.qOSTM = false; plotTrack(info); }; - if (qOpenStMap) + if (osm) menu['Plot OpenStMap'] = function() { info.qOSTM = true; plotTrack(info); @@ -161,49 +164,22 @@ function viewTrack(n, info) { return E.showMenu(menu); } -function drawopenstmap(lat, lon, map) { - var s = require("Storage"); - var cx = g.getWidth()/2; - var cy = g.getHeight()/2; - var p = Bangle.project({lat:lat,lon:lon}); - var ix = (p.x-map.center.x)*4096/map.scale + (map.imgx/2) - cx; - var iy = (map.center.y-p.y)*4096/map.scale + (map.imgy/2) - cy; - var tx = 0|(ix/map.tilesize); - var ty = 0|(iy/map.tilesize); - var ox = (tx*map.tilesize)-ix; - var oy = (ty*map.tilesize)-iy; - for (var x=ox,ttx=tx;x String.fromCodePoint(char.charCodeAt(0)+127397) )+" "; + if (localeParts[1]=="NAV") + icon = "⛵✈️ "; return `` }).join("\n"); diff --git a/apps/locale/locales.js b/apps/locale/locales.js index c29a49937..0861bc907 100644 --- a/apps/locale/locales.js +++ b/apps/locale/locales.js @@ -4,13 +4,15 @@ const distanceUnits = { // how many meters per X? "yd": 0.9144, "mi": 1609.34, "km": 1000, - "kmi": 1000 + "kmi": 1000, + "nm": 1852 }; const speedUnits = { // how many kph per X? "kmh": 1, "kph": 1, "km/h": 1, - "mph": 1.60934 + "mph": 1.60934, + "kts": 1.852 }; /* @@ -87,7 +89,7 @@ var locales = { currency_symbol: "Rs.", currency_first: true, int_curr_symbol: "INR", - speed: 'kms', + speed: 'kmh', distance: { "0": "m", "1": "km" }, temperature: '°C', ampm: { 0: "am", 1: "pm" }, @@ -99,6 +101,24 @@ var locales = { day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", // No translation for english... }, + "en_NAV": { // navigation units nautical miles and knots + lang: "en_NAV", + decimal_point: ".", + thousands_sep: ",", + currency_symbol: "£", currency_first: true, + int_curr_symbol: "GBP", + speed: 'kts', + distance: { "0": "m", "1": "nm" }, + temperature: '°C', + ampm: { 0: "am", 1: "pm" }, + timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" }, + datePattern: { 0: "%b %d %Y", 1: "%d/%m/%Y" }, // Feb 28 2020" // "01/03/2020"(short) + abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec", + month: "January,February,March,April,May,June,July,August,September,October,November,December", + abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat", + day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", + // No translation for english... + }, "de_DE": { lang: "de_DE", decimal_point: ",", diff --git a/apps/openstmap/ChangeLog b/apps/openstmap/ChangeLog index a7b8065fa..64b39b509 100644 --- a/apps/openstmap/ChangeLog +++ b/apps/openstmap/ChangeLog @@ -1,3 +1,5 @@ 0.01: New App! 0.02: Fix marker position, color, and map scaling 0.03: Show widgets (mainly so we can use the GPS recorder widget) +0.04: Move map rendering to a module (fix #396) +0.05: Show currently active gpsrec GPS trace (fix #395) diff --git a/apps/openstmap/app.js b/apps/openstmap/app.js index 5be4be82a..940557361 100644 --- a/apps/openstmap/app.js +++ b/apps/openstmap/app.js @@ -1,62 +1,37 @@ -var s = require("Storage"); -var map = s.readJSON("openstmap.json"); +var m = require("openstmap"); var HASWIDGETS = true; var y1,y2; - -map.center = Bangle.project({lat:map.lat,lon:map.lon}); -var lat = map.lat, lon = map.lon; var fix = {}; function redraw() { - var cx = g.getWidth()/2; - var cy = g.getHeight()/2; - var p = Bangle.project({lat:lat,lon:lon}); - var ix = (p.x-map.center.x)*4096/map.scale + (map.imgx/2) - cx; - var iy = (map.center.y-p.y)*4096/map.scale + (map.imgy/2) - cy; - //console.log(ix,iy); - var tx = 0|(ix/map.tilesize); - var ty = 0|(iy/map.tilesize); - var ox = (tx*map.tilesize)-ix; - var oy = (ty*map.tilesize)-iy; g.setClipRect(0,y1,g.getWidth()-1,y2); - for (var x=ox,ttx=tx;x