2020-04-01 20:14:34 +00:00
|
|
|
/**
|
2020-04-17 23:38:12 +00:00
|
|
|
* BangleJS ASTROCALC
|
|
|
|
*
|
2020-04-01 20:14:34 +00:00
|
|
|
* Inspired by: https://www.timeanddate.com
|
2020-04-17 23:38:12 +00:00
|
|
|
*
|
|
|
|
* Original Author: Paul Cockrell https://github.com/paulcockrell
|
|
|
|
* Created: April 2020
|
|
|
|
*
|
|
|
|
* Calculate the Sun and Moon positions based on watch GPS and display graphically
|
2020-04-01 20:14:34 +00:00
|
|
|
*/
|
|
|
|
|
2022-12-09 09:49:33 +00:00
|
|
|
const SunCalc = require("suncalc"); // from modules folder
|
2020-04-17 23:38:12 +00:00
|
|
|
const storage = require("Storage");
|
2020-04-01 20:14:34 +00:00
|
|
|
|
|
|
|
function drawMoon(phase, x, y) {
|
|
|
|
const moonImgFiles = [
|
2020-04-02 12:45:27 +00:00
|
|
|
"new",
|
|
|
|
"waxing-crescent",
|
|
|
|
"first-quarter",
|
|
|
|
"waxing-gibbous",
|
|
|
|
"full",
|
|
|
|
"waning-gibbous",
|
|
|
|
"last-quarter",
|
|
|
|
"waning-crescent",
|
2020-04-01 20:14:34 +00:00
|
|
|
];
|
|
|
|
|
2020-04-02 12:45:27 +00:00
|
|
|
img = require("Storage").read(`${moonImgFiles[phase]}.img`);
|
|
|
|
// image width & height = 92px
|
|
|
|
g.drawImage(img, x - parseInt(92 / 2), y);
|
2020-04-01 20:14:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// linear interpolation between two values a and b
|
|
|
|
// u controls amount of a/b and is in range [0.0,1.0]
|
|
|
|
function lerp(a,b,u) {
|
|
|
|
return (1-u) * a + u * b;
|
|
|
|
}
|
|
|
|
|
|
|
|
function titlizeKey(key) {
|
|
|
|
return (key[0].toUpperCase() + key.slice(1)).match(/[A-Z][a-z]+/g).join(" ");
|
|
|
|
}
|
|
|
|
|
|
|
|
function dateToTimeString(date) {
|
|
|
|
const hrs = ("0" + date.getHours()).substr(-2);
|
|
|
|
const mins = ("0" + date.getMinutes()).substr(-2);
|
|
|
|
const secs = ("0" + date.getMinutes()).substr(-2);
|
|
|
|
|
|
|
|
return `${hrs}:${mins}:${secs}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawTitle(key) {
|
|
|
|
const fontHeight = 16;
|
|
|
|
const x = 0;
|
|
|
|
const x2 = g.getWidth() - 1;
|
|
|
|
const y = fontHeight + 26;
|
|
|
|
const y2 = g.getHeight() - 1;
|
|
|
|
const title = titlizeKey(key);
|
|
|
|
|
|
|
|
g.setFont("6x8", 2);
|
|
|
|
g.setFontAlign(0,-1);
|
|
|
|
g.drawString(title,(x+x2)/2,y-fontHeight-2);
|
|
|
|
g.drawLine(x,y-2,x2,y-2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @params {Number} angle Angle of point around a radius
|
|
|
|
* @params {Number} radius Radius of the point to be drawn, default 2
|
|
|
|
* @params {Object} color Color of the point
|
|
|
|
* @params {Number} color.r Red 0-1
|
|
|
|
* @params {Number} color.g Green 0-1
|
|
|
|
* @params {Number} color.b Blue 0-1
|
|
|
|
*/
|
|
|
|
function drawPoint(angle, radius, color) {
|
|
|
|
const pRad = Math.PI / 180;
|
2022-12-11 21:09:16 +00:00
|
|
|
const faceWidth = g.getWidth()/3; // watch face radius
|
2020-04-01 20:14:34 +00:00
|
|
|
const centerPx = g.getWidth() / 2;
|
|
|
|
|
|
|
|
const a = angle * pRad;
|
|
|
|
const x = centerPx + Math.sin(a) * faceWidth;
|
|
|
|
const y = centerPx - Math.cos(a) * faceWidth;
|
|
|
|
|
|
|
|
if (!radius) radius = 2;
|
|
|
|
|
|
|
|
g.setColor(color.r, color.g, color.b);
|
|
|
|
g.fillCircle(x, y + 20, radius);
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawPoints() {
|
|
|
|
const startColor = {r: 140, g: 255, b: 255}; // light blue
|
|
|
|
const endColor = {r: 0, g: 0, b: 140}; // dark turquoise
|
|
|
|
|
|
|
|
const steps = 60;
|
|
|
|
const step_u = 1.0 / (steps / 2);
|
|
|
|
let u = 0.0;
|
|
|
|
|
|
|
|
for (let i = 0; i < steps; i++) {
|
|
|
|
const colR = lerp(startColor.r, endColor.r, u) / 255;
|
|
|
|
const colG = lerp(startColor.g, endColor.g, u) / 255;
|
|
|
|
const colB = lerp(startColor.b, endColor.b, u) / 255;
|
|
|
|
const col = {r: colR, g: colG, b: colB};
|
|
|
|
|
|
|
|
if (i >= 0 && i <= 30) {
|
|
|
|
u += step_u;
|
|
|
|
} else {
|
|
|
|
u -= step_u;
|
|
|
|
}
|
|
|
|
|
|
|
|
drawPoint((360 * i) / steps, 2, col);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawData(title, obj, startX, startY) {
|
2023-07-27 16:26:56 +00:00
|
|
|
g.clearRect(Bangle.appRect);
|
2020-04-01 20:14:34 +00:00
|
|
|
drawTitle(title);
|
|
|
|
|
|
|
|
let xPos, yPos;
|
|
|
|
|
|
|
|
if (typeof(startX) === "undefined" || startX === null) {
|
|
|
|
// Center text
|
|
|
|
g.setFontAlign(0,-1);
|
|
|
|
xPos = (0 + g.getWidth() - 2) / 2;
|
|
|
|
} else {
|
|
|
|
xPos = startX;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof(startY) === "undefined") {
|
|
|
|
yPos = 5;
|
|
|
|
} else {
|
|
|
|
yPos = startY;
|
|
|
|
}
|
|
|
|
|
|
|
|
g.setFont("6x8", 1);
|
|
|
|
|
|
|
|
Object.keys(obj).forEach((key) => {
|
|
|
|
g.drawString(`${key}: ${obj[key]}`, xPos, yPos += 20);
|
|
|
|
});
|
|
|
|
|
|
|
|
g.flip();
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawMoonPositionPage(gps, title) {
|
|
|
|
const pos = SunCalc.getMoonPosition(new Date(), gps.lat, gps.lon);
|
2022-12-11 21:09:16 +00:00
|
|
|
const moonColor = g.theme.dark ? {r: 1, g: 1, b: 1} : {r: 0, g: 0, b: 0};
|
2020-04-01 20:14:34 +00:00
|
|
|
|
|
|
|
const pageData = {
|
|
|
|
Azimuth: pos.azimuth.toFixed(2),
|
|
|
|
Altitude: pos.altitude.toFixed(2),
|
|
|
|
Distance: `${pos.distance.toFixed(0)} km`,
|
|
|
|
"Parallactic Ang": pos.parallacticAngle.toFixed(2),
|
|
|
|
};
|
|
|
|
const azimuthDegrees = parseInt(pos.azimuth * 180 / Math.PI);
|
|
|
|
|
2022-12-11 21:09:16 +00:00
|
|
|
drawData(title, pageData, null, g.getHeight()/2 - Object.keys(pageData).length/2*20);
|
2020-04-01 20:14:34 +00:00
|
|
|
drawPoints();
|
2022-12-11 21:09:16 +00:00
|
|
|
drawPoint(azimuthDegrees, 8, moonColor);
|
2020-04-01 20:14:34 +00:00
|
|
|
|
2023-07-27 16:26:56 +00:00
|
|
|
Bangle.setUI({mode: "custom", back: () => moonIndexPageMenu(gps)});
|
2020-04-01 20:14:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function drawMoonIlluminationPage(gps, title) {
|
|
|
|
const phaseNames = [
|
2022-12-11 21:36:31 +00:00
|
|
|
/*LANG*/"New Moon", /*LANG*/"Waxing Crescent", /*LANG*/"First Quarter", /*LANG*/"Waxing Gibbous",
|
|
|
|
/*LANG*/"Full Moon", /*LANG*/"Waning Gibbous", /*LANG*/"Last Quater", /*LANG*/"Waning Crescent",
|
2020-04-01 20:14:34 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
const phase = SunCalc.getMoonIllumination(new Date());
|
2022-12-11 21:16:39 +00:00
|
|
|
const phaseIdx = Math.round(phase.phase*8);
|
2020-04-01 20:14:34 +00:00
|
|
|
const pageData = {
|
2022-12-11 21:09:16 +00:00
|
|
|
Phase: phaseNames[phaseIdx],
|
2020-04-01 20:14:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
drawData(title, pageData, null, 35);
|
2022-12-11 21:09:16 +00:00
|
|
|
drawMoon(phaseIdx, g.getWidth() / 2, g.getHeight() / 2);
|
2020-04-01 20:14:34 +00:00
|
|
|
|
2023-07-27 16:26:56 +00:00
|
|
|
Bangle.setUI({mode: "custom", back: () => moonIndexPageMenu(gps)});
|
2020-04-01 20:14:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function drawMoonTimesPage(gps, title) {
|
|
|
|
const times = SunCalc.getMoonTimes(new Date(), gps.lat, gps.lon);
|
2022-12-11 21:09:16 +00:00
|
|
|
const moonColor = g.theme.dark ? {r: 1, g: 1, b: 1} : {r: 0, g: 0, b: 0};
|
2020-04-01 20:14:34 +00:00
|
|
|
|
|
|
|
const pageData = {
|
|
|
|
Rise: dateToTimeString(times.rise),
|
|
|
|
Set: dateToTimeString(times.set),
|
|
|
|
};
|
|
|
|
|
2022-12-11 21:09:16 +00:00
|
|
|
drawData(title, pageData, null, g.getHeight()/2 - Object.keys(pageData).length/2*20 + 5);
|
2020-04-01 20:14:34 +00:00
|
|
|
drawPoints();
|
|
|
|
|
|
|
|
// Draw the moon rise position
|
|
|
|
const risePos = SunCalc.getMoonPosition(times.rise, gps.lat, gps.lon);
|
|
|
|
const riseAzimuthDegrees = parseInt(risePos.azimuth * 180 / Math.PI);
|
2022-12-11 21:09:16 +00:00
|
|
|
drawPoint(riseAzimuthDegrees, 8, moonColor);
|
2020-04-01 20:14:34 +00:00
|
|
|
|
|
|
|
// Draw the moon set position
|
|
|
|
const setPos = SunCalc.getMoonPosition(times.set, gps.lat, gps.lon);
|
|
|
|
const setAzimuthDegrees = parseInt(setPos.azimuth * 180 / Math.PI);
|
2022-12-11 21:09:16 +00:00
|
|
|
drawPoint(setAzimuthDegrees, 8, moonColor);
|
2020-04-01 20:14:34 +00:00
|
|
|
|
2023-07-27 16:26:56 +00:00
|
|
|
Bangle.setUI({mode: "custom", back: () => moonIndexPageMenu(gps)});
|
2020-04-01 20:14:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function drawSunShowPage(gps, key, date) {
|
|
|
|
const pos = SunCalc.getPosition(date, gps.lat, gps.lon);
|
|
|
|
|
|
|
|
const hrs = ("0" + date.getHours()).substr(-2);
|
|
|
|
const mins = ("0" + date.getMinutes()).substr(-2);
|
|
|
|
const secs = ("0" + date.getMinutes()).substr(-2);
|
|
|
|
const time = `${hrs}:${mins}:${secs}`;
|
|
|
|
|
|
|
|
const azimuth = Number(pos.azimuth.toFixed(2));
|
|
|
|
const azimuthDegrees = parseInt(pos.azimuth * 180 / Math.PI);
|
|
|
|
const altitude = Number(pos.altitude.toFixed(2));
|
|
|
|
|
|
|
|
const pageData = {
|
|
|
|
Time: time,
|
|
|
|
Altitude: altitude,
|
|
|
|
Azimumth: azimuth,
|
|
|
|
Degrees: azimuthDegrees
|
|
|
|
};
|
|
|
|
|
2022-12-11 21:09:16 +00:00
|
|
|
drawData(key, pageData, null, g.getHeight()/2 - Object.keys(pageData).length/2*20 + 5);
|
2020-04-01 20:14:34 +00:00
|
|
|
|
|
|
|
drawPoints();
|
|
|
|
|
|
|
|
// Draw the suns position
|
|
|
|
drawPoint(azimuthDegrees, 8, {r: 1, g: 1, b: 0});
|
|
|
|
|
2023-07-27 16:26:56 +00:00
|
|
|
Bangle.setUI({mode: "custom", back: () => sunIndexPageMenu(gps)});
|
2020-04-01 20:14:34 +00:00
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
function sunIndexPageMenu(gps) {
|
2020-05-23 21:32:33 +00:00
|
|
|
const sunTimes = SunCalc.getTimes(new Date(), gps.lat, gps.lon);
|
|
|
|
|
|
|
|
const sunMenu = {
|
|
|
|
"": {
|
|
|
|
"title": "-- Sun --",
|
|
|
|
},
|
|
|
|
"Current Pos": () => {
|
|
|
|
m = E.showMenu();
|
|
|
|
drawSunShowPage(gps, "Current Pos", new Date());
|
|
|
|
},
|
|
|
|
};
|
2020-04-01 20:14:34 +00:00
|
|
|
|
2020-05-23 21:32:33 +00:00
|
|
|
Object.keys(sunTimes).sort().reduce((menu, key) => {
|
|
|
|
const title = titlizeKey(key);
|
|
|
|
menu[title] = () => {
|
|
|
|
m = E.showMenu();
|
|
|
|
drawSunShowPage(gps, key, sunTimes[key]);
|
|
|
|
};
|
|
|
|
return menu;
|
|
|
|
}, sunMenu);
|
2020-04-01 20:14:34 +00:00
|
|
|
|
2020-05-23 21:32:33 +00:00
|
|
|
sunMenu["< Back"] = () => m = indexPageMenu(gps);
|
2020-04-01 20:14:34 +00:00
|
|
|
|
2020-05-23 21:32:33 +00:00
|
|
|
return E.showMenu(sunMenu);
|
2020-04-01 20:14:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function moonIndexPageMenu(gps) {
|
2020-05-23 21:32:33 +00:00
|
|
|
const moonMenu = {
|
|
|
|
"": {
|
|
|
|
"title": "-- Moon --",
|
|
|
|
},
|
|
|
|
"Times": () => {
|
|
|
|
m = E.showMenu();
|
2022-12-11 21:36:31 +00:00
|
|
|
drawMoonTimesPage(gps, /*LANG*/"Times");
|
2020-05-23 21:32:33 +00:00
|
|
|
},
|
|
|
|
"Position": () => {
|
|
|
|
m = E.showMenu();
|
2022-12-11 21:36:31 +00:00
|
|
|
drawMoonPositionPage(gps, /*LANG*/"Position");
|
2020-05-23 21:32:33 +00:00
|
|
|
},
|
|
|
|
"Illumination": () => {
|
|
|
|
m = E.showMenu();
|
2022-12-11 21:36:31 +00:00
|
|
|
drawMoonIlluminationPage(gps, /*LANG*/"Illumination");
|
2020-05-23 21:32:33 +00:00
|
|
|
},
|
|
|
|
"< Back": () => m = indexPageMenu(gps),
|
|
|
|
};
|
2020-04-01 20:14:34 +00:00
|
|
|
|
2020-05-23 21:32:33 +00:00
|
|
|
return E.showMenu(moonMenu);
|
2020-04-01 20:14:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function indexPageMenu(gps) {
|
|
|
|
const menu = {
|
|
|
|
"": {
|
2022-12-11 21:36:31 +00:00
|
|
|
"title": /*LANG*/"Select",
|
2020-04-01 20:14:34 +00:00
|
|
|
},
|
2022-12-11 21:36:31 +00:00
|
|
|
/*LANG*/"Sun": () => {
|
2020-04-01 20:14:34 +00:00
|
|
|
m = sunIndexPageMenu(gps);
|
|
|
|
},
|
2022-12-11 21:36:31 +00:00
|
|
|
/*LANG*/"Moon": () => {
|
2020-04-01 20:14:34 +00:00
|
|
|
m = moonIndexPageMenu(gps);
|
|
|
|
},
|
2022-12-11 21:09:16 +00:00
|
|
|
"< Back": () => { load(); }
|
2020-04-01 20:14:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
return E.showMenu(menu);
|
|
|
|
}
|
|
|
|
|
2020-04-17 23:38:12 +00:00
|
|
|
function getCenterStringX(str) {
|
|
|
|
return (g.getWidth() - g.stringWidth(str)) / 2;
|
|
|
|
}
|
|
|
|
|
2020-04-01 20:14:34 +00:00
|
|
|
function init() {
|
2022-12-11 21:09:16 +00:00
|
|
|
let location = require("Storage").readJSON("mylocation.json",1)||{"lat":51.5072,"lon":0.1276,"location":"London"};
|
2023-07-27 16:26:56 +00:00
|
|
|
Bangle.loadWidgets();
|
2022-12-11 21:09:16 +00:00
|
|
|
indexPageMenu(location);
|
2023-07-27 16:26:56 +00:00
|
|
|
Bangle.drawWidgets();
|
2020-04-01 20:14:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let m;
|
2022-12-09 09:49:33 +00:00
|
|
|
init();
|