/** * Geographic utility functions for location detection and classification */ /** * Bounding box for a geographic region */ interface BoundingBox { minLat: number; maxLat: number; minLon: number; maxLon: number; } /** * Geographic region with bounding box and metadata */ interface GeographicRegion { name: string; bbox: BoundingBox; description?: string; } /** * Check if coordinates are within the Great Lakes region * @param latitude Latitude coordinate * @param longitude Longitude coordinate * @returns The Great Lake name if in region, null otherwise */ export declare function getGreatLakeRegion(latitude: number, longitude: number): string | null; /** * Check if coordinates are within a major US coastal bay or large inland lake * @param latitude Latitude coordinate * @param longitude Longitude coordinate * @returns The bay/lake name if in region, null otherwise */ export declare function getMajorCoastalBayRegion(latitude: number, longitude: number): string | null; /** * Check if coordinates should use NOAA marine data (Great Lakes or major coastal bays) * @param latitude Latitude coordinate * @param longitude Longitude coordinate * @returns Object with detection results */ export declare function shouldUseNOAAMarine(latitude: number, longitude: number): { useNOAA: boolean; region: string | null; source: 'great-lakes' | 'coastal-bay' | 'ocean'; }; /** * Get a human-readable description of the marine region * @param latitude Latitude coordinate * @param longitude Longitude coordinate * @returns Description string */ export declare function getMarineRegionDescription(latitude: number, longitude: number): string; /** * Get all Great Lakes regions (for testing and documentation) */ export declare function getGreatLakesRegions(): GeographicRegion[]; /** * Get all major coastal bay regions (for testing and documentation) */ export declare function getMajorCoastalBayRegions(): GeographicRegion[]; /** * Approximate country/region detection from coordinates * PRIVACY: Intentionally vague - only major regions for privacy * ACCURACY: This is a ROUGH approximation with known inaccuracies: * - Mexico, Caribbean, Central America -> OTHER * - Some border regions may be misclassified * - Alaska and Hawaii have special handling * * This is called OUTSIDE the analytics module to ensure coordinates * never enter the analytics boundary. Trade-off: Privacy (no reverse geocoding) * vs Accuracy (bounding boxes) * * @param lat Latitude (-90 to 90) * @param lon Longitude (-180 to 180) * @returns ISO 3166-1 alpha-2 region code (US, CA, EU, AP, SA, AF, OC, OTHER) */ export declare function getCountryFromCoordinates(lat: number, lon: number): string; /** * Determine if coordinates are within the United States (including Alaska, Hawaii and Puerto Rico; the other NWS-served territories are `isInNwsTerritory`) * Uses bounding box approach for simplicity * @param latitude Latitude coordinate * @param longitude Longitude coordinate * @returns True if coordinates fall within US bounding boxes (CONUS, Alaska, Hawaii, Puerto Rico) */ export declare function isInUS(latitude: number, longitude: number): boolean; /** * Determine if coordinates fall within Great Britain and its surrounding waters, * drawn generously — this is a **routing-only** pre-gate, not a coverage claim. * * It exists to answer one question cheaply: "is a Nominatim reverse-geocode call * worth making for this point?" Nominatim is rate-limited to 1 req/sec server-wide, * so `get_river_conditions` must not fire one for every non-US point on Earth just * to learn the answer is 'not gb' for the vast majority of them. This predicate * narrows that down; the country-code lookup that follows it is what actually * decides 'gb' vs anything else. * * This function NEVER renders, and no sentence in any tool output may be derived * from it — it must never be promoted to one. The coverage claim the tool actually * makes — "the EA river-gauge network" — comes from filtering stations on a * non-empty `riverName` field, never from this box and never from the word * "England". See GOTCHAS G53: a routing box that is 95% right is fine for "which * upstream do I ask" — the wrong 5% costs one extra API call — but the same box * behind a rendered sentence becomes a false statement about a named place. Keep * this predicate on the routing side of that line. * * Because a false positive here costs one Nominatim call (cheap) and a false * negative silently drops the feature for a real GB point (expensive), the boxes * below are drawn wide: they cover the Scottish islands (Outer Hebrides, Orkney, * Shetland — Shetland reaches past 60.85N), the southwest tip (Land's End, Isles * of Scilly), and are allowed to overlap the near Continent and Atlantic approaches. * * Ireland is the one neighbor that must be excluded (Dublin must read false), and * it cannot be excluded with a single box: Ireland's latitude span (Mizen Head * 51.45N to Malin Head 55.38N) and longitude span (Dunmore Head -10.66W to Burr * Point, Co. Down, -5.43W) both overlap Great Britain's. So GB is split into three * latitude bands here, each clipped only where it actually risks Ireland: * - south of 51.4N (Scilly, Land's End, the Channel coast) — south of Ireland's * southernmost point, so longitude is left wide open * - north of 55.45N (Highlands, Hebrides, Orkney, Shetland) — north of Ireland's * northernmost point, so longitude is left wide open * - the 51.4-55.45N band in between (England, Wales, southern/mid Scotland), * where the west edge is drawn at -5.85: it clears Dublin (-6.26) and the rest * of the Republic, though it also catches the extreme eastern tip of Northern * Ireland (Burr Point, Co. Down, -5.43) — harmless, since NI is still part of * the UK and resolves to 'gb' at the country-code step that follows * * @param latitude Latitude coordinate * @param longitude Longitude coordinate * @returns True if coordinates fall within Great Britain's generous routing box */ export declare function isInGreatBritain(latitude: number, longitude: number): boolean; /** * Determine if coordinates fall within one of the NWS-served US territories that * `isInUS` deliberately leaves out — Guam, the Northern Mariana Islands (southern * arc only), the US Virgin Islands and American Samoa. * * It answers exactly one question: **will api.weather.gov accept an alerts point * here?** Not "is this the United States", not "does NWS forecast here" (it reports * no `gridId` for American Samoa at all), and not "does any other service cover * this place". * * This function is **routing only and NEVER renders.** No sentence in any tool * output may be derived from it. Its one consumer is the critical-alert banner's * pre-filter in `src/handlers/criticalAlertBanner.ts`, which uses it to decide * whether one `getAlerts` call is worth making. See GOTCHAS G53: promoting a * routing heuristic to a rendered claim inherits every edge the heuristic was * previously allowed to get wrong. The moment a rendered sentence names a * territory off this predicate, every box below owes a fresh audit against the * jurisdiction that sentence names. * * **Why these boxes are drawn tightly to the islands, which is the opposite of * `isInGreatBritain`'s posture.** There, a false positive costs one cheap * Nominatim call, so the boxes are deliberately generous. Here a false positive * costs an HTTP 400 `Parameter "point" is invalid: out of bounds` from * api.weather.gov, which `NOAAService.makeRequest` turns into an * `InvalidLocationError` after its own `securityEvent: true` warn; the banner's * catch then warns a second time, and `getAlerts` never caches a failure, so every * call repeats both. Two security-event log lines per request is the price of a * loose edge, so every corner below was probed against the live endpoint on * 2026-09-18 and the seams measured. An edge moved "for tidiness" is a 400 or a * dropped island. The probe table is in `.devdocs/plan-nws-alert-jurisdiction.md`. * * **Why Guam and the CNMI are two boxes and not one.** The union's northwest * corner, `15.2 N, 144.8 E`, is a measured 400 — the accepted region is not convex * there, so a single merged box would admit open ocean NWS rejects. * * **Why the CNMI box stops at 15.35 N.** NWS rejects every point from 16.0 N north * along 145.7 E: Anatahan, Pagan, Agrihan and Farallon de Pajaros are all out of * bounds. The northern Marianas are excluded because the service excludes them, * not because they were forgotten. * * **The USVI box admits the British Virgin Islands** (Tortola, Virgin Gorda). NWS * answers 200 at those points and nothing here is rendered, so the admission is * harmless and deliberate — tightening the box to exclude them would buy nothing * and risk clipping St Thomas and St John. * * **Why American Samoa is two boxes.** The territory is not a rectangle: the main * box holds the Tutuila–Manuʻa–Rose band, and Swains Island is a separate pocket * ~350 km north. Apia (`−13.83, −171.76`, sovereign Samoa) and the `−168.05` * corners outside the band are measured 400s. * * **`isInUS` must never be widened to absorb these.** That predicate backs a * *rendered* coverage claim in `get_river_conditions` (G53), and the two questions * have different answers: NWPS gauges Puerto Rico and gauges nothing in Guam, so a * widened `isInUS` would tell a caller in Guam that NWPS covers Guam. The two * predicates are **disjoint by construction** — the Puerto Rico box's east edge is * `−65.2` and the USVI box's west edge is `−65.15`, so they touch without * overlapping. * * @param latitude Latitude coordinate * @param longitude Longitude coordinate * @returns True if NWS accepts an alerts point at these coordinates in one of the * four territories `isInUS` excludes */ export declare function isInNwsTerritory(latitude: number, longitude: number): boolean; export {}; //# sourceMappingURL=geography.d.ts.map