Spaces:
Running
Running
| # SPDX-License-Identifier: Apache-2.0 | |
| # © 2026 Lutar, Stephen P. — SZL Holdings · Doctrine v11 · Perplexity Computer Agent. | |
| """killinchu / shared geo live connectors (keyless → CONNECTED now). | |
| usgs USGS earthquake feeds (GeoJSON) keyless | |
| noaa NOAA api.weather.gov forecast/alerts keyless (UA header) | |
| overpass OpenStreetMap Overpass API keyless | |
| """ | |
| from __future__ import annotations | |
| import time | |
| from typing import Any | |
| from ..base import Connector, Records, State, http_json, http_text, _now | |
| from ..registry import register | |
| _CACHE: dict[str, tuple[float, Any]] = {} | |
| def _cached(k, ttl): | |
| h = _CACHE.get(k) | |
| return h[1] if h and (time.time() - h[0]) < ttl else None | |
| def _put(k, v): | |
| _CACHE[k] = (time.time(), v) | |
| class UsgsConnector(Connector): | |
| id = "usgs" | |
| label = "USGS earthquakes" | |
| category = "geo" | |
| auth_kind = "none" | |
| free_tier = True | |
| provider_base = "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson" | |
| docs_url = "https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php" | |
| schema_preview = ["mag", "place", "time", "longitude", "latitude", "depth"] | |
| def _probe(self): | |
| st, _ = http_json(self.provider_base) | |
| return (st == 200), f"USGS HTTP {st}" | |
| def read(self, query: dict | None = None) -> Records: | |
| window = (query or {}).get("window", "all_day") # all_day|2.5_week|4.5_month | |
| limit = max(1, min(int((query or {}).get("limit", 15)), 50)) | |
| url = f"https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/{window}.geojson" | |
| ck = f"usgs:{window}:{limit}" | |
| c = _cached(ck, 120) | |
| if c: | |
| return c | |
| st, raw = http_json(url) | |
| if st == 200 and isinstance(raw, dict): | |
| items = [] | |
| for f in (raw.get("features", []) or [])[:limit]: | |
| p = f.get("properties", {}); g = f.get("geometry", {}).get("coordinates", [None, None, None]) | |
| items.append({"mag": p.get("mag"), "place": p.get("place"), "time": p.get("time"), | |
| "longitude": g[0], "latitude": g[1], "depth": g[2]}) | |
| r = Records(connector_id=self.id, category=self.category, state=State.CONNECTED, | |
| records=items, source=f"USGS {window} GeoJSON", live=True, | |
| note=f"live · {len(raw.get('features', []))} events", schema_preview=self.schema_preview) | |
| _put(ck, r) | |
| return r | |
| return self._ready_records(f"USGS unreachable (HTTP {st})") | |
| class NoaaConnector(Connector): | |
| id = "noaa" | |
| label = "NOAA weather (api.weather.gov)" | |
| category = "geo" | |
| auth_kind = "none" | |
| free_tier = True | |
| provider_base = "https://api.weather.gov" | |
| docs_url = "https://www.weather.gov/documentation/services-web-api" | |
| schema_preview = ["event", "severity", "area", "effective", "headline"] | |
| def _probe(self): | |
| st, _ = http_json(self.provider_base + "/alerts/active?limit=1") | |
| return (st == 200), f"NOAA HTTP {st}" | |
| def read(self, query: dict | None = None) -> Records: | |
| limit = max(1, min(int((query or {}).get("limit", 12)), 50)) | |
| area = (query or {}).get("area", "") | |
| url = f"{self.provider_base}/alerts/active?limit={limit}" + (f"&area={area}" if area else "") | |
| ck = f"noaa:{area}:{limit}" | |
| c = _cached(ck, 180) | |
| if c: | |
| return c | |
| st, raw = http_json(url) | |
| if st == 200 and isinstance(raw, dict): | |
| items = [] | |
| for f in (raw.get("features", []) or [])[:limit]: | |
| p = f.get("properties", {}) | |
| items.append({"event": p.get("event"), "severity": p.get("severity"), | |
| "area": p.get("areaDesc"), "effective": p.get("effective"), | |
| "headline": (p.get("headline") or "")[:120]}) | |
| r = Records(connector_id=self.id, category=self.category, state=State.CONNECTED, | |
| records=items, source="NOAA /alerts/active", live=True, | |
| note=f"live · {len(raw.get('features', []))} active alerts", schema_preview=self.schema_preview) | |
| _put(ck, r) | |
| return r | |
| return self._ready_records(f"NOAA unreachable (HTTP {st})") | |
| class OverpassConnector(Connector): | |
| id = "overpass" | |
| label = "OpenStreetMap Overpass" | |
| category = "geo" | |
| auth_kind = "none" | |
| free_tier = True | |
| provider_base = "https://overpass-api.de/api/interpreter" | |
| docs_url = "https://wiki.openstreetmap.org/wiki/Overpass_API" | |
| schema_preview = ["type", "id", "name", "lat", "lon"] | |
| def _probe(self): | |
| q = "[out:json][timeout:8];node(1);out;" | |
| import urllib.parse as up | |
| st, _ = http_json(self.provider_base + "?data=" + up.quote(q), timeout=10.0) | |
| return (st == 200), f"Overpass HTTP {st}" | |
| def read(self, query: dict | None = None) -> Records: | |
| # default: harbours/ports near a bbox (maritime/coastline use) | |
| oql = (query or {}).get("oql") or ( | |
| "[out:json][timeout:15];node[\"harbour\"=\"yes\"](36.0,-6.0,44.0,3.0);out 15;") | |
| limit = max(1, min(int((query or {}).get("limit", 15)), 40)) | |
| import urllib.parse as up | |
| ck = f"overpass:{hash(oql)}:{limit}" | |
| c = _cached(ck, 600) | |
| if c: | |
| return c | |
| st, raw = http_json(self.provider_base + "?data=" + up.quote(oql), timeout=20.0) | |
| if st == 200 and isinstance(raw, dict): | |
| items = [] | |
| for el in (raw.get("elements", []) or [])[:limit]: | |
| items.append({"type": el.get("type"), "id": el.get("id"), | |
| "name": (el.get("tags", {}) or {}).get("name"), | |
| "lat": el.get("lat"), "lon": el.get("lon")}) | |
| r = Records(connector_id=self.id, category=self.category, state=State.CONNECTED, | |
| records=items, source="OpenStreetMap Overpass", live=True, | |
| note=f"live · {len(raw.get('elements', []))} elements", schema_preview=self.schema_preview) | |
| _put(ck, r) | |
| return r | |
| return self._ready_records(f"Overpass unreachable (HTTP {st})") | |
| __all__ = ["UsgsConnector", "NoaaConnector", "OverpassConnector"] | |