BangleApps/apps/astrocalc/astrocalc-app.js

317 lines
8.7 KiB
JavaScript
Raw Normal View History

/**
* BangleJS ASTROCALC
*
* Inspired by: https://www.timeanddate.com
*
* Original Author: Paul Cockrell https://github.com/paulcockrell
* Created: April 2020
*
* Calculate the Sun and Moon positions based on watch GPS and display graphically
*/
const SunCalc = require("suncalc"); // from modules folder
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",
];
2024-03-13 10:51:40 +00:00
const img = require("Storage").read(`${moonImgFiles[phase]}.img`);
2020-04-02 12:45:27 +00:00
// image width & height = 92px
g.drawImage(img, x - parseInt(92 / 2), y);
}
// 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;
2024-03-04 20:34:50 +00:00
//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;
const faceWidth = g.getWidth()/3; // watch face radius
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);
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);
const moonColor = g.theme.dark ? {r: 1, g: 1, b: 1} : {r: 0, g: 0, b: 0};
const azimuth = pos.azimuth + Math.PI; // 0 is south, we want 0 to be north
const pageData = {
2023-08-19 14:32:44 +00:00
Azimuth: parseInt(azimuth * 180 / Math.PI + 0.5) + '°',
Altitude: parseInt(pos.altitude * 180 / Math.PI + 0.5) + '°',
Distance: `${pos.distance.toFixed(0)} km`,
2023-08-19 14:32:44 +00:00
"Parallactic Ang": parseInt(pos.parallacticAngle * 180 / Math.PI + 0.5) + '°',
};
const azimuthDegrees = parseInt(azimuth * 180 / Math.PI + 0.5);
drawData(title, pageData, null, g.getHeight()/2 - Object.keys(pageData).length/2*20);
drawPoints();
drawPoint(azimuthDegrees, 8, moonColor);
2023-07-27 16:26:56 +00:00
Bangle.setUI({mode: "custom", back: () => moonIndexPageMenu(gps)});
}
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",
];
const phase = SunCalc.getMoonIllumination(new Date());
2022-12-11 21:16:39 +00:00
const phaseIdx = Math.round(phase.phase*8);
const pageData = {
Phase: phaseNames[phaseIdx],
};
drawData(title, pageData, null, 35);
drawMoon(phaseIdx, g.getWidth() / 2, g.getHeight() / 2);
2023-07-27 16:26:56 +00:00
Bangle.setUI({mode: "custom", back: () => moonIndexPageMenu(gps)});
}
function drawMoonTimesPage(gps, title) {
const times = SunCalc.getMoonTimes(new Date(), gps.lat, gps.lon);
const moonColor = g.theme.dark ? {r: 1, g: 1, b: 1} : {r: 0, g: 0, b: 0};
const pageData = {
Rise: times.rise ? dateToTimeString(times.rise) : "Not today",
Set: times.set ? dateToTimeString(times.set) : "Not today",
};
drawData(title, pageData, null, g.getHeight()/2 - Object.keys(pageData).length/2*20 + 5);
drawPoints();
// Draw the moon rise position
const risePos = SunCalc.getMoonPosition(times.rise, gps.lat, gps.lon);
const riseAzimuth = risePos.azimuth + Math.PI; // 0 is south, we want 0 to be north
const riseAzimuthDegrees = parseInt(riseAzimuth * 180 / Math.PI);
drawPoint(riseAzimuthDegrees, 8, moonColor);
// Draw the moon set position
const setPos = SunCalc.getMoonPosition(times.set, gps.lat, gps.lon);
const setAzimuth = setPos.azimuth + Math.PI; // 0 is south, we want 0 to be north
const setAzimuthDegrees = parseInt(setAzimuth * 180 / Math.PI);
drawPoint(setAzimuthDegrees, 8, moonColor);
2023-07-27 16:26:56 +00:00
Bangle.setUI({mode: "custom", back: () => moonIndexPageMenu(gps)});
}
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 = pos.azimuth + Math.PI; // 0 is south, we want 0 to be north
2023-08-19 14:32:44 +00:00
const azimuthDegrees = parseInt(azimuth * 180 / Math.PI + 0.5) + '°';
const altitude = parseInt(pos.altitude * 180 / Math.PI + 0.5) + '°';
const pageData = {
Time: time,
Altitude: altitude,
Azimuth: azimuthDegrees,
};
drawData(key, pageData, null, g.getHeight()/2 - Object.keys(pageData).length/2*20 + 5);
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)});
return null;
}
let m;
function sunIndexPageMenu(gps) {
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());
},
};
Object.keys(sunTimes).sort().reduce((menu, key) => {
const title = titlizeKey(key);
menu[title] = () => {
m = E.showMenu();
drawSunShowPage(gps, key, sunTimes[key]);
};
return menu;
}, sunMenu);
sunMenu["< Back"] = () => m = indexPageMenu(gps);
return E.showMenu(sunMenu);
}
function moonIndexPageMenu(gps) {
const moonMenu = {
"": {
"title": "-- Moon --",
},
"Times": () => {
m = E.showMenu();
2022-12-11 21:36:31 +00:00
drawMoonTimesPage(gps, /*LANG*/"Times");
},
"Position": () => {
m = E.showMenu();
2022-12-11 21:36:31 +00:00
drawMoonPositionPage(gps, /*LANG*/"Position");
},
"Illumination": () => {
m = E.showMenu();
2022-12-11 21:36:31 +00:00
drawMoonIlluminationPage(gps, /*LANG*/"Illumination");
},
"< Back": () => m = indexPageMenu(gps),
};
return E.showMenu(moonMenu);
}
function indexPageMenu(gps) {
const menu = {
"": {
2022-12-11 21:36:31 +00:00
"title": /*LANG*/"Select",
},
2022-12-11 21:36:31 +00:00
/*LANG*/"Sun": () => {
m = sunIndexPageMenu(gps);
},
2022-12-11 21:36:31 +00:00
/*LANG*/"Moon": () => {
m = moonIndexPageMenu(gps);
},
"< Back": () => { load(); }
};
return E.showMenu(menu);
}
//function getCenterStringX(str) {
// return (g.getWidth() - g.stringWidth(str)) / 2;
//}
function init() {
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();
indexPageMenu(location);
2023-07-27 16:26:56 +00:00
Bangle.drawWidgets();
}
init();