Merge remote-tracking branch 'upstream/master'

pull/3017/head
Hugh Barney 2023-09-12 20:51:15 +01:00
commit b55d5269da
36 changed files with 2252 additions and 163 deletions

View File

@ -31,7 +31,7 @@
<div class="form-group"> <div class="form-group">
<label class="form-label">Select which GNSS system you want.</label> <label class="form-label">Select which GNSS system you want.</label>
<label class="form-radio"> <label class="form-radio">
<input type="radio" name="gnss_select" value="1" checked><i class="form-icon"></i> GPS <input type="radio" name="gnss_select" value="1" checked><i class="form-icon"></i> GPS (fastest to get a fix)
</label> </label>
<label class="form-radio"> <label class="form-radio">
<input type="radio" name="gnss_select" value="2"><i class="form-icon"></i> BDS <input type="radio" name="gnss_select" value="2"><i class="form-icon"></i> BDS

View File

@ -23,7 +23,16 @@ var SL = W/15; // ship side length
var AS = W/18; // asteroid radius var AS = W/18; // asteroid radius
// radius of ship, assumed a circle inside equilateral traingle of side SS // radius of ship, assumed a circle inside equilateral traingle of side SS
// r = a / root 3 where a is length of equilateral triangle // r = a / root 3 where a is length of equilateral triangle
var SR = SS / Math.sqrt(3); var SR = SS / Math.sqrt(3);
var AST = [ // asteroid polygon as X/Y pairs
0 ,-1.5,
1 , 0,
0.5, 0,
0.5, 0.5,
0 , 1,
-1 , 0,
-1 , -1
];
g.clear().setFontAlign(0,-1); g.clear().setFontAlign(0,-1);
@ -148,15 +157,7 @@ function onFrame() {
a.y += a.vy*d; a.y += a.vy*d;
//g.drawCircle(a.x, a.y, a.rad); //g.drawCircle(a.x, a.y, a.rad);
// a 7 point asteroid with rough circle radius of scale 2 // a 7 point asteroid with rough circle radius of scale 2
g.drawPoly([ g.drawPoly(g.transformVertices(AST,{x:a.x,y:a.y,scale:a.rad,rotate:t}),true);
a.x , a.y - 1.5 * a.rad,
a.x + a.rad , a.y ,
a.x + a.rad/2 , a.y ,
a.x + a.rad/2 , a.y + a.rad/2 ,
a.x , a.y + a.rad ,
a.x - a.rad , a.y ,
a.x - a.rad , a.y - a.rad
],true);
if (a.x<0) a.x+=W; if (a.x<0) a.x+=W;
if (a.y<0) a.y+=H; if (a.y<0) a.y+=H;

View File

@ -1 +1,2 @@
0.01: New App! 0.01: New App!
0.02: Shorten the timeout before executing to 250 ms.

View File

@ -1,6 +1,6 @@
# Fast Reset # Fast Reset
Reset the watch by holding the hardware button for half a second. If 'Fastload Utils' is installed this will typically be done with fastloading. A buzz acts as indicator. Reset the watch by pressing the hardware button just a little bit longer than a click. If 'Fastload Utils' is installed this will typically be done with fastloading. A buzz acts as indicator.
Fast Reset was developed with the app history feature of 'Fastload Utils' in mind. If many apps are in the history stack, the user may want a fast way to exit directly to the clock face without using the firmwares reset function. Fast Reset was developed with the app history feature of 'Fastload Utils' in mind. If many apps are in the history stack, the user may want a fast way to exit directly to the clock face without using the firmwares reset function.
@ -12,11 +12,11 @@ Just install and it will run as boot code.
If 'Fastload Utils' is installed fastloading will be used when possible. Otherwise a standard `load(.bootcde)` is used. If 'Fastload Utils' is installed fastloading will be used when possible. Otherwise a standard `load(.bootcde)` is used.
If the hardware button is held for longer the standard reset functionality of the firmware is executed as well (total 1.5 seconds). And eventually the watchdog will be kicked. If the hardware button is held for longer the standard reset functionality of the firmware is executed as well. And eventually the watchdog will be kicked.
## Controls ## Controls
Hold the hardware button for half a second to feel the buzz, loading the clock face. Press the hardware button just a little longer than a click to feel the buzz, loading the clock face.
## Requests ## Requests

View File

@ -1,5 +1,5 @@
{let buzzTimeout; {let buzzTimeout;
setWatch((e)=>{ setWatch((e)=>{
if (e.state) buzzTimeout = setTimeout(()=>{Bangle.buzz(80,0.40);Bangle.showClock();}, 500); if (e.state) buzzTimeout = setTimeout(()=>{Bangle.buzz(80,0.40);Bangle.showClock();}, 250);
if (!e.state && buzzTimeout) clearTimeout(buzzTimeout);}, if (!e.state && buzzTimeout) clearTimeout(buzzTimeout);},
BTN,{repeat:true, edge:'both' });} BTN,{repeat:true, edge:'both' });}

View File

@ -1,12 +1,12 @@
{ "id": "fastreset", { "id": "fastreset",
"name": "Fast Reset", "name": "Fast Reset",
"shortName":"Fast Reset", "shortName":"Fast Reset",
"version":"0.01", "version":"0.02",
"description": "Reset the watch by holding the hardware button for half a second. If 'Fastload Utils' is installed this will typically be done with fastloading. A buzz acts as indicator.", "description": "Reset the watch by pressing the hardware button just a little bit longer than a click. If 'Fastload Utils' is installed this will typically be done with fastloading. A buzz acts as indicator.",
"icon": "app.png", "icon": "app.png",
"type": "bootloader", "type": "bootloader",
"tags": "system", "tags": "system",
"supports" : ["BANGLEJS2"], "supports" : ["BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"fastreset.boot.js","url":"boot.js"} {"name":"fastreset.boot.js","url":"boot.js"}

View File

@ -5,3 +5,4 @@
0.05: Prevent drawing into app area. 0.05: Prevent drawing into app area.
0.06: Fix issue where .draw was being called by reference (not allowing widgets to be hidden) 0.06: Fix issue where .draw was being called by reference (not allowing widgets to be hidden)
0.07: Handle the swipe event that is generated when draging to change light intensity, so it doesn't trigger some other swipe handler. 0.07: Handle the swipe event that is generated when draging to change light intensity, so it doesn't trigger some other swipe handler.
0.08: Ensure boot code doesn't allocate and leave a gloval variable named 'settings'

View File

@ -1,5 +1,6 @@
{
// load settings // load settings
var settings = Object.assign({ let settings = Object.assign({
value: 1, value: 1,
isOn: true isOn: true
}, require("Storage").readJSON("lightswitch.json", true) || {}); }, require("Storage").readJSON("lightswitch.json", true) || {});
@ -12,6 +13,4 @@ Bangle.removeListener("tap", require("lightswitch.js").tapListener);
// add tap listener to unlock and/or flash backlight // add tap listener to unlock and/or flash backlight
if (settings.unlockSide || settings.tapSide) Bangle.on("tap", require("lightswitch.js").tapListener); if (settings.unlockSide || settings.tapSide) Bangle.on("tap", require("lightswitch.js").tapListener);
}
// clear variable
settings = undefined;

View File

@ -2,7 +2,7 @@
"id": "lightswitch", "id": "lightswitch",
"name": "Light Switch Widget", "name": "Light Switch Widget",
"shortName": "Light Switch", "shortName": "Light Switch",
"version": "0.07", "version": "0.08",
"description": "A fast way to switch LCD backlight on/off, change the brightness and show the lock status. All in one widget.", "description": "A fast way to switch LCD backlight on/off, change the brightness and show the lock status. All in one widget.",
"icon": "images/app.png", "icon": "images/app.png",
"screenshots": [ "screenshots": [

View File

@ -2,6 +2,10 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="stylesheet" href="../../css/spectre.min.css"> <link rel="stylesheet" href="../../css/spectre.min.css">
<style>
table { width:100%;}
.table_t {font-weight:bold;width:40%;};
</style>
</head> </head>
<body> <body>
@ -13,6 +17,11 @@
<div class="form-group"> <div class="form-group">
<input id="translations" type="checkbox" /> <label for="translations">Add common language translations like "Yes", "No", "On", "Off"<br/><i>(Not recommended. For translations use the option under <code>More...</code> in the app loader.</i></label> <input id="translations" type="checkbox" /> <label for="translations">Add common language translations like "Yes", "No", "On", "Off"<br/><i>(Not recommended. For translations use the option under <code>More...</code> in the app loader.</i></label>
</div> </div>
<p>
<table id="examples">
</p>
</table>
<p>Then click <button id="upload" class="btn btn-primary">Upload</button></p> <p>Then click <button id="upload" class="btn btn-primary">Upload</button></p>
<script src="../../core/lib/customize.js"></script> <script src="../../core/lib/customize.js"></script>
@ -93,21 +102,8 @@ exports = { name : "en_GB", currencySym:"£",
checkChars(locale,localeName); checkChars(locale,localeName);
}); });
var languageSelector = document.getElementById("languages");
languageSelector.innerHTML = Object.keys(locales).map(l=>{
var localeParts = l.split("_"); // en_GB -> ["en","GB"]
var icon = "";
// If we have a 2 char ISO country code, use it to get the unicode flag
if (localeParts[1] && localeParts[1].length==2)
icon = localeParts[1].toUpperCase().replace(/./g, char => String.fromCodePoint(char.charCodeAt(0)+127397) )+" ";
if (localeParts[1]=="NAV")
icon = "&#9973;&#9992;&#65039; ";
return `<option value="${l}">${icon}${l}</option>`
}).join("\n");
document.getElementById("upload").addEventListener("click", function() { function createLocaleModule(lang) {
const lang = languageSelector.options[languageSelector.selectedIndex].value;
console.log(`Language ${lang}`); console.log(`Language ${lang}`);
const translations = document.getElementById('translations').checked; const translations = document.getElementById('translations').checked;
@ -151,7 +147,7 @@ exports = { name : "en_GB", currencySym:"£",
var replaceList = { var replaceList = {
"%Y": "d.getFullYear()", "%Y": "d.getFullYear()",
"%y": "(d.getFullYear().toString()).slice(-2)", "%y": "d.getFullYear().toString().slice(-2)",
"%m": "('0'+(d.getMonth()+1).toString()).slice(-2)", "%m": "('0'+(d.getMonth()+1).toString()).slice(-2)",
"%-m": "d.getMonth()+1", "%-m": "d.getMonth()+1",
"%d": "('0'+d.getDate()).slice(-2)", "%d": "('0'+d.getDate()).slice(-2)",
@ -182,16 +178,17 @@ exports = { name : "en_GB", currencySym:"£",
`exports.number(n) + ${js(locale.currency_symbol)}`; `exports.number(n) + ${js(locale.currency_symbol)}`;
var temperature = locale.temperature=='°F' ? '(t*9/5)+32' : 't'; var temperature = locale.temperature=='°F' ? '(t*9/5)+32' : 't';
var localeModule = ` function getLocaleModule(isLocal) {
return `
function round(n, dp) { function round(n, dp) {
if (dp===undefined) dp=0; if (dp===undefined) dp=0;
var p = Math.min(dp,dp - Math.floor(Math.log(n)/Math.log(10))); var p = Math.max(0,Math.min(dp,dp - Math.floor(Math.log(n)/Math.log(10))));
return n.toFixed(p); return n.toFixed(p);
} }
var is12; var is12;
function getHours(d) { function getHours(d) {
var h = d.getHours(); var h = d.getHours();
if (is12 === undefined) is12 = (require('Storage').readJSON('setting.json', 1) || {})["12hour"]; if (is12 === undefined) is12 = ${isLocal ? "false" : `(require('Storage').readJSON('setting.json', 1) || {})["12hour"]`};
if (!is12) return ('0' + h).slice(-2); if (!is12) return ('0' + h).slice(-2);
return ((h % 12 == 0) ? 12 : h % 12).toString(); return ((h % 12 == 0) ? 12 : h % 12).toString();
} }
@ -224,7 +221,54 @@ exports = {
time: (d,short) => short ? \`${timeS}\` : \`${timeN}\`, time: (d,short) => short ? \`${timeS}\` : \`${timeN}\`,
meridian: d => d.getHours() < 12 ? ${js(locale.ampm[0])}:${js(locale.ampm[1])}, meridian: d => d.getHours() < 12 ? ${js(locale.ampm[0])}:${js(locale.ampm[1])},
}; };
`.trim(); `.trim()
};
var exports;
eval(getLocaleModule(true));
console.log("exports:",exports);
var date = new Date();
document.getElementById("examples").innerHTML = `
<tr><td class="table_t"></td><td style="font-weight:bold">Short</td><td style="font-weight:bold">Long</td></tr>
<tr><td class="table_t">Day</td><td>${exports.dow(date,1)}</td><td>${exports.dow(date,0)}</td></tr>
<tr><td class="table_t">Month</td><td>${exports.month(date,1)}</td><td>${exports.month(date,0)}</td></tr>
<tr><td class="table_t">Date</td><td>${exports.date(date,1)}</td><td>${exports.date(date,0)}</td></tr>
<tr><td class="table_t">Time</td><td>${exports.time(date,1)}</td><td>${exports.time(date,0)}</td></tr>
<tr><td class="table_t">Number</td><td>${exports.number(12.3456789)}</td><td>${exports.number(12.3456789,4)}</td></tr>
<tr><td class="table_t">Currency</td><td></td><td>${exports.currency(12.34)}</td></tr>
<tr><td class="table_t">Distance</td><td>${exports.distance(12.34,0)}</td><td>${exports.distance(12345.6,1)}</td></tr>
<tr><td class="table_t">Speed</td><td></td><td>${exports.speed(123)}</td></tr>
<tr><td class="table_t">Temperature</td><td></td><td>${exports.temp(12,0)}</td></tr>
`;
return getLocaleModule(false);
}
var languageSelector = document.getElementById("languages");
languageSelector.innerHTML = Object.keys(locales).map(l=>{
var locale = locales[l];
var localeParts = l.split("_"); // en_GB -> ["en","GB"]
var icon = "";
// If we have a 2 char ISO country code, use it to get the unicode flag
if (locale.icon)
icon = locale.icon+" ";
else if (localeParts[1] && localeParts[1].length==2)
icon = localeParts[1].toUpperCase().replace(/./g, char => String.fromCodePoint(char.charCodeAt(0)+127397) )+" ";
return `<option value="${l}">${icon}${l}${locale.notes?" - "+locale.notes:""}</option>`
}).join("\n");
languageSelector.addEventListener('change', function() {
const lang = languageSelector.options[languageSelector.selectedIndex].value;
createLocaleModule(lang);
});
// initial value
createLocaleModule(languageSelector.options[languageSelector.selectedIndex].value);
document.getElementById("upload").addEventListener("click", function() {
const lang = languageSelector.options[languageSelector.selectedIndex].value;
var localeModule = createLocaleModule(lang);
console.log("Locale Module is:",localeModule); console.log("Locale Module is:",localeModule);
sendCustomizedApp({ sendCustomizedApp({

View File

@ -79,6 +79,44 @@ var locales = {
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
// No translation for english... // No translation for english...
}, },
"en_US": {
lang: "en_US",
notes: "USA with MM/DD/YY date",
decimal_point: ".",
thousands_sep: ",",
currency_symbol: "$", currency_first: true,
int_curr_symbol: "USD",
speed: "mph",
distance: { 0: "ft", 1: "mi" },
temperature: "°F",
ampm: { 0: "am", 1: "pm" },
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
datePattern: { 0: "%b %d, %Y", 1: "%m/%d/%y" },
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...
},
"en_US 2": {
lang: "en_US 2", icon:"🇺🇸",
notes: "USA with YYYY-MM-DD date",
decimal_point: ".",
thousands_sep: ",",
currency_symbol: "$", currency_first: true,
int_curr_symbol: "USD",
speed: "mph",
distance: { 0: "ft", 1: "mi" },
temperature: "°F",
ampm: { 0: "am", 1: "pm" },
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
datePattern: { 0: "%b %d, %Y", 1: "%Y-%m-%d" },
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...
},
"en_IN": { "en_IN": {
lang: "en_IN", lang: "en_IN",
decimal_point: ".", decimal_point: ".",
@ -118,7 +156,7 @@ var locales = {
// No translation for english... // No translation for english...
}, },
"en_NAV": { // navigation units nautical miles and knots "en_NAV": { // navigation units nautical miles and knots
lang: "en_NAV", lang: "en_NAV", icon: "&#9973;&#9992;&#65039;",
decimal_point: ".", decimal_point: ".",
thousands_sep: ",", thousands_sep: ",",
currency_symbol: "£", currency_first: true, currency_symbol: "£", currency_first: true,
@ -154,24 +192,6 @@ var locales = {
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus", trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus",
"< Back": "< Zurück", "Delete": "Löschen", "Mark Unread": "Als ungelesen markieren" } "< Back": "< Zurück", "Delete": "Löschen", "Mark Unread": "Als ungelesen markieren" }
}, },
"en_US": {
lang: "en_US",
decimal_point: ".",
thousands_sep: ",",
currency_symbol: "$", currency_first: true,
int_curr_symbol: "USD",
speed: "mph",
distance: { 0: "ft", 1: "mi" },
temperature: "°F",
ampm: { 0: "am", 1: "pm" },
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
datePattern: { 0: "%b %d, %Y", 1: "%m/%d/%y" },
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...
},
"en_JP": { // we do not have the font, so it is not ja_JP "en_JP": { // we do not have the font, so it is not ja_JP
lang: "en_JP", lang: "en_JP",
decimal_point: ".", decimal_point: ".",

View File

@ -4,3 +4,4 @@
0.58: show/hide "messages" widget directly, instead of through library stub 0.58: show/hide "messages" widget directly, instead of through library stub
0.59: fixes message timeout by using setinterval, as it was intended. So the buzz is triggered every x seconds until the timeout occours. 0.59: fixes message timeout by using setinterval, as it was intended. So the buzz is triggered every x seconds until the timeout occours.
0.60: Bump version to allow new buzz.js module to be loaded - fixes memory/performance hog when buzz called 0.60: Bump version to allow new buzz.js module to be loaded - fixes memory/performance hog when buzz called
0.61: Add repeatCalls option to allow different repeat settings for messages vs calls

View File

@ -204,16 +204,18 @@ exports.buzz = function(msgSrc) {
if ((require("Storage").readJSON("setting.json", 1) || {}).quiet) return Promise.resolve(); // never buzz during Quiet Mode if ((require("Storage").readJSON("setting.json", 1) || {}).quiet) return Promise.resolve(); // never buzz during Quiet Mode
const msgSettings = require("Storage").readJSON("messages.settings.json", true) || {}; const msgSettings = require("Storage").readJSON("messages.settings.json", true) || {};
let pattern; let pattern;
let repeat;
if (msgSrc && msgSrc.toLowerCase()==="phone") { if (msgSrc && msgSrc.toLowerCase()==="phone") {
// special vibration pattern for incoming calls // special vibration pattern for incoming calls
pattern = msgSettings.vibrateCalls; pattern = msgSettings.vibrateCalls;
repeat = msgSettings.repeatCalls;
} else { } else {
pattern = msgSettings.vibrate; pattern = msgSettings.vibrate;
repeat = msgSettings.repeat;
} }
if (pattern===undefined) { pattern = ":"; } // pattern may be "", so we can't use || ":" here if (pattern===undefined) { pattern = ":"; } // pattern may be "", so we can't use || ":" here
if (!pattern) return Promise.resolve(); if (!pattern) return Promise.resolve();
let repeat = msgSettings.repeat;
if (repeat===undefined) repeat = 4; // repeat may be zero if (repeat===undefined) repeat = 4; // repeat may be zero
if (repeat) if (repeat)
{ {

View File

@ -1,7 +1,7 @@
{ {
"id": "messages", "id": "messages",
"name": "Messages", "name": "Messages",
"version": "0.60", "version": "0.61",
"description": "Library to handle, load and store message events received from Android/iOS", "description": "Library to handle, load and store message events received from Android/iOS",
"icon": "app.png", "icon": "app.png",
"type": "module", "type": "module",

View File

@ -6,6 +6,7 @@
if (settings.vibrate===undefined) settings.vibrate=":"; if (settings.vibrate===undefined) settings.vibrate=":";
if (settings.vibrateCalls===undefined) settings.vibrateCalls=":"; if (settings.vibrateCalls===undefined) settings.vibrateCalls=":";
if (settings.repeat===undefined) settings.repeat=4; if (settings.repeat===undefined) settings.repeat=4;
if (settings.repeatCalls===undefined) settings.repeatCalls=settings.repeat;
if (settings.vibrateTimeout===undefined) settings.vibrateTimeout=60; if (settings.vibrateTimeout===undefined) settings.vibrateTimeout=60;
if (settings.unreadTimeout===undefined) settings.unreadTimeout=60; if (settings.unreadTimeout===undefined) settings.unreadTimeout=60;
if (settings.maxMessages===undefined) settings.maxMessages=3; if (settings.maxMessages===undefined) settings.maxMessages=3;
@ -33,6 +34,12 @@
format: v => v?v+"s":/*LANG*/"Off", format: v => v?v+"s":/*LANG*/"Off",
onchange: v => updateSetting("repeat", v) onchange: v => updateSetting("repeat", v)
}, },
/*LANG*/'Repeat for calls': {
value: settings().repeatCalls,
min: 0, max: 10,
format: v => v?v+"s":/*LANG*/"Off",
onchange: v => updateSetting("repeatCalls", v)
},
/*LANG*/'Vibrate timer': { /*LANG*/'Vibrate timer': {
value: settings().vibrateTimeout, value: settings().vibrateTimeout,
min: 0, max: settings().maxUnreadTimeout, step : 10, min: 0, max: settings().maxUnreadTimeout, step : 10,

View File

@ -3,3 +3,4 @@
0.03: Update help screen with more details. 0.03: Update help screen with more details.
0.04: Update cards to draw rounded on newer firmware. Make sure in-game menu can't be pulled up during end of game. 0.04: Update cards to draw rounded on newer firmware. Make sure in-game menu can't be pulled up during end of game.
0.05: add confirmation prompt to new game to prevent fat fingering new game during existing one. 0.05: add confirmation prompt to new game to prevent fat fingering new game during existing one.
0.06: fix AI logic typo and add prompt to show what AI played each turn.

View File

@ -2,7 +2,7 @@
"name": "Red 7 Card Game", "name": "Red 7 Card Game",
"shortName" : "Red 7", "shortName" : "Red 7",
"icon": "icon.png", "icon": "icon.png",
"version":"0.05", "version":"0.06",
"description": "An implementation of the card game Red 7 for your watch. Play against the AI and be the last player still in the game to win!", "description": "An implementation of the card game Red 7 for your watch. Play against the AI and be the last player still in the game to win!",
"tags": "game", "tags": "game",
"supports":["BANGLEJS2"], "supports":["BANGLEJS2"],

View File

@ -17,6 +17,9 @@ class Card {
//this.rect = {}; //this.rect = {};
this.clippedRect = {}; this.clippedRect = {};
} }
get description() {
return this.cardColor+" "+this.cardNum;
}
get number() { get number() {
return this.cardNum; return this.cardNum;
} }
@ -514,7 +517,7 @@ class AI {
//Play card that wins //Play card that wins
this.palette.addCard(c); this.palette.addCard(c);
this.hand.removeCard(c); this.hand.removeCard(c);
return true; return { winning: true, paletteAdded: c };
} }
clonePalette.removeCard(c); clonePalette.removeCard(c);
} }
@ -524,26 +527,26 @@ class AI {
//Play rule card that wins //Play rule card that wins
ruleStack.addCard(c); ruleStack.addCard(c);
this.hand.removeCard(c); this.hand.removeCard(c);
return true; return { winning: true, ruleAdded: c };
} else { } else {
//Check if any palette play can win with rule. //Check if any palette play can win with rule.
for(let h of this.hand.handCards) { for(let h of this.hand.handCards) {
if(h === c) {} if(h === c) {}
else { else {
clonePalette.addCard(c); clonePalette.addCard(h);
if(isWinningCombo(c, clonePalette, otherPalette)) { if(isWinningCombo(c, clonePalette, otherPalette)) {
ruleStack.addCard(c); ruleStack.addCard(c);
this.hand.removeCard(c); this.hand.removeCard(c);
this.palette.addCard(h); this.palette.addCard(h);
this.hand.removeCard(h); this.hand.removeCard(h);
return true; return { winning: true, ruleAdded: c, paletteAdded: h };
} }
clonePalette.removeCard(c); clonePalette.removeCard(h);
} }
} }
} }
} }
return false; return { winning: false };
} }
} }
@ -726,18 +729,20 @@ function finishTurn() {
if(AIhand.handCards.length === 0) { if(AIhand.handCards.length === 0) {
drawGameOver(true); drawGameOver(true);
} else { } else {
var takenTurn = aiPlayer.takeTurn(ruleCards, playerPalette); var aiResult = aiPlayer.takeTurn(ruleCards, playerPalette);
//Check if game over conditions met. E.showPrompt("AI played: " + ("paletteAdded" in aiResult ? aiResult["paletteAdded"].description+" to pallete. ":"") + ("ruleAdded" in aiResult ? aiResult["ruleAdded"].description+" to rules.":""),{buttons: {"Ok":0}}).then(function(){
if(!takenTurn) { //Check if game over conditions met.
drawGameOver(true); if(!aiResult["winning"]) {
} else if(playerHand.handCards.length === 0) { drawGameOver(true);
drawGameOver(false); } else if(playerHand.handCards.length === 0) {
} else if(!canPlay(playerHand, playerPalette, AIPalette)) { drawGameOver(false);
drawGameOver(false); } else if(!canPlay(playerHand, playerPalette, AIPalette)) {
} else { drawGameOver(false);
E.showMenu(); } else {
drawScreen1(); E.showMenu();
} drawScreen1();
}
});
} }
} }
@ -843,4 +848,3 @@ drawMainMenu();
setWatch(function(){ setWatch(function(){
drawMainMenu(); drawMainMenu();
},BTN, {edge: "rising", debounce: 50, repeat: true}); },BTN, {edge: "rising", debounce: 50, repeat: true});

View File

@ -38,6 +38,8 @@ Directories at the end of .mtar should be hashed, not linear searched.
Geojson is not really suitable as it takes a lot of storage. Geojson is not really suitable as it takes a lot of storage.
It would be nice to support polygons. Web-based tool for preparing maps would be nice.
Web-based tool for preparing maps would be nice. Storing 12bit coordinates, but only using 8bits.
Polygons should go first to get proper z-order.

View File

@ -386,7 +386,7 @@ function emptyMap() {
m.scale = 2; m.scale = 2;
g.reset().clearRect(R); g.reset().clearRect(R);
redraw(18); redraw(18);
print("Benchmark done (31 sec)"); print("Benchmark done");
} }
}; };
if (fix.fix) menu[/*LANG*/"Center GPS"]=() =>{ if (fix.fix) menu[/*LANG*/"Center GPS"]=() =>{
@ -400,36 +400,52 @@ function emptyMap() {
var gjson = null; var gjson = null;
function stringFromArray(data) {
var count = data.length;
var str = "";
for(var index = 0; index < count; index += 1)
str += String.fromCharCode(data[index]);
return str;
}
const st = require('Storage');
const hs = require('heatshrink');
function readTarFile(tar, f) { function readTarFile(tar, f) {
const st = require('Storage'); let json_off = st.read(tar, 0, 16) * 1;
json_off = st.read(tar, 0, 16) * 1;
if (isNaN(json_off)) { if (isNaN(json_off)) {
print("Don't have archive", tar); print("Don't have archive", tar);
return undefined; return undefined;
} }
while (1) { while (1) {
json_len = st.read(tar, json_off, 6) * 1; let json_len = st.read(tar, json_off, 6) * 1;
if (json_len == -1) if (json_len == -1)
break; break;
json_off += 6; json_off += 6;
json = st.read(tar, json_off, json_len); let json = st.read(tar, json_off, json_len);
//print("Have directory, ", json.length, "bytes"); //print("Have directory, ", json.length, "bytes");
//print(json); let files = JSON.parse(json);
files = JSON.parse(json); let rec = files[f];
//print(files); if (rec) {
rec = files[f]; let cs = st.read(tar, rec.st, rec.si);
if (rec) if (rec.comp == "hs") {
return st.read(tar, rec.st, rec.si); let d = stringFromArray(hs.decompress(cs));
//print("Decompressed", d);
return d;
}
return cs;
}
json_off += json_len; json_off += json_len;
} }
return undefined; return undefined;
} }
function loadVector(name) { function loadVector(name) {
var t1 = getTime(); var t1 = getTime();
print(".. Read", name); print(".. Read", name);
//s = require("Storage").read(name); //s = require("Storage").read(name);
var s = readTarFile("delme.mtar", name); var s = readTarFile("world.mtar", name);
if (s == undefined) { if (s == undefined) {
print("Don't have file", name); print("Don't have file", name);
return null; return null;
@ -438,8 +454,7 @@ function loadVector(name) {
print(".... Read and parse took ", getTime()-t1); print(".... Read and parse took ", getTime()-t1);
return r; return r;
} }
function drawPoint(a) { /* FIXME: let... */
function drawPoint(a) {
lon = a.geometry.coordinates[0]; lon = a.geometry.coordinates[0];
lat = a.geometry.coordinates[1]; lat = a.geometry.coordinates[1];
@ -459,7 +474,6 @@ function drawPoint(a) {
g.drawString(a.properties.name, p.x, p.y); g.drawString(a.properties.name, p.x, p.y);
points ++; points ++;
} }
function drawLine(a, qual) { function drawLine(a, qual) {
lon = a.geometry.coordinates[0][0]; lon = a.geometry.coordinates[0][0];
lat = a.geometry.coordinates[0][1]; lat = a.geometry.coordinates[0][1];
@ -485,38 +499,196 @@ function drawLine(a, qual) {
i = len-1; i = len-1;
points ++; points ++;
p1 = p2; p1 = p2;
g.flip();
} }
} }
function drawPolygon(a, qual) {
lon = a.geometry.coordinates[0][0];
lat = a.geometry.coordinates[0][1];
i = 1;
step = 1;
len = a.geometry.coordinates.length;
if (len > 62) {
step = log2(len) - 5;
step = 1<<step;
}
step = step * qual;
var p1 = m.latLonToXY(lat, lon);
let pol = [p1.x, p1.y];
while (i < len) {
lon = a.geometry.coordinates[i][0];
lat = a.geometry.coordinates[i][1];
var p2 = m.latLonToXY(lat, lon);
function drawVector(gjson, qual) { pol.push(p2.x, p2.y);
if (i == len-1)
break;
i = i + step;
if (i>len)
i = len-1;
points ++;
}
if (a.properties.fill) {
g.setColor(a.properties.fill);
} else {
g.setColor(.75, .75, 1);
}
g.fillPoly(pol, true);
if (a.properties.stroke) {
g.setColor(a.properties.stroke);
} else {
g.setColor(0,0,0)
}
g.drawPoly(pol, true);
}
function toScreen(tile, xy) {
// w, s, e, n, (x,y in 0..4096 range)
let x = xy[0];
let y = xy[1];
let r = {};
r.x = ((x/4096) * (tile[2]-tile[0])) + tile[0];
r.y = ((1-(y/4096)) * (tile[3]-tile[1])) + tile[1];
return r;
}
var d_off = 1;
function getBin(bin, i, prev) {
let x = bin[i*3 + d_off ]<<4;
let y = bin[i*3 + d_off+1]<<4;
//print("Point", x, y, bin);
return [x, y];
}
function getBinLength(bin) {
return (bin.length-d_off) / 3;
}
function newPoint(tile, a, rec, bin) {
var p = toScreen(tile, getBin(bin, 0, null));
var sz = 2;
if (a.properties) {
if (a.properties["marker-color"]) {
g.setColor(a.properties["marker-color"]);
}
if (a.properties.marker_size == "small")
sz = 1;
if (a.properties.marker_size == "large")
sz = 4;
}
g.fillRect(p.x-sz, p.y-sz, p.x+sz, p.y+sz);
if (rec.tags) {
g.setColor(0,0,0);
g.setFont("Vector", 18).setFontAlign(-1,-1);
g.drawString(rec.tags.name, p.x, p.y);
}
points ++;
}
function newLine(tile, a, bin) {
let xy = getBin(bin, 0, null);
let i = 1;
let step = 1;
let len = getBinLength(bin);
let p1 = toScreen(tile, xy);
if (a.properties && a.properties.stroke) {
g.setColor(a.properties.stroke);
}
while (i < len) {
xy = getBin(bin, i, xy);
var p2 = toScreen(tile, xy);
//print(p1.x, p1.y, p2.x, p2.y);
g.drawLine(p1.x, p1.y, p2.x, p2.y);
if (i == len-1)
break;
i = i + step;
if (i>len)
i = len-1;
points ++;
p1 = p2;
}
}
function newPolygon(tile, a, bin) {
let xy = getBin(bin, 0, null);
i = 1;
step = 1;
len = getBinLength(bin);
if (len > 62) {
step = log2(len) - 5;
step = 1<<step;
}
var p1 = toScreen(tile, xy);
let pol = [p1.x, p1.y];
while (i < len) {
xy = getBin(bin, i, xy); // FIXME... when skipping
var p2 = toScreen(tile, xy);
pol.push(p2.x, p2.y);
if (i == len-1)
break;
i = i + step;
if (i>len)
i = len-1;
points ++;
}
if (a.properties && a.properties.fill) {
g.setColor(a.properties.fill);
} else {
g.setColor(.75, .75, 1);
}
g.fillPoly(pol, true);
if (a.properties && a.properties.stroke) {
g.setColor(a.properties.stroke);
} else {
g.setColor(0,0,0)
}
g.drawPoly(pol, true);
}
function newVector(tile, rec) {
let bin = E.toUint8Array(atob(rec.b));
a = meta.attrs[bin[0]];
if (a.type == 1) {
newPoint(tile, a, rec, bin);
} else if (a.type == 2) {
newLine(tile, a, bin);
} else if (a.type == 3) {
newPolygon(tile, a, bin);
} else print("Unknown record", a);
g.flip();
}
function drawVector(gjson, tile, qual) {
var d = gjson; var d = gjson;
points = 0; points = 0;
var t1 = getTime(); var t1 = getTime();
for (var a of d.features) { let xy1 = m.latLonToXY(tile[1], tile[0]);
if (a.type != "Feature") let xy2 = m.latLonToXY(tile[3], tile[2]);
print("Expecting feature"); let t2 = [ xy1.x, xy1.y, xy2.x, xy2.y ];
print(t2);
for (var a of d) { // d.features for geojson
g.setColor(0,0,0); g.setColor(0,0,0);
if (a.type != "Feature") {
newVector(t2, a);
continue;
}
// marker-size, marker-color, stroke // marker-size, marker-color, stroke
if (qual < 32 && a.geometry.type == "Point") if (qual < 32 && a.geometry.type == "Point")
drawPoint(a); drawPoint(a);
if (qual < 8 && a.geometry.type == "LineString") if (qual < 8 && a.geometry.type == "LineString")
drawLine(a, qual); drawLine(a, qual);
if (qual < 8 && a.geometry.type == "Polygon")
drawPolygon(a, qual);
} }
print("....", points, "painted in", getTime()-t1, "sec"); print("....", points, "painted in", getTime()-t1, "sec");
} }
function fname(lon, lat, zoom) { function fname(lon, lat, zoom) {
var bbox = [lon, lat, lon, lat]; var bbox = [lon, lat, lon, lat];
var r = xyz(bbox, 13, false, "WGS84"); var r = xyz(bbox, 13, false, "WGS84");
//console.log('fname', r); //console.log('fname', r);
return 'z'+zoom+'-'+r.minX+'-'+r.minY+'.json'; return 'z'+zoom+'-'+r.minX+'-'+r.minY+'.json';
} }
function fnames(zoom) { function fnames(zoom) {
var bb = [m.lon, m.lat, m.lon, m.lat]; var bb = [m.lon, m.lat, m.lon, m.lat];
var r = xyz(bb, zoom, false, "WGS84"); var r = xyz(bb, zoom, false, "WGS84");
let maxt = 16;
while (1) { while (1) {
var bb2 = bbox(r.minX, r.minY, zoom, false, "WGS84"); var bb2 = bbox(r.minX, r.minY, zoom, false, "WGS84");
var os = m.latLonToXY(bb2[3], bb2[0]); var os = m.latLonToXY(bb2[3], bb2[0]);
@ -525,6 +697,9 @@ function fnames(zoom) {
else if (os.y >= 0) else if (os.y >= 0)
r.minY -= 1; r.minY -= 1;
else break; else break;
if (!maxt)
break;
maxt--;
} }
while (1) { while (1) {
var bb2 = bbox(r.maxX, r.maxY, zoom, false, "WGS84"); var bb2 = bbox(r.maxX, r.maxY, zoom, false, "WGS84");
@ -534,13 +709,16 @@ function fnames(zoom) {
else if (os.y <= g.getHeight()) else if (os.y <= g.getHeight())
r.maxY += 1; r.maxY += 1;
else break; else break;
if (!maxt)
break;
maxt--;
} }
if (!maxt)
print("!!! Too many tiles, not painting some");
print(".. paint range", r); print(".. paint range", r);
return r; return r;
} }
function log2(x) { return Math.log(x) / Math.log(2); } function log2(x) { return Math.log(x) / Math.log(2); }
function getZoom(qual) { function getZoom(qual) {
var z = 16-Math.round(log2(m.scale)); var z = 16-Math.round(log2(m.scale));
z += qual; z += qual;
@ -551,7 +729,6 @@ function getZoom(qual) {
return meta.max_zoom; return meta.max_zoom;
return z; return z;
} }
function drawDebug(text, perc) { function drawDebug(text, perc) {
g.setClipRect(0,0,R.x2,R.y); g.setClipRect(0,0,R.x2,R.y);
g.reset(); g.reset();
@ -564,7 +741,6 @@ function drawDebug(text, perc) {
g.setClipRect(R.x,R.y,R.x2,R.y2); g.setClipRect(R.x,R.y,R.x2,R.y2);
g.flip(); g.flip();
} }
function drawAll(qual) { function drawAll(qual) {
var zoom = getZoom(qual); var zoom = getZoom(qual);
var t1 = getTime(); var t1 = getTime();
@ -583,7 +759,7 @@ function drawAll(qual) {
var n ='z'+zoom+'-'+x+'-'+y+'-'+cnt+'.json'; var n ='z'+zoom+'-'+x+'-'+y+'-'+cnt+'.json';
var gjson = loadVector(n); var gjson = loadVector(n);
if (!gjson) break; if (!gjson) break;
drawVector(gjson, 1); drawVector(gjson, bbox(x, y, zoom, false, "WGS84"), 1);
} }
num++; num++;
drawDebug("Zoom "+zoom+" tiles "+num+"/"+tiles, num/tiles); drawDebug("Zoom "+zoom+" tiles "+num+"/"+tiles, num/tiles);
@ -611,7 +787,7 @@ function introScreen() {
} }
m.scale = 76; m.scale = 76000;
m.lat = 50.001; m.lat = 50.001;
m.lon = 14.759; m.lon = 14.759;

126
apps/spacew/bench.js Normal file
View File

@ -0,0 +1,126 @@
R = Bangle.appRect;
function introScreen() {
g.reset().clearRect(R);
g.setColor(0,0,0).setFont("Vector",25);
g.setFontAlign(0,0);
g.drawString("Benchmark", 85,35);
g.setColor(0,0,0).setFont("Vector",18);
g.drawString("Press button", 85,55);
}
function lineBench() {
/* 500 lines a second on hardware, 125 lines with flip */
for (let i=0; i<1000; i++) {
let x1 = Math.random() * 160;
let y1 = Math.random() * 160;
let x2 = Math.random() * 160;
let y2 = Math.random() * 160;
g.drawLine(x1, y1, x2, y2);
//g.flip();
}
}
function polyBench() {
/* 275 hollow polygons a second on hardware, 99 with flip */
/* 261 filled polygons a second on hardware, 99 with flip */
for (let i=0; i<1000; i++) {
let x1 = Math.random() * 160;
let y1 = Math.random() * 160;
let x2 = Math.random() * 160;
let y2 = Math.random() * 160;
let c = Math.random();
g.setColor(c, c, c);
g.fillPoly([80, x1, y1, 80, 80, x2, y2, 80], true);
//g.flip();
}
}
function checksum(d) {
let sum = 0;
for (i=0; i<d.length; i++) {
sum += (d[i]*1);
}
return sum;
}
function linearRead() {
/* 10000b block -> 8.3MB/sec, 781..877 IOPS
1000b block -> 920K/sec, 909 IOPS, 0.55 sec
100b block -> 100K/sec
10b block -> 10K/sec, 1020 IOPS, 914 IOPS with ops counting
1000b block backwards -- 0.59 sec.
100b block -- 5.93.
backwards -- 6.27
random -- 7.13
checksum 5.97 -> 351 seconds with checksum. 1400bytes/second
*/
let size = 500000;
let block = 100;
let i = 0;
let ops = 0;
let sum = 0;
while (i < size) {
//let pos = Math.random() * size;
let pos = i;
//let pos = size-i;
let d = require("Storage").read("delme.mtar", pos, block);
//sum += checksum(E.toUint8Array(d));
i += block;
ops ++;
}
print(ops, "ops", sum);
}
function drawBench(name) {
g.setColor(0,0,0).setFont("Vector",25);
g.setFontAlign(0,0);
g.drawString(name, 85,35);
g.setColor(0,0,0).setFont("Vector",18);
g.drawString("Running", 85,55);
g.flip();
}
function runBench(b, name) {
drawBench(name);
g.reset().clearRect(R);
let t1 = getTime();
print("--------------------------------------------------");
print("Running",name);
b();
let m = (getTime()-t1) + " sec";
print("..done in", m);
drawBench(name);
g.setColor(0,0,0).setFont("Vector",18);
g.drawString(m, 85,85);
}
function redraw() {
//runBench(lineBench, "Lines");
runBench(polyBench, "Polygons");
//runBench(linearRead, "Linear read");
}
function showMap() {
g.reset().clearRect(R);
redraw();
emptyMap();
}
function emptyMap() {
Bangle.setUI({mode:"custom",drag:e=>{
g.reset().clearRect(R);
redraw();
}, btn: btn=>{
mapVisible = false;
var menu = {"":{title:"Benchmark"},
"< Back": ()=> showMap(),
/*LANG*/"Run": () =>{
showMap();
}};
E.showMenu(menu);
}});
}
const st = require('Storage');
const hs = require('heatshrink');
introScreen();
emptyMap();

View File

@ -1,6 +1,6 @@
{ "id": "spacew", { "id": "spacew",
"name": "Space Weaver", "name": "Space Weaver",
"version":"0.01", "version":"0.02",
"description": "Application for displaying vector maps", "description": "Application for displaying vector maps",
"icon": "app.png", "icon": "app.png",
"readme": "README.md", "readme": "README.md",
@ -8,6 +8,7 @@
"tags": "outdoors,gps,osm", "tags": "outdoors,gps,osm",
"storage": [ "storage": [
{"name":"spacew.app.js","url":"app.js"}, {"name":"spacew.app.js","url":"app.js"},
{"name":"spacew.img","url":"app-icon.js","evaluate":true} {"name":"spacew.img","url":"app-icon.js","evaluate":true},
{"name":"world.mtar","url":"world.mtar"}
] ]
} }

View File

@ -1,7 +1,8 @@
#!/usr/bin/nodejs #!/usr/bin/nodejs
// https://stackoverflow.com/questions/49129643/how-do-i-merge-an-array-of-uint8arrays
var pc = 1; var pc = 1;
var hack = 0;
const hs = require('./heatshrink.js'); const hs = require('./heatshrink.js');
if (pc) { if (pc) {
@ -23,19 +24,21 @@ function writeTar(tar, dir) {
var h_len = 16; var h_len = 16;
var cur = h_len; var cur = h_len;
files = fs.readdirSync(dir); files = fs.readdirSync(dir);
data = ''; let data = [];
var directory = ''; var directory = '';
var json = {}; var json = {};
for (f of files) { for (f of files) {
let f_rec = {};
d = fs.readFileSync(dir+f); d = fs.readFileSync(dir+f);
cs = d; if (0) {
//cs = String.fromCharCode.apply(null, hs.compress(d)) cs = hs.compress(d);
f_rec.comp = "hs";
} else
cs = d;
print("Processing", f, cur, d.length, cs.length); print("Processing", f, cur, d.length, cs.length);
//if (d.length == 42) continue; data.push(cs);
data = data + cs;
var f_rec = {};
f_rec.st = cur; f_rec.st = cur;
var len = d.length; var len = cs.length;
f_rec.si = len; f_rec.si = len;
cur = cur + len; cur = cur + len;
json[f] = f_rec; json[f] = f_rec;
@ -53,10 +56,10 @@ function writeTar(tar, dir) {
while (header.length < h_len) { while (header.length < h_len) {
header = header+' '; header = header+' ';
} }
if (!hack) fs.writeFileSync(tar, header);
fs.writeFileSync(tar, header+data+directory); for (d of data)
else fs.appendFileSync(tar, Buffer.from(d));
fs.writeFileSync(tar, directory); fs.appendFileSync(tar, directory);
} }
function readTarFile(tar, f) { function readTarFile(tar, f) {
@ -70,7 +73,7 @@ function readTarFile(tar, f) {
} }
if (pc) if (pc)
writeTar("delme.mtaz", "delme/"); writeTar("delme.mtar", "delme/");
else { else {
print(readTarFile("delme.mtar", "ahoj")); print(readTarFile("delme.mtar", "ahoj"));
print(readTarFile("delme.mtar", "nazdar")); print(readTarFile("delme.mtar", "nazdar"));

View File

@ -14,5 +14,5 @@
"linear_tags": true, "linear_tags": true,
"area_tags": false, "area_tags": false,
"exclude_tags": [], "exclude_tags": [],
"include_tags": [ "place", "name", "landuse", "highway" ] "include_tags": [ "place", "name", "landuse", "highway", "natural" ]
} }

View File

@ -1,17 +1,49 @@
#!/bin/bash #!/bin/bash
if [ ".$1" == "-f" ]; then
# http://bboxfinder.com/#0.000000,0.000000,0.000000,0.000000
Z=
# Czech republic -- hitting internal limit in nodejs
#BBOX=10,60,20,30
#Z="--maxz 9"
# No Moravia -- ascii conversion takes 43min, "Error: Cannot create a string longer than 0x3fffffe7 characters"
#BBOX=10,60,17.75,30
#Z="--maxz 9"
# Just Moravia -- 266MB delme.pbf -- FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed
#BBOX=16.13,60,35,30
#Z="--maxz 9"
# Roudnice az Kutna hora -- band 1.1 deg -- 145MB delme.pbf, 5m cstocs, 40+m split.
# -- band 0.1 deg -- 13MB delme.pbf, 13s split, 21k result.
#BBOX=14.20,50.45,15.32,49.20
#Z="--maxz 9"
# Roudnice az... -- band 0.5 deg -- 91MB delme.pbf, 200k result.
# Roudnice az... -- band 0.8 deg -- 120MB delme.pbf, 2.5GB while splitting, 260k result.
#BBOX=14.20,50.45,15.0,49.20
#Z="--maxz 9"
# Prague; 1.2MB map, not really useful
#BBOX=14.25,50.17,14.61,49.97
#Z="--maxz 14"
# Zernovka small -- 3.5 delme.pbf, ~850K result.
BBOX=14.7,49.9,14.8,50.1
# Zernovka big
#BBOX=14.6,49.7,14.9,50.1
if [ ".$1" == ".-f" ]; then
I=/data/gis/osm/dumps/czech_republic-2023-07-24.osm.pbf I=/data/gis/osm/dumps/czech_republic-2023-07-24.osm.pbf
#I=/data/gis/osm/dumps/zernovka.osm.bz2 #I=/data/gis/osm/dumps/zernovka.osm.bz2
O=cr.geojson O=cr.geojson
rm delme.pbf $O rm delme.pbf $O
time osmium extract $I --bbox 14.7,49.9,14.8,50.1 -f pbf -o delme.pbf ls -alh $I
time osmium extract $I --bbox $BBOX -f pbf -o delme.pbf
ls -alh delme.pbf
time osmium export delme.pbf -c prepare.json -o $O time osmium export delme.pbf -c prepare.json -o $O
ls -alh $O
# ~.5G in 15min
echo "Converting to ascii" echo "Converting to ascii"
time cstocs utf8 ascii cr.geojson > cr_ascii.geojson time cstocs utf8 ascii cr.geojson > cr_ascii.geojson
mv -f cr_ascii.geojson delme.json mv -f cr_ascii.geojson delme.json
fi fi
rm -r delme/; mkdir delme rm -r delme/; mkdir delme
./split.js time ./split.js $Z
./minitar.js ./minitar.js
ls -lS delme/*.json | head -20 ls -lS delme/*.json | head -20
cat delme/* | wc -c cat delme/* | wc -c

View File

@ -6,11 +6,11 @@
const fs = require('fs'); const fs = require('fs');
const sphm = require('./sphericalmercator.js'); const sphm = require('./sphericalmercator.js');
var split = require('geojson-vt') var split = require('geojson-vt');
const process = require('process');
// delme.json needs to be real file, symlink to geojson will not work // delme.json needs to be real file, symlink to geojson will not work
console.log("Loading json");
var gjs = require("./delme.json");
function tileToLatLon(x, y, z, x_, y_) { function tileToLatLon(x, y, z, x_, y_) {
var [ w, s, e, n ] = merc.bbox(x, y, z); var [ w, s, e, n ] = merc.bbox(x, y, z);
@ -30,9 +30,39 @@ function convGeom(tile, geom) {
return g; return g;
} }
function clamp(i) {
if (i<0)
return 0;
if (i>4095)
return 4095;
return i;
}
function binGeom(tile, geom) {
let off = 1;
let r = new Uint8Array(geom.length * 3 + off);
let j = off;
for (i = 0; i< geom.length; i++) {
let x = geom[i][0];
let y = geom[i][1];
x = clamp(x);
y = clamp(y);
r[j++] = x >> 4;
r[j++] = y >> 4;
r[j++] = (x & 0x0f) + ((y & 0x0f) << 4);
}
return r;
}
function zoomPoint(tags) { function zoomPoint(tags) {
var z = 99; var z = 99;
if (tags.featurecla == "Admin-0 scale ranksscalerank") z = 2;
if (tags.featurecla == "Admin-0 capital") z = 3;
if (tags.featurecla == "Admin-1 capital") z = 4;
if (tags.featurecla == "Populated place") z = 5;
if (tags.place == "city") z = 4; if (tags.place == "city") z = 4;
if (tags.place == "town") z = 8; if (tags.place == "town") z = 8;
if (tags.place == "village") z = 10; if (tags.place == "village") z = 10;
@ -40,10 +70,59 @@ function zoomPoint(tags) {
return z; return z;
} }
var meta = {};
var ac = -1;
meta.attrs = [];
var a_town = ++ac;
meta.attrs[ac] = {};
meta.attrs[ac].type = 1;
var a_village = ++ac
meta.attrs[ac] = {};
meta.attrs[ac].type = 1;
meta.attrs[ac].properties = {};
meta.attrs[ac].properties["marker-color"] = "#800000";
var a_way = ++ac;
meta.attrs[ac] = {};
meta.attrs[ac].type = 2;
var a_secondary = ++ac;
meta.attrs[ac] = {};
meta.attrs[ac].type = 2;
meta.attrs[ac].properties = {};
meta.attrs[ac].properties.stroke = "#000040";
var a_tertiary = ++ac;
meta.attrs[ac] = {};
meta.attrs[ac].type = 2;
meta.attrs[ac].properties = {};
meta.attrs[ac].properties.stroke = "#000080";
var a_track = ++ac;
meta.attrs[ac] = {};
meta.attrs[ac].type = 2;
meta.attrs[ac].properties = {};
meta.attrs[ac].properties.stroke = "#404040";
var a_path = ++ac;
meta.attrs[ac] = {};
meta.attrs[ac].type = 2;
meta.attrs[ac].properties = {};
meta.attrs[ac].properties.stroke = "#408040";
var a_polygon = ++ac;
meta.attrs[ac] = {};
meta.attrs[ac].type = 3;
var a_forest = ++ac;
meta.attrs[ac] = {};
meta.attrs[ac].properties = {};
meta.attrs[ac].properties.fill = "#c0ffc0";
meta.attrs[ac].type = 3;
var a_water = ++ac;
meta.attrs[ac] = {};
meta.attrs[ac].properties = {};
meta.attrs[ac].properties.fill = "#c0c0ff";
meta.attrs[ac].type = 3;
function paintPoint(tags) { function paintPoint(tags) {
var p = {}; var p = {};
if (tags.place == "village") p["marker-color"] = "#ff0000"; if (tags.place == "city" || tags.place == "town") { p.attr = a_town; }
if (tags.place == "village") { p.attr = a_village; p["marker-color"] = "#ff0000"; }
return p; return p;
} }
@ -51,15 +130,17 @@ function paintPoint(tags) {
function zoomWay(tags) { function zoomWay(tags) {
var z = 99; var z = 99;
if (tags.scalerank == 0) z = 0;
if (tags.highway == "motorway") z = 7; if (tags.highway == "motorway") z = 7;
if (tags.highway == "primary") z = 9; if (tags.highway == "primary") z = 9;
if (tags.highway == "secondary") z = 13; if (tags.highway == "secondary") z = 13;
if (tags.highway == "tertiary") z = 14; if (tags.highway == "tertiary") z = 14;
if (tags.highway == "unclassified") z = 16; if (tags.highway == "unclassified") z = 15;
if (tags.highway == "residential") z = 17; if (tags.highway == "residential") z = 15;
if (tags.highway == "track") z = 17; if (tags.highway == "track") z = 15;
if (tags.highway == "path") z = 17; if (tags.highway == "path") z = 16;
if (tags.highway == "footway") z = 17; if (tags.highway == "footway") z = 16;
return z; return z;
} }
@ -67,52 +148,120 @@ function zoomWay(tags) {
function paintWay(tags) { function paintWay(tags) {
var p = {}; var p = {};
p.attr = a_way;
if (tags.highway == "motorway" || tags.highway == "primary") /* ok */; if (tags.highway == "motorway" || tags.highway == "primary") /* ok */;
if (tags.highway == "secondary" || tags.highway == "tertiary") p.stroke = "#0000ff"; if (tags.highway == "secondary" || tags.highway == "tertiary") { p.stroke = "#0000ff"; p.attr = a_secondary; }
if (tags.highway == "tertiary" || tags.highway == "unclassified" || tags.highway == "residential") p.stroke = "#00ff00"; if (tags.highway == "tertiary" || tags.highway == "unclassified" || tags.highway == "residential") { p.stroke = "#00ff00"; p.attr = a_tertiary; }
if (tags.highway == "track") p.stroke = "#ff0000"; if (tags.highway == "track") { p.stroke = "#ff0000"; p.attr = a_track; }
if (tags.highway == "path" || tags.highway == "footway") p.stroke = "#800000"; if (tags.highway == "path" || tags.highway == "footway") { p.stroke = "#800000"; p.attr = a_path; }
return p;
}
function zoomPolygon(tags) {
var z = 99;
if (tags.scalerank == 0) z = 0;
if (tags.landuse == "forest") z = 16;
if (tags.natural == "water") z = 16;
return z;
}
function paintPolygon(tags) {
var p = {};
p.attr = a_polygon;
if (tags.landuse == "forest") { p.fill = "#c0ffc0"; p.attr = a_forest; }
if (tags.natural == "water") { p.fill = "#c0c0ff"; p.attr = a_water; }
if (tags.featurecla == "Admin-0 sovereignty") p.attr = a_way;
return p; return p;
} }
function writeFeatures(name, feat) function writeFeatures(name, feat)
{ {
var n = {}; if (0) {
n.type = "FeatureCollection"; var n = {};
n.features = feat; n.type = "FeatureCollection";
n.features = feat;
fs.writeFile(name+'.json', JSON.stringify(n), on_error); fs.writeFile(name+'.json', JSON.stringify(n), on_error);
} else {
if (feat.length > 0)
fs.writeFile(name+'.json', JSON.stringify(feat), on_error);
}
} }
function btoa(s) {
return Buffer.from(s).toString('base64');
}
// E.toString()
function toGjson(name, d, tile) { function toGjson(name, d, tile) {
var cnt = 0; var cnt = 0;
var feat = []; var feat = [];
for (var a of d) { for (var a of d) {
var f = {}; let f = {}; // geojson output
let b = {}; // moving towards binary output
var zoom = 99; var zoom = 99;
var p = {}; var p = {};
var bin = [];
if (!a.tags)
a.tags = a.properties;
f.properties = a.tags; f.properties = a.tags;
f.type = "Feature"; f.type = "Feature";
f.geometry = {}; f.geometry = {};
if (a.type == 1) { if (a.type == 1) {
f.geometry.type = "Point"; f.geometry.type = "Point";
f.geometry.coordinates = convGeom(tile, a.geometry)[0]; f.geometry.coordinates = convGeom(tile, a.geometry)[0];
bin = binGeom(tile, a.geometry);
zoom = zoomPoint(a.tags); zoom = zoomPoint(a.tags);
p = paintPoint(a.tags); p = paintPoint(a.tags);
} else if (a.type == 2) { } else if (a.type == 2) {
f.geometry.type = "LineString"; f.geometry.type = "LineString";
f.geometry.coordinates = convGeom(tile, a.geometry[0]); f.geometry.coordinates = convGeom(tile, a.geometry[0]);
bin = binGeom(tile, a.geometry[0]);
zoom = zoomWay(a.tags); zoom = zoomWay(a.tags);
p = paintWay(a.tags); p = paintWay(a.tags);
if (zoom == 99) {
f.geometry.type = "Polygon";
zoom = zoomPolygon(a.tags);
p = paintPolygon(a.tags);
}
} else if (a.type == 3) {
f.geometry.type = "Polygon";
f.geometry.coordinates = convGeom(tile, a.geometry[0]);
bin = binGeom(tile, a.geometry[0]);
zoom = zoomPolygon(a.tags);
p = paintPolygon(a.tags);
} else { } else {
//console.log("Unknown type", a.type); console.log("Unknown type", a.type);
} }
//zoom -= 4; // Produces way nicer map, at expense of space. //zoom -= 4; // Produces way nicer map, at expense of space.
if (tile.z < zoom) if (tile.z < zoom)
continue; continue;
f.properties = Object.assign({}, f.properties, p); f.properties = Object.assign({}, f.properties, p);
feat.push(f); //feat.push(f); FIXME
bin[0] = p.attr;
b.b = btoa(bin);
b.tags = {};
if (a.tags.name)
b.tags.name = a.tags.name;
if (a.tags.nameascii)
b.tags.name = a.tags.nameascii;
if (a.tags.sr_subunit)
b.tags.name = a.tags.sr_subunit;
//delete(a.tags.highway);
//delete(a.tags.landuse);
//delete(a.tags.natural);
//delete(a.tags.place);
// b.properties = p
feat.push(b);
var s = JSON.stringify(feat); var s = JSON.stringify(feat);
if (s.length > 6000) { if (s.length > 6000) {
console.log("tile too big, splitting", cnt); console.log("tile too big, splitting", cnt);
@ -135,16 +284,41 @@ var merc = new sphm({
}); });
console.log("Splitting data"); console.log("Splitting data");
var meta = {}
meta.min_zoom = 0; meta.min_zoom = 0;
meta.max_zoom = 17; // HERE meta.max_zoom = 16; // HERE
// = 16 ... split3 takes > 30 minutes // = 16 ... split3 takes > 30 minutes
// = 13 ... 2 minutes // = 13 ... 2 minutes
if (process.argv[2] == "-h") {
console.log("help here");
process.exit(0);
}
if (process.argv[2] == "--maxz") {
meta.max_zoom = 1*process.argv[3];
console.log("... max zoom", meta.max_zoom);
}
if (process.argv[2] == "--world") {
console.log("Loading world");
meta.max_zoom = 4;
var g_sovereign = require("./ne_10m_admin_0_sovereignty.json");
var g_labels = require("./ne_10m_admin_0_label_points.json");
var g_places = require("./ne_10m_populated_places_simple.json");
gjs = {}
gjs.type = "FeatureCollection";
//gjs.features = g_sovereign.features + g_labels.features + g_places.features;
gjs.features = g_sovereign.features.concat(g_labels.features).concat(g_places.features);
console.log(gjs);
} else {
console.log("Loading json");
gjs = require("./delme.json");
}
var index = split(gjs, Object.assign({ var index = split(gjs, Object.assign({
maxZoom: meta.max_zoom, maxZoom: meta.max_zoom,
indexMaxZoom: meta.max_zoom, indexMaxZoom: meta.max_zoom,
indexMaxPoints: 0, indexMaxPoints: 0,
tolerance: 30, tolerance: 10,
buffer: 0,
}), {}); }), {});
console.log("Producing output"); console.log("Producing output");

View File

@ -1,8 +1,7 @@
#!/bin/bash #!/bin/bash
zoom() { zoom() {
echo "Zoom $1" VAL=`cat delme/z$1-* | wc -c`
cat delme/z$1-* | wc -c echo "Zoom $1 -- " $[$VAL/1024]
echo "M..k..."
} }
echo "Total data" echo "Total data"
@ -16,7 +15,17 @@ zoom 14
zoom 13 zoom 13
zoom 12 zoom 12
zoom 11 zoom 11
zoom 10 zoom 9
zoom 8
zoom 7
zoom 6
zoom 5
zoom 4
zoom 3
zoom 2
zoom 1
zoom 0
echo "Zoom 1..9" echo "Zoom 1..9"
cat delme/z?-* | wc -c cat delme/z?-* | wc -c
echo "M..k..." echo "M..k..."

14
apps/spacew/prep/world.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
if [ ".$1" == ".-f" ]; then
wget https://raw.githubusercontent.com/martynafford/natural-earth-geojson/master/10m/cultural/ne_10m_admin_0_sovereignty.json
wget https://raw.githubusercontent.com/martynafford/natural-earth-geojson/master/10m/cultural/ne_10m_admin_0_label_points.json
wget https://raw.githubusercontent.com/martynafford/natural-earth-geojson/master/10m/cultural/ne_10m_populated_places_simple.json
fi
rm delme.json
rm -r delme/; mkdir delme
./split.js --world
./minitar.js
ls -lS delme/*.json | head -20
cat delme/* | wc -c
ls -l delme.mtar

1411
apps/spacew/world.mtar Normal file

File diff suppressed because one or more lines are too long

1
apps/widhr/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: First release

10
apps/widhr/README.md Normal file
View File

@ -0,0 +1,10 @@
# Last announced heartrate BPM Widget
* Displays the last announced bpm measurement from Bangle.on('HRM', ...);
* it does not enable the heartrate sensor to do measurements it waits on such annoucement. I use it to view the last read value when letting the system take a reading every 10 minutes.
* saves last read value to a file so it can display that last value between app starts / reboots
![](screenshot_widhr.png)
Code based on Lato Pedometer Written by: [Hugh Barney](https://github.com/hughbarney)
Code Modified by: [Willems Davy](https://github.com/joyrider3774)

19
apps/widhr/metadata.json Normal file
View File

@ -0,0 +1,19 @@
{
"id": "widhr",
"name": "Last announced heartrate BPM Widget",
"shortName":"Last Heartrate BPM",
"icon": "widhr.icon.png",
"screenshots": [{"url":"screenshot_widhr.png"}],
"version":"0.01",
"type": "widget",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"description": "Displays the last announced heartrate BPM from `Bangle.on(\"HRM\", ...);` (it does not enable the hrm sensor it expects the system or other app todo so)",
"tags": "widget",
"data": [
{"name":"widhr.data.json"}
],
"storage": [
{"name":"widhr.wid.js","url":"widhr.wid.js"}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
apps/widhr/widhr.icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

30
apps/widhr/widhr.wid.js Normal file
View File

@ -0,0 +1,30 @@
function widhr_hrm(hrm) {
require("Storage").writeJSON("widhr.data.json", {"bpm":hrm.bpm});
WIDGETS["widhr"].draw();
}
Bangle.on('HRM', widhr_hrm);
function widhr_draw() {
var json = require("Storage").readJSON("widhr.data.json");
var bpm = json === undefined ? 0 : json.bpm;
//3x6 from bpm text in 6x8 font
var w = (bpm.toString().length)*8 > 3 * 6 ? (bpm.toString().length)*8 : 3 * 6;
if (w != this.width)
{
this.width = w;
setTimeout(() => Bangle.drawWidgets(),10); return;
}
g.reset();
g.setColor(g.theme.bg);
g.fillRect(this.x, this.y, this.x + this.width, this.y + 23); // erase background
g.setColor(g.theme.fg);
g.setFont("6x8:1");
g.setFontAlign(-1, 0);
g.drawString("bpm", this.x, this.y + 5);
g.setFont("4x6:2");
g.setFontAlign(-1, 0);
g.drawString(bpm, this.x, this.y + 17);
}
WIDGETS["widhr"]={area:"tl",sortorder:-1,width:13,draw:widhr_draw};

2
core

@ -1 +1 @@
Subproject commit 431a3fb743da5c370729ab748cb2c177e70a345b Subproject commit 37a22e0c49666ec3947ad0daaf5f5675101ca485