Add per-circuit distance radius support (near: {lat, lon, miles})

Prep for a new circuit that should only show events within 500 miles
of St. Louis, MO — the ID given (OlkMrRfxrN) 404s against the leagues
endpoint (every other circuit ID is 12 chars, this one is 10, so it's
likely missing characters from a copy/paste), so adding the actual
circuit entry once that's confirmed. This part is independent and
verified against known distances (Collinsville IL ~10mi, Ashland MO
~111mi, PA ~748mi from St. Louis) — a no-op for every existing circuit
since `near` is optional and withinRadius() passes everything through
when it's unset.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SBzcNkW7JcYipnAX6HfgmK
This commit is contained in:
2026-09-04 16:30:58 -05:00
co-authored by Claude Sonnet 5
parent 83b6a2069d
commit 9337a2a01c
+29 -1
View File
@@ -41,6 +41,12 @@ loadHTML('/footer.html', 'footer');
entries here; the page builds one section per circuit.
`id` is the leagueId from a circuit's BCP calendar URL,
e.g. bestcoastpairings.com/circuit/<id>/calendar
Optional `near: { lat, lon, miles }` restricts a circuit to only
events within that radius (great-circle distance) — a fixed
property of the circuit itself, unlike the Format/Scope toggles
which the viewer controls. Events with no coordinate data are
excluded rather than assumed to be in range.
============================================================ */
const CONFIG = {
circuits: [
@@ -51,6 +57,27 @@ const CONFIG = {
],
};
// St. Louis, MO — reference point for circuits restricted by distance.
const ST_LOUIS = { lat: 38.6270, lon: -90.1994 };
// Great-circle distance in miles between two lat/lon points (haversine).
function milesBetween(lat1, lon1, lat2, lon2) {
const R = 3958.8;
const toRad = d => (d * Math.PI) / 180;
const dLat = toRad(lat2 - lat1);
const dLon = toRad(lon2 - lon1);
const a = Math.sin(dLat / 2) ** 2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
function withinRadius(ev, near) {
if (!near) return true;
const coord = ev.coordinate; // BCP gives [lon, lat]
if (!Array.isArray(coord) || coord.length !== 2) return false;
const [lon, lat] = coord;
return milesBetween(lat, lon, near.lat, near.lon) <= near.miles;
}
/* ---- BCP API ---- */
const API = "https://newprod-api.bestcoastpairings.com/v1";
const HEADERS = { "client-id": "web-app", "env": "bcp", "accept": "application/json" };
@@ -265,7 +292,8 @@ async function load() {
allResults = await Promise.all(
CONFIG.circuits.map(async circuit => {
try {
return { circuit, events: await fetchUpcomingEvents(circuit.id) };
const events = await fetchUpcomingEvents(circuit.id);
return { circuit, events: events.filter(ev => withinRadius(ev, circuit.near)) };
} catch (err) {
console.error("Failed to load circuit", circuit.id, err);
return { circuit, events: [], error: true };