diff --git a/changelog.json b/changelog.json index e4ca134..6334094 100644 --- a/changelog.json +++ b/changelog.json @@ -9,7 +9,8 @@ "Updating selected QSOs from QRZ.com now shows a progress bar with the callsign being queried. On a contest log freshly imported, a hundred contacts mean a hundred network round trips, and until now nothing moved on screen while it worked.", "That progress bar sits in a corner instead of a dialog, so the rest of OpsLog stays usable while thousands of QSOs are looked up. The menu entry is renamed \"Update from the callsign databases\": it has always queried every configured provider, QRZ.com then HamQTH, not QRZ.com alone.", "The world map can show the grey line: the day/night terminator with its twilight band, redrawn every minute. Button at the top right of the map, off by default, and the choice is remembered.", - "The protocol trace checkboxes (CAT and WinKeyer) now show whether the trace is really running. They came back unticked on a trace that was still on, so ticking the box to enable it actually switched it off and the log sent afterwards contained no trace." + "The protocol trace checkboxes (CAT and WinKeyer) now show whether the trace is really running. They came back unticked on a trace that was still on, so ticking the box to enable it actually switched it off and the log sent afterwards contained no trace.", + "Grey line: the shading no longer stops at a straight edge when the map is zoomed out. It is drawn on each repeated copy of the world, not only on the first." ], "fr": [ "Le backend Kenwood est désormais éprouvé face à une radio qui répond : OpsLog dialogue avec l'émulateur TS-2000 qu'il embarque déjà pour les amplis ACOM, ce qui vérifie fréquence, mode, VFO, split et PTT de bout en bout. Un essai sur un vrai Kenwood reste nécessaire, mais le dialogue n'est plus non testé.", @@ -18,7 +19,8 @@ "La mise à jour des QSO sélectionnés depuis QRZ.com affiche désormais une barre de progression avec l'indicatif en cours d'interrogation. Sur un log de concours fraîchement importé, cent contacts font cent allers-retours réseau, et jusqu'ici rien ne bougeait à l'écran pendant ce temps.", "Cette barre de progression s'affiche dans un coin plutôt qu'en fenêtre : le reste d'OpsLog reste utilisable pendant l'interrogation de milliers de QSO. L'entrée de menu devient « Mettre à jour depuis les annuaires » : elle a toujours interrogé tous les annuaires configurés, QRZ.com puis HamQTH, et pas QRZ.com seul.", "La carte du monde peut afficher la ligne grise : le terminateur jour/nuit et sa bande de crépuscule, redessinés chaque minute. Bouton en haut à droite de la carte, désactivé par défaut, et le choix est mémorisé.", - "Les cases de trace du protocole (CAT et WinKeyer) reflètent désormais l'état réel de la trace. Elles réapparaissaient décochées alors que la trace tournait : cocher la case pour l'activer la désactivait en fait, et le journal envoyé ensuite ne contenait aucune trace." + "Les cases de trace du protocole (CAT et WinKeyer) reflètent désormais l'état réel de la trace. Elles réapparaissaient décochées alors que la trace tournait : cocher la case pour l'activer la désactivait en fait, et le journal envoyé ensuite ne contenait aucune trace.", + "Ligne grise : l'ombrage ne s'arrête plus net sur un bord droit quand la carte est dézoomée. Il est tracé sur chaque copie du monde affichée, et non sur la première seulement." ] }, { diff --git a/frontend/src/components/MainMap.tsx b/frontend/src/components/MainMap.tsx index df9e8d1..3fd40c1 100644 --- a/frontend/src/components/MainMap.tsx +++ b/frontend/src/components/MainMap.tsx @@ -190,22 +190,40 @@ export function WorldMap({ fromGrid, toGrid, fromLabel, toLabel, beamAzimuths, b greylineLayer.current?.remove(); const g = L.layerGroup([], { pane: 'greyline' }); + // The rings span −180…+180 once, but this map repeats the world sideways + // (worldCopyJump, and zoomed out several copies are on screen at once). A + // single ring therefore ends in a hard vertical edge wherever a copy + // begins — the shading looked like a rectangle laid over the planet. + // + // So each ring is drawn again shifted a full turn either way. Three copies + // cover every zoom level this map reaches; they cost nothing, being three + // polygons on a canvas layer. + const COPIES = [-360, 0, 360]; + const shifted = (ring: [number, number][], by: number): [number, number][] => + by === 0 ? ring : ring.map(([lat, lon]) => [lat, lon + by] as [number, number]); + // Two bands, drawn dark-to-light so they read as one gradient into night: // full darkness (Sun below the horizon) and civil twilight (down to −6°), // which is the band operators actually mean by "grey line". - const night = L.polygon(nightPolygon(now, 0), { - pane: 'greyline', stroke: false, fillColor: '#0b1220', fillOpacity: 0.32, interactive: false, - }); - const twilight = L.polygon(nightPolygon(now, -6), { - pane: 'greyline', stroke: false, fillColor: '#0b1220', fillOpacity: 0.22, interactive: false, - }); + const nightRing = nightPolygon(now, 0); + const twilightRing = nightPolygon(now, -6); // The terminator itself, so the line is readable at a glance rather than - // being inferred from where the shading fades. - const line = L.polyline(nightPolygon(now, 0).slice(0, -2), { - pane: 'greyline', color: '#f59e0b', weight: 1, opacity: 0.75, interactive: false, - }); + // being inferred from where the shading fades. Dropping the last two + // points leaves the terminator alone, without the segment that closes the + // polygon along the dark pole. + const lineRing = nightRing.slice(0, -2); - night.addTo(g); twilight.addTo(g); line.addTo(g); + for (const by of COPIES) { + L.polygon(shifted(twilightRing, by), { + pane: 'greyline', stroke: false, fillColor: '#0b1220', fillOpacity: 0.22, interactive: false, + }).addTo(g); + L.polygon(shifted(nightRing, by), { + pane: 'greyline', stroke: false, fillColor: '#0b1220', fillOpacity: 0.32, interactive: false, + }).addTo(g); + L.polyline(shifted(lineRing, by), { + pane: 'greyline', color: '#f59e0b', weight: 1, opacity: 0.75, interactive: false, + }).addTo(g); + } g.addTo(m); greylineLayer.current = g; };