feat(monitor/collectors/usgs): GeoJSON feed client (2.5_day, ms-since-epoch decoder)

This commit is contained in:
CarterPerez-dev 2026-05-02 04:21:38 -04:00
parent 11a68de2b7
commit 1d414ff341
3 changed files with 271 additions and 0 deletions

View File

@ -0,0 +1,87 @@
// ©AngelaMos | 2026
// client.go
package usgs
import (
"context"
"fmt"
"time"
"golang.org/x/time/rate"
"github.com/carterperez-dev/monitor-the-situation/backend/internal/httpx"
)
const (
defaultUSGSBaseURL = "https://earthquake.usgs.gov"
pathQuakes2_5_day = "/earthquakes/feed/v1.0/summary/2.5_day.geojson"
defaultUSGSRate = 100 * time.Millisecond
defaultUSGSBurst = 5
defaultUSGSBudget = 5
defaultUSGSBreaker = 60 * time.Second
)
type ClientConfig struct {
BaseURL string
}
type Client struct {
hx *httpx.Client
}
func NewClient(cfg ClientConfig) *Client {
if cfg.BaseURL == "" {
cfg.BaseURL = defaultUSGSBaseURL
}
return &Client{
hx: httpx.New(httpx.Config{
Name: "usgs",
BaseURL: cfg.BaseURL,
Rate: rate.Every(defaultUSGSRate),
Burst: defaultUSGSBurst,
ConsecutiveFailureBudget: defaultUSGSBudget,
BreakerTimeout: defaultUSGSBreaker,
}),
}
}
type Feed struct {
Type string `json:"type"`
Features []Feature `json:"features"`
}
type Feature struct {
Type string `json:"type"`
ID string `json:"id"`
Properties Properties `json:"properties"`
Geometry Geometry `json:"geometry"`
}
type Properties struct {
Mag float64 `json:"mag"`
Place string `json:"place"`
Time int64 `json:"time"`
Updated int64 `json:"updated"`
Alert string `json:"alert"`
Tsunami int `json:"tsunami"`
URL string `json:"url"`
Type string `json:"type"`
}
func (p Properties) OccurredAt() time.Time {
return time.UnixMilli(p.Time).UTC()
}
type Geometry struct {
Type string `json:"type"`
Coordinates []float64 `json:"coordinates"`
}
func (c *Client) Fetch(ctx context.Context) (Feed, error) {
var feed Feed
if err := c.hx.GetJSON(ctx, pathQuakes2_5_day, nil, &feed); err != nil {
return Feed{}, fmt.Errorf("fetch usgs feed: %w", err)
}
return feed, nil
}

View File

@ -0,0 +1,43 @@
// ©AngelaMos | 2026
// client_test.go
package usgs_test
import (
"context"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/carterperez-dev/monitor-the-situation/backend/internal/collectors/usgs"
)
func TestClient_FetchDecodesFeatures(t *testing.T) {
body, err := os.ReadFile("testdata/2_5_day.geojson")
require.NoError(t, err)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/geo+json")
_, _ = w.Write(body)
}))
defer srv.Close()
c := usgs.NewClient(usgs.ClientConfig{BaseURL: srv.URL})
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
feed, err := c.Fetch(ctx)
require.NoError(t, err)
require.GreaterOrEqual(t, len(feed.Features), 1)
f := feed.Features[0]
require.NotEmpty(t, f.ID)
require.NotEmpty(t, f.Properties.Place)
require.GreaterOrEqual(t, f.Properties.Mag, 2.5)
require.Len(t, f.Geometry.Coordinates, 3)
require.False(t, f.Properties.OccurredAt().IsZero())
}

View File

@ -0,0 +1,141 @@
{
"type": "FeatureCollection",
"metadata": {
"generated": 1777710093000,
"url": "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.geojson",
"title": "USGS Magnitude 2.5+ Earthquakes, Past Day",
"status": 200,
"api": "2.4.0",
"count": 39
},
"features": [
{
"type": "Feature",
"properties": {
"mag": 2.8,
"place": "18 km NNW of Jal, New Mexico",
"time": 1777707261749,
"updated": 1777707667040,
"tz": null,
"url": "https://earthquake.usgs.gov/earthquakes/eventpage/us7000shr9",
"detail": "https://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/us7000shr9.geojson",
"felt": null,
"cdi": null,
"mmi": null,
"alert": null,
"status": "reviewed",
"tsunami": 0,
"sig": 121,
"net": "us",
"code": "7000shr9",
"ids": ",us7000shr9,",
"sources": ",us,",
"types": ",origin,phase-data,",
"nst": 35,
"dmin": 0.255,
"rms": 0.56,
"gap": 57,
"magType": "mb_lg",
"type": "earthquake",
"title": "M 2.8 - 18 km NNW of Jal, New Mexico"
},
"geometry": {
"type": "Point",
"coordinates": [
-103.2923,
32.2604,
5.109
]
},
"id": "us7000shr9"
},
{
"type": "Feature",
"properties": {
"mag": 2.97,
"place": "27 km SW of Esperanza, Puerto Rico",
"time": 1777705903970,
"updated": 1777707214900,
"tz": null,
"url": "https://earthquake.usgs.gov/earthquakes/eventpage/pr71515393",
"detail": "https://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/pr71515393.geojson",
"felt": null,
"cdi": null,
"mmi": null,
"alert": null,
"status": "reviewed",
"tsunami": 0,
"sig": 136,
"net": "pr",
"code": "71515393",
"ids": ",pr71515393,",
"sources": ",pr,",
"types": ",origin,phase-data,",
"nst": 15,
"dmin": 0.278,
"rms": 0.29,
"gap": 171,
"magType": "md",
"type": "earthquake",
"title": "M 3.0 - 27 km SW of Esperanza, Puerto Rico"
},
"geometry": {
"type": "Point",
"coordinates": [
-65.6316666666667,
17.898,
7.84
]
},
"id": "pr71515393"
},
{
"type": "Feature",
"properties": {
"mag": 4.6,
"place": "79 km N of Calama, Chile",
"time": 1777702962852,
"updated": 1777704100040,
"tz": null,
"url": "https://earthquake.usgs.gov/earthquakes/eventpage/us7000shqp",
"detail": "https://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/us7000shqp.geojson",
"felt": null,
"cdi": null,
"mmi": null,
"alert": null,
"status": "reviewed",
"tsunami": 0,
"sig": 326,
"net": "us",
"code": "7000shqp",
"ids": ",us7000shqp,",
"sources": ",us,",
"types": ",origin,phase-data,",
"nst": 58,
"dmin": 0.437,
"rms": 1.41,
"gap": 37,
"magType": "mb",
"type": "earthquake",
"title": "M 4.6 - 79 km N of Calama, Chile"
},
"geometry": {
"type": "Point",
"coordinates": [
-68.7738,
-21.7539,
115.882
]
},
"id": "us7000shqp"
}
],
"bbox": [
-178.5514,
-57.7679,
5.109,
178.8233,
61.621,
532.819
]
}