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
4 changes: 2 additions & 2 deletions content/quiet-corners-maps.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,9 @@ Proof stage. The engine builds real PDFs; what is outstanding is the physical ve
- **State road cameras (links only)** — five operators whose public camera lists are read for pins and links only: [Missouri DOT's MoDOT Traveler Information](https://traveler.modot.org/timconfig/feed/desktop/StreamingCams2.json), [Maryland CHART's](https://chart.maryland.gov/DataFeeds/GetCamerasJson), [Delaware DOT's](https://tmc.deldot.gov/json/videocamera.json), [New Mexico DOT's NMRoads](https://servicev5.nmroads.com/RealMapWAR/GetCameraInfo) and the [New Jersey Turnpike Authority's camera list](https://www.njta.gov/travel-resources/camera-list/). Each is read with a plain request carrying this site's own user agent and nothing else. On 2026-10-04 each operator's robots.txt was fetched, and the terms page was read where one is linked: the Turnpike Authority's disclaimer, the State of Maryland's and State of Delaware's privacy notices (linked from CHART and DelDOT) and MoDOT's privacy policy (linked from modot.org; no terms page is linked from traveler.modot.org) contain no ban on this use, and no terms page is linked from NMRoads. West Virginia DOT's WV511 is left out because its disclaimer forbids republishing its material. A pin shows the camera's name, the operator's credit, a "View on" link to the operator's public camera page (or its camera map), and no picture; the site keeps no picture or stream address for them, the usage terms are unverified, and the page's security policy lists none of these hosts. A pin outside its own state, or within 30 m of another pin from the same operator, is dropped (an in-service pin is kept over an out-of-service one). Like the National Park Service list, these lists are snapshots this site builds ahead of time (one file per state, committed here and fetched again only by a `--refresh-seeds` run, so a normal site build never asks these operators). The map reads them through the Camera links switch, area by area; opening one shows the neutral card (name, credit and the owner's link) without loading a picture.
- **PhenoCam Network research cameras** — [camera list](https://phenocam.nau.edu/api/cameras/), a screen-only surface credited "PhenoCam Network (CC BY)". Only a `--refresh-seeds` run reads the list (a normal site build serves the committed file and asks the network nothing); it keeps the United States cameras the network marks active and that sent a picture in the previous 30 days, as one snapshot file, which the map reads through the Research cameras (PhenoCam) switch. A camera's card links to the network's own page for the camera and its picture is the network's latest image, hotlinked from `phenocam.nau.edu`, never stored here. The list's contact names and addresses are never copied. A failed refresh keeps the last good file.
- **AviationWX airport cameras** — [airport list](https://api.aviationwx.org/v1/airports), a screen-only surface credited with the service's own line, "Weather data from AviationWX.org". Only a `--refresh-seeds` run reads the airport list (never a picture; a normal site build asks the service nothing), and it keeps one pin per webcam at each listed United States airport, as one snapshot file, which the map reads through the Airport cameras (AviationWX) switch. A pin's picture is the service's 720-pixel image, hotlinked from `api.aviationwx.org`, never stored, and its card links to the airport's own AviationWX page. A failed refresh keeps the last good file.
- **USGS VolcView and AVO volcano cameras** — [VolcView ashcam list](https://volcview.wr.usgs.gov/ashcam-api/webcamApi/webcams) with the [Alaska Volcano Observatory list](https://avo.alaska.edu/ashcam-api/webcamApi/webcams), a screen-only surface credited per camera to its owner ("Alaska Volcano Observatory", "USGS Hawaiian Volcano Observatory", "FAA weather cameras", "National Park Service", "USGS Volcano Hazards Program", each "via USGS VolcView" except "USGS Volcano Hazards Program"). Only a `--refresh-seeds` run reads the two lists (a normal build asks USGS and AVO nothing), with a plain GET that sends the build's own User-Agent, never a browser's, an Origin or a Referer, with a pause between requests. The AVO list names each camera's `organization`: a known one (AVO, HVO, YVO, FAA) is credited, any other named owner (UAF, DGGS, WSDOT, private) drops the camera; a camera it does not name is credited by the page it links to, and one that links to a private or state owner (a ski resort, WSDOT) is dropped. A camera is kept once per `webcamCode` (the AVO list is a subset of VolcView's: 396 of 460 on 2026-10-05, none only on AVO), and one with the same name within 50 m of another counts as the same camera. A camera with no picture, no position (the feed uses 0,0), a position outside US territory (the 50 states, DC, Alaska west of the dateline and the territories), or a newest picture over 24 hours old (or stamped more than 10 minutes in the future) is dropped, and every kept still is also checked with a HEAD request, at most 4 at a time (404, 403, a non-image or a Last-Modified over 24 hours drops it). Rights are `unverified`, credit plus a link to the camera's VolcView page. [USGS copyrights and credits](https://www.usgs.gov/information-policies-and-instructions/copyrights-and-credits) (read 2026-10-05): "USGS-authored or produced data and information are considered to be in the U.S. Public Domain. While the content of most USGS websites is in the U.S. Public Domain, not all information, illustrations, or photographs on our site are. Some non-USGS photographs, images, and/or graphics that appear on USGS websites are used by the USGS with permission from the copyright holder." The VolcView host publishes no robots.txt (404) and answers its API with 200. avo.alaska.edu has no terms or disclaimer page (its about page carries no copyright text and /disclaimer is 404) and its robots.txt is an HTML 200 page with no rules, so decision D1 applies to its credit: rights `unverified`, credit and link to the owner; its list is read for `organization` only. Pictures are hotlinked from `volcview.wr.usgs.gov`, never stored; the cameras show today under the **Research cameras (PhenoCam) switch**, which asks no host for anything when turned on; opening a pin loads its picture from `volcview.wr.usgs.gov` (`avo.alaska.edu` is asked only by the build, for its list). Cameras west of the dateline are written to their own files, the western Aleutians to `volcview/ak-west.json` and any in Guam or the Northern Mariana Islands to `volcview/gu-mp.json`, so no index file box crosses the antimeridian. In the Alaska box, east of -141, a point is kept only southwest of the panhandle border (Atlin, Stewart, Mount Edziza and Dease Lake, in British Columbia, are dropped). A failed refresh keeps the last good file.
- **USGS VolcView and AVO volcano cameras** — [VolcView ashcam list](https://volcview.wr.usgs.gov/ashcam-api/webcamApi/webcams) with the [Alaska Volcano Observatory list](https://avo.alaska.edu/ashcam-api/webcamApi/webcams), a screen-only surface credited per camera to its owner ("Alaska Volcano Observatory", "USGS Hawaiian Volcano Observatory", "FAA weather cameras", "National Park Service", "USGS Volcano Hazards Program", each "via USGS VolcView" except "USGS Volcano Hazards Program"). Only a `--refresh-seeds` run reads the two lists (a normal build asks USGS and AVO nothing), with a plain GET that sends the build's own User-Agent, never a browser's, an Origin or a Referer, with a pause between requests. The AVO list names each camera's `organization`: a known one (AVO, HVO, YVO, FAA) is credited, any other named owner (UAF, DGGS, WSDOT, private) drops the camera; a camera it does not name is credited by the page it links to, and one that links to a private or state owner (a ski resort, WSDOT) is dropped. A camera is kept once per `webcamCode` (the AVO list is a subset of VolcView's: 396 of 460 on 2026-10-05, none only on AVO), and one with the same name within 50 m of another counts as the same camera. A camera with no picture, no position (the feed uses 0,0), a position outside US territory (the 50 states, DC, Alaska west of the dateline and the territories), or a newest picture over 24 hours old (or stamped more than 10 minutes in the future) is dropped, and every kept still is also checked with a HEAD request, at most 4 at a time (404, 403, a non-image or a Last-Modified over 24 hours drops it). Rights are `unverified`, credit plus a link to the camera's VolcView page. [USGS copyrights and credits](https://www.usgs.gov/information-policies-and-instructions/copyrights-and-credits) (read 2026-10-05): "USGS-authored or produced data and information are considered to be in the U.S. Public Domain. While the content of most USGS websites is in the U.S. Public Domain, not all information, illustrations, or photographs on our site are. Some non-USGS photographs, images, and/or graphics that appear on USGS websites are used by the USGS with permission from the copyright holder." The VolcView host publishes no robots.txt (404) and answers its API with 200. avo.alaska.edu has no terms or disclaimer page (its about page carries no copyright text and /disclaimer is 404) and its robots.txt is an HTML 200 page with no rules, so decision D1 applies to its credit: rights `unverified`, credit and link to the owner; its list is read for `organization` only. Pictures are hotlinked from `volcview.wr.usgs.gov`, never stored; the cameras show today under the **Research cameras (PhenoCam) switch**, which asks no host for anything when turned on; opening a pin loads its picture from `volcview.wr.usgs.gov` (`avo.alaska.edu` is asked only by the build, for its list). Cameras west of the dateline are written to their own files, the western Aleutians to `volcview/ak-west.json` and any in Guam or the Northern Mariana Islands to `volcview/gu-mp.json`, so no index file box crosses the antimeridian. In the Alaska box, east of -141, a point is kept only inside the panhandle border, which closes to the south along the Dixon Entrance line of the 1903 Alaska Boundary Tribunal award (Cape Muzon, USGS GNIS, to the south end of the Portland Channel boundary, International Boundary Commission digital boundary v1.3, read 2026-10-09), so everything south of that line is treated as Canada: Atlin, Stewart, Mount Edziza, Dease Lake, Prince Rupert, Masset and Haida Gwaii (British Columbia) are dropped, and Ketchikan, Metlakatla, Hyder, Craig, Sitka and Yakutat stay. Canada reads the line as the maritime boundary and the US as only dividing the land; this build uses it as the cut-off either way. The rough Washington box is likewise cut along the Strait of Juan de Fuca, Haro Strait and Boundary Pass boundary, so Victoria, Saanich, Sidney, Duncan and the Gulf Islands (British Columbia) are dropped while Port Angeles, Neah Bay, Friday Harbor and Point Roberts stay. A failed refresh keeps the last good file.
- **NOAA GLERL Great Lakes cameras** — [camera page](https://www.glerl.noaa.gov/metdata/), a screen-only surface credited "NOAA Great Lakes Environmental Research Laboratory (GLERL)". Only a `--refresh-seeds` run reads the page and HEADs each still (4 at a time, with a pause; a plain GET and HEAD with the build's own User-Agent), keeping a camera whose picture is under 24 hours old (the Muskegon cameras last updated in 2013 drop out). The station list and positions were copied from GLERL's /metdata/ map on 2026-10-05 and are not re-read on a refresh, so a station GLERL adds later is skipped until the table is updated. Rights are `unverified`, credit plus a link to the station's page. [GLERL's site notice](https://www.glerl.noaa.gov/home/notice.html) (read 2026-10-05): "Information presented on these pages is considered public information and may be distributed or copied. Use of appropriate byline/photo/image credits is requested." robots.txt disallows only `/incoming/` and a few file types, not `/metdata/`. Pictures are hotlinked from `www.glerl.noaa.gov`, never stored; the cameras show today under the **Research cameras (PhenoCam) switch**, which asks no host for anything when turned on, and opening a pin loads its picture from `www.glerl.noaa.gov`.
- **NOAA NDBC buoy cameras — not included.** The station list (`activestations.xml`) answers, but the picture host `www.ndbc.noaa.gov/buoycam` answers 403 from its CDN to this build's honest User-Agent. The build does not imitate a browser, so NDBC buoy cameras are dropped until NOAA allows the build agent or a public API (a follow-up).
- **NOAA NDBC buoy cameras — not included.** The station list (`activestations.xml`) answers, but the picture host `www.ndbc.noaa.gov/buoycam` answers 403 from its CDN to this build's honest User-Agent. The build does not imitate a browser, so NDBC buoy cameras are dropped until NOAA allows the build agent or a public API (a follow-up). A build test fails if any NDBC provider, source, origin or picture host is ever published.

Every source a set is built from is credited by name on its printed map key, derived from what the build actually consumed rather than written by hand.

Expand Down
37 changes: 29 additions & 8 deletions scripts/qcm/build-camera-snapshot.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -944,19 +944,40 @@ export const PROVIDERS = Object.freeze([
// (the Aleutians, lon 172 to 180) and the territories (PR, VI, GU, AS, MP), as the unit spec counts them.
const VOLCVIEW_EXTRA_BOXES = Object.freeze([[51.0, 53.6, 172.0, 180.0], ...['pr', 'vi', 'gu', 'as', 'mp'].map((c) => STATE_BBOX[c])]);
// The rough Alaska box spills into British Columbia and Yukon (east of -141) and the rough Washington
// box into British Columbia (north of 49.0); those corners are not US territory. East of -141 a point
// in the Alaska box stays only southwest of the panhandle border (a polyline, closed to the south).
const PANHANDLE_BORDER = [[-141, 60.3], [-135.5, 59.8], [-133.4, 58.9], [-130.0, 56.1], [-130.0, 55.0], [-130.6, 54.7], [-130.6, 40], [-141, 40]];
function insidePanhandle(lat, lon) {
// box into British Columbia (north of 49.0) and onto southern Vancouver Island; those corners are not
// US territory. East of -141 a point in the Alaska box stays only inside the panhandle polygon below,
// and everything south of the polygon (Prince Rupert, Haida Gwaii) is Canada.
//
// Sources, read 2026-10-09 (public geography, nothing invented):
// - Point A, Cape Muzon: USGS GNIS feature 1420865, 54.6678 N 132.6889 W. Point B, the south end of
// the Portland Channel boundary: International Boundary Commission digital boundary v1.3 (NAD83),
// 54.7074 N 130.6156 W. The straight A-B line is the Dixon Entrance line of the 1903 Alaska
// Boundary Tribunal award (about 54 40 N); Canada reads it as the maritime boundary, the US as
// only dividing the land. This filter treats it as the cut-off: a camera south of it is not US.
// - The Portland Channel vertices between B and the 56th parallel, and the Juan de Fuca / Haro Strait
// / Boundary Pass chain below, are vertices of the same IBC shapefile, thinned to about 500 m.
// - The north and west edges are the older rough hull, unchanged.
const PANHANDLE_BORDER = [[-141, 60.3], [-135.5, 59.8], [-133.4, 58.9], [-130.0, 56.1], [-130.0, 55.3],
[-130.1752, 55.0979], [-130.3369, 54.9248], [-130.5803, 54.7903], [-130.6156, 54.7074], [-132.6889, 54.6678], [-141, 54.6678]];
// Canada west of the Juan de Fuca / Haro Strait / Boundary Pass line (IBC v1.3) and north of it, up to 49.05
// (the 49.0 to 49.4 band is the other clause of inCanadianCorner). Victoria, Saanich, Sidney, Duncan and
// the Gulf Islands sit inside the rough Washington box (lat up to 49.1, lon from -124.9).
const SW_BC_BORDER = [[-124.9, 48.4934], [-124.7272, 48.4934], [-124.0121, 48.2967], [-123.6791, 48.24], [-123.5412, 48.2246],
[-123.2484, 48.284], [-123.1151, 48.4228], [-123.1599, 48.4535], [-123.2189, 48.5487], [-123.2679, 48.694],
[-123.0085, 48.7671], [-123.0085, 48.8312], [-123.3222, 49.0021], [-123.3222, 49.05], [-124.9, 49.05]];
function insidePolygon(poly, lat, lon) {
let inside = false;
for (let i = 0, j = PANHANDLE_BORDER.length - 1; i < PANHANDLE_BORDER.length; j = i++) {
const [xi, yi] = PANHANDLE_BORDER[i];
const [xj, yj] = PANHANDLE_BORDER[j];
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
const [xi, yi] = poly[i];
const [xj, yj] = poly[j];
if ((yi > lat) !== (yj > lat) && lon < ((xj - xi) * (lat - yi)) / (yj - yi) + xi) inside = !inside;
}
return inside;
}
const inCanadianCorner = (lat, lon) => (lon > -141 && lon <= -129.9 && lat >= 54.5 && lat <= 71.5 && !insidePanhandle(lat, lon)) || (lat > 49.0 && lat <= 49.4 && lon >= -124.9 && lon <= -117.0);
const insidePanhandle = (lat, lon) => insidePolygon(PANHANDLE_BORDER, lat, lon);
const inCanadianCorner = (lat, lon) => (lon > -141 && lon <= -129.9 && lat <= 71.5 && !insidePanhandle(lat, lon))
|| (lat > 49.0 && lat <= 49.4 && lon >= -124.9 && lon <= -117.0)
|| insidePolygon(SW_BC_BORDER, lat, lon);
function inVolcviewCoverage(lat, lon) {
if (inCanadianCorner(lat, lon)) return false;
return inUnitedStates(lat, lon) || VOLCVIEW_EXTRA_BOXES.some((b) => lat >= b[0] && lat <= b[1] && lon >= b[2] && lon <= b[3]);
Expand Down
Loading
Loading