Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 34 additions & 71 deletions served.html
Original file line number Diff line number Diff line change
Expand Up @@ -2509,79 +2509,42 @@ <h3>Understanding the Graph Behavior</h3>
const declination = 23.45 * Math.sin((360/365) * (dayOfYear - 81) * Math.PI / 180);
const declinationRad = declination * Math.PI / 180;

// Draw gray line as a band (civil twilight: solar altitude between -6° and +6°)
const grayLinePoints = [];

// Sweep across all longitudes and latitudes to find twilight zone
for (let lon = -180; lon <= 180; lon += 2) {
for (let lat = -90; lat <= 90; lat += 2) {
const latRad = lat * Math.PI / 180;

// Calculate hour angle from longitude and time
const solarLon = -15 * (hours - 12); // Sun's longitude at this time
const hourAngle = (lon - solarLon) * Math.PI / 180;

// Calculate solar altitude using spherical astronomy
const sinAlt = Math.sin(latRad) * Math.sin(declinationRad) +
Math.cos(latRad) * Math.cos(declinationRad) * Math.cos(hourAngle);
const solarAlt = Math.asin(Math.max(-1, Math.min(1, sinAlt))) * 180 / Math.PI;

// Gray line is where solar altitude is near 0 (±6 degrees for civil twilight)
if (Math.abs(solarAlt) < 6) {
grayLinePoints.push([lat, lon]);
}
}
// Draw gray line as a band (civil twilight: solar altitude between -6° and +6°).
// Solve sin(α) = sinDecl·sin(lat) + cosDecl·cos(HA)·cos(lat)
// = R·sin(lat + φ)
// for α = ±6° at every longitude, where
// R = √(sin²δ + cos²δ·cos²HA), φ = atan2(cosδ·cosHA, sinδ).
// Then lat = asin(sin(α)/R) − φ, clamped to [−90°, 90°].
const sinDecl = Math.sin(declinationRad);
const cosDecl = Math.cos(declinationRad);
const solarLon = -15 * (hours - 12);
const sinAltDay = Math.sin(6 * Math.PI / 180);
const sinAltNight = Math.sin(-6 * Math.PI / 180);

const dayEdge = [];
const nightEdge = [];
for (let lon = -180; lon <= 180; lon += 1) {
const hourAngle = (lon - solarLon) * Math.PI / 180;
const cosHA = Math.cos(hourAngle);
const R = Math.sqrt(sinDecl * sinDecl + cosDecl * cosDecl * cosHA * cosHA);
const phi = Math.atan2(cosDecl * cosHA, sinDecl);
const argDay = Math.max(-1, Math.min(1, sinAltDay / R));
const argNight = Math.max(-1, Math.min(1, sinAltNight / R));
const latDay = (Math.asin(argDay) - phi) * 180 / Math.PI;
const latNight = (Math.asin(argNight) - phi) * 180 / Math.PI;
dayEdge.push([Math.max(-90, Math.min(90, latDay)), lon]);
nightEdge.push([Math.max(-90, Math.min(90, latNight)), lon]);
}

if (grayLinePoints.length > 0) {
// Create a feature group for better visualization
const features = [];

// Group nearby points to create a continuous band
const grouped = {};
grayLinePoints.forEach(([lat, lon]) => {
const latKey = Math.round(lat / 5) * 5; // Group by 5-degree latitude bands
if (!grouped[latKey]) grouped[latKey] = [];
grouped[latKey].push(lon);
});

// For each latitude band, create a polygon strip
const polygons = [];
for (const [latKey, lons] of Object.entries(grouped)) {
const lat = parseFloat(latKey);
lons.sort((a, b) => a - b);

// Create rectangles for continuous longitude segments
let segmentStart = lons[0];
for (let i = 1; i < lons.length; i++) {
if (lons[i] - lons[i-1] > 10) { // Gap detected
// Close current segment
polygons.push([
[lat - 5, segmentStart],
[lat + 5, segmentStart],
[lat + 5, lons[i-1]],
[lat - 5, lons[i-1]]
]);
segmentStart = lons[i];
}
}
// Close final segment
polygons.push([
[lat - 5, segmentStart],
[lat + 5, segmentStart],
[lat + 5, lons[lons.length - 1]],
[lat - 5, lons[lons.length - 1]]
]);
}

grayLineLayer = L.polygon(polygons, {
color: '#fbbf24',
fillColor: '#fbbf24',
weight: 1,
opacity: 0.5,
fillOpacity: 0.3
}).addTo(map);
}
// Closed polygon: day edge west→east, then night edge east→west.
const polygon = dayEdge.concat(nightEdge.reverse());
grayLineLayer = L.polygon(polygon, {
color: '#fbbf24',
fillColor: '#fbbf24',
weight: 1,
opacity: 0.5,
fillOpacity: 0.3
}).addTo(map);

// Update time display
if (grayLineHour !== null) {
Expand Down
105 changes: 34 additions & 71 deletions src/dvoacap/dashboard/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -2509,79 +2509,42 @@ <h3>Understanding the Graph Behavior</h3>
const declination = 23.45 * Math.sin((360/365) * (dayOfYear - 81) * Math.PI / 180);
const declinationRad = declination * Math.PI / 180;

// Draw gray line as a band (civil twilight: solar altitude between -6° and +6°)
const grayLinePoints = [];

// Sweep across all longitudes and latitudes to find twilight zone
for (let lon = -180; lon <= 180; lon += 2) {
for (let lat = -90; lat <= 90; lat += 2) {
const latRad = lat * Math.PI / 180;

// Calculate hour angle from longitude and time
const solarLon = -15 * (hours - 12); // Sun's longitude at this time
const hourAngle = (lon - solarLon) * Math.PI / 180;

// Calculate solar altitude using spherical astronomy
const sinAlt = Math.sin(latRad) * Math.sin(declinationRad) +
Math.cos(latRad) * Math.cos(declinationRad) * Math.cos(hourAngle);
const solarAlt = Math.asin(Math.max(-1, Math.min(1, sinAlt))) * 180 / Math.PI;

// Gray line is where solar altitude is near 0 (±6 degrees for civil twilight)
if (Math.abs(solarAlt) < 6) {
grayLinePoints.push([lat, lon]);
}
}
// Draw gray line as a band (civil twilight: solar altitude between -6° and +6°).
// Solve sin(α) = sinDecl·sin(lat) + cosDecl·cos(HA)·cos(lat)
// = R·sin(lat + φ)
// for α = ±6° at every longitude, where
// R = √(sin²δ + cos²δ·cos²HA), φ = atan2(cosδ·cosHA, sinδ).
// Then lat = asin(sin(α)/R) − φ, clamped to [−90°, 90°].
const sinDecl = Math.sin(declinationRad);
const cosDecl = Math.cos(declinationRad);
const solarLon = -15 * (hours - 12);
const sinAltDay = Math.sin(6 * Math.PI / 180);
const sinAltNight = Math.sin(-6 * Math.PI / 180);

const dayEdge = [];
const nightEdge = [];
for (let lon = -180; lon <= 180; lon += 1) {
const hourAngle = (lon - solarLon) * Math.PI / 180;
const cosHA = Math.cos(hourAngle);
const R = Math.sqrt(sinDecl * sinDecl + cosDecl * cosDecl * cosHA * cosHA);
const phi = Math.atan2(cosDecl * cosHA, sinDecl);
const argDay = Math.max(-1, Math.min(1, sinAltDay / R));
const argNight = Math.max(-1, Math.min(1, sinAltNight / R));
const latDay = (Math.asin(argDay) - phi) * 180 / Math.PI;
const latNight = (Math.asin(argNight) - phi) * 180 / Math.PI;
dayEdge.push([Math.max(-90, Math.min(90, latDay)), lon]);
nightEdge.push([Math.max(-90, Math.min(90, latNight)), lon]);
}

if (grayLinePoints.length > 0) {
// Create a feature group for better visualization
const features = [];

// Group nearby points to create a continuous band
const grouped = {};
grayLinePoints.forEach(([lat, lon]) => {
const latKey = Math.round(lat / 5) * 5; // Group by 5-degree latitude bands
if (!grouped[latKey]) grouped[latKey] = [];
grouped[latKey].push(lon);
});

// For each latitude band, create a polygon strip
const polygons = [];
for (const [latKey, lons] of Object.entries(grouped)) {
const lat = parseFloat(latKey);
lons.sort((a, b) => a - b);

// Create rectangles for continuous longitude segments
let segmentStart = lons[0];
for (let i = 1; i < lons.length; i++) {
if (lons[i] - lons[i-1] > 10) { // Gap detected
// Close current segment
polygons.push([
[lat - 5, segmentStart],
[lat + 5, segmentStart],
[lat + 5, lons[i-1]],
[lat - 5, lons[i-1]]
]);
segmentStart = lons[i];
}
}
// Close final segment
polygons.push([
[lat - 5, segmentStart],
[lat + 5, segmentStart],
[lat + 5, lons[lons.length - 1]],
[lat - 5, lons[lons.length - 1]]
]);
}

grayLineLayer = L.polygon(polygons, {
color: '#fbbf24',
fillColor: '#fbbf24',
weight: 1,
opacity: 0.5,
fillOpacity: 0.3
}).addTo(map);
}
// Closed polygon: day edge west→east, then night edge east→west.
const polygon = dayEdge.concat(nightEdge.reverse());
grayLineLayer = L.polygon(polygon, {
color: '#fbbf24',
fillColor: '#fbbf24',
weight: 1,
opacity: 0.5,
fillOpacity: 0.3
}).addTo(map);

// Update time display
if (grayLineHour !== null) {
Expand Down
Loading