Skip to main content

max / balanced_breakfast

4.9 KB · 217 lines History Blame Raw
1 // USGS earthquake feed. Fetches GeoJSON from the USGS summary endpoint.
2 // Configurable magnitude threshold and time window. Converts millisecond
3 // timestamps to seconds; color-codes severity by magnitude bracket.
4
5 const USGS_API = "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary";
6
7 fn id() {
8 "earthquakes"
9 }
10
11 fn name() {
12 "USGS Earthquakes"
13 }
14
15 fn capabilities() {
16 #{
17 supports_pagination: false
18 }
19 }
20
21 fn config_schema() {
22 #{
23 description: "Real-time earthquake data from the USGS. Choose a minimum magnitude and time window.",
24 fields: [
25 #{
26 key: "magnitude",
27 label: "Minimum Magnitude",
28 field_type: "select",
29 required: true,
30 description: "Minimum earthquake magnitude to show",
31 default_value: "significant",
32 options: ["significant", "4.5", "2.5", "1.0", "all"]
33 },
34 #{
35 key: "timeframe",
36 label: "Time Window",
37 field_type: "select",
38 required: true,
39 description: "How far back to look",
40 default_value: "day",
41 options: ["hour", "day", "week", "month"]
42 }
43 ]
44 }
45 }
46
47 fn fetch(config, cursor) {
48 let magnitude = "significant";
49 if config.magnitude != () {
50 magnitude = config.magnitude;
51 }
52
53 let timeframe = "day";
54 if config.timeframe != () {
55 timeframe = config.timeframe;
56 }
57
58 // Build the feed URL
59 let feed_name = get_feed_name(magnitude);
60 let url = USGS_API + "/" + feed_name + "_" + timeframe + ".geojson";
61
62 let data = http_get_json(url);
63
64 if data == () {
65 return #{ items: [], has_more: false };
66 }
67
68 let features = data.features;
69 if features == () {
70 return #{ items: [], has_more: false };
71 }
72
73 let items = [];
74 for feature in features {
75 let item = parse_quake(feature);
76 if item != () {
77 items.push(item);
78 }
79 }
80
81 #{
82 items: items,
83 has_more: false
84 }
85 }
86
87 fn get_feed_name(magnitude) {
88 if magnitude == "4.5" { return "4.5"; }
89 if magnitude == "2.5" { return "2.5"; }
90 if magnitude == "1.0" { return "1.0"; }
91 if magnitude == "all" { return "all"; }
92 "significant"
93 }
94
95 fn parse_quake(feature) {
96 let props = feature.properties;
97 if props == () {
98 return ();
99 }
100
101 let title = "Unknown earthquake";
102 if props.title != () {
103 title = props.title;
104 } else if props.place != () {
105 title = props.place;
106 }
107
108 let mag = 0.0;
109 if props.mag != () {
110 mag = props.mag;
111 }
112
113 let place = "";
114 if props.place != () {
115 place = props.place;
116 }
117
118 let time = timestamp_now();
119 if props.time != () {
120 // USGS times are in milliseconds
121 time = props.time / 1000;
122 }
123
124 let url = "";
125 if props.url != () {
126 url = props.url;
127 }
128
129 let tsunami = 0;
130 if props.tsunami != () {
131 tsunami = props.tsunami;
132 }
133
134 let felt = 0;
135 if props.felt != () {
136 felt = props.felt;
137 }
138
139 let alert = "";
140 if props.alert != () {
141 alert = props.alert;
142 }
143
144 let quake_id = "";
145 if feature.id != () {
146 quake_id = feature.id;
147 }
148
149 // Coordinates
150 let coords = "";
151 if feature.geometry != () {
152 let geo = feature.geometry;
153 if geo.coordinates != () {
154 let c = geo.coordinates;
155 if c.len() >= 2 {
156 coords = "Lat: " + c[1] + ", Lon: " + c[0];
157 if c.len() >= 3 {
158 coords += ", Depth: " + c[2] + " km";
159 }
160 }
161 }
162 }
163
164 // Build body
165 let body = "**Location:** " + place + "\n";
166 body += "**Magnitude:** " + mag + "\n";
167 if coords != "" {
168 body += "**Coordinates:** " + coords + "\n";
169 }
170 if tsunami > 0 {
171 body += "**Tsunami warning:** Yes\n";
172 }
173 if felt > 0 {
174 body += "**Felt reports:** " + felt + "\n";
175 }
176 if alert != "" {
177 body += "**Alert level:** " + alert + "\n";
178 }
179
180 // Indicator based on magnitude
181 let indicator = "๐ŸŸข";
182 if mag >= 7.0 {
183 indicator = "๐Ÿ”ด";
184 } else if mag >= 5.0 {
185 indicator = "๐ŸŸ ";
186 } else if mag >= 3.0 {
187 indicator = "๐ŸŸก";
188 }
189
190 let secondary = "M" + mag + " ยท " + place;
191
192 let tags = ["earthquake", "usgs"];
193 if tsunami > 0 {
194 tags.push("tsunami");
195 }
196
197 #{
198 id: #{ source: "earthquakes", item_id: quake_id },
199 bite: #{
200 author: "USGS",
201 text: truncate(title, 100),
202 secondary: truncate(secondary, 80),
203 indicator: indicator
204 },
205 content: #{
206 title: title,
207 body: body,
208 url: url
209 },
210 meta: #{
211 source_name: "USGS Earthquakes",
212 published_at: time,
213 tags: tags
214 }
215 }
216 }
217