andrewammann commited on
Commit
180a010
·
verified ·
1 Parent(s): 70e0514

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +249 -19
index.html CHANGED
@@ -1,19 +1,249 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Hail Size Estimator</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.production.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.22.10/babel.min.js"></script>
11
+ <script src="https://cdn.jsdelivr.net/npm/axios@1.4.0/dist/axios.min.js"></script>
12
+ <script src="https://unpkg.com/@duckdb/duckdb-wasm@1.28.0/dist/duckdb-browser.js"></script>
13
+ <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
14
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
15
+ <style>
16
+ #map { height: 400px; }
17
+ .sidebar { width: 300px; }
18
+ </style>
19
+ </head>
20
+ <body class="bg-gray-100">
21
+ <div id="root"></div>
22
+
23
+ <script type="text/babel">
24
+ const { useState, useEffect } = React;
25
+
26
+ const App = () => {
27
+ const [address, setAddress] = useState("Dallas, TX");
28
+ const [date, setDate] = useState("2025-08-08");
29
+ const [showData, setShowData] = useState("Show All");
30
+ const [data, setData] = useState([]);
31
+ const [lat, setLat] = useState(null);
32
+ const [lon, setLon] = useState(null);
33
+ const [map, setMap] = useState(null);
34
+ const geocodeKey = process.env.GEOCODE_KEY || "YOUR_API_KEY";
35
+
36
+ const convertToCSV = (data) => {
37
+ const headers = ["Date_utc", "Within 1 Mile", "Within 3 Miles", "Within 5 Miles", "Within 10 Miles", "Address"];
38
+ const csvRows = [headers.join(",")];
39
+ data.forEach(row => {
40
+ const values = headers.map(header => {
41
+ return header === "Address" ? `"${address}"` : `"${row[header] || ""}"`;
42
+ });
43
+ csvRows.push(values.join(","));
44
+ });
45
+ return new Blob([csvRows.join("\n")], { type: "text/csv" });
46
+ };
47
+
48
+ const geocode = async (address) => {
49
+ try {
50
+ const encodedAddress = encodeURIComponent(address);
51
+ const response = await axios.get(
52
+ `https://api.geocod.io/v1.7/geocode?q=${encodedAddress}&api_key=${geocodeKey}`,
53
+ { headers: { "Content-Type": "application/json" } }
54
+ );
55
+ const { lat, lng } = response.data.results[0].location;
56
+ setLat(lat);
57
+ setLon(lng);
58
+ return { lat, lng };
59
+ } catch (error) {
60
+ alert("Address not found. Try correcting with City, State & Zip.");
61
+ return { lat: null, lng: null };
62
+ }
63
+ };
64
+
65
+ const getData = async (lat, lon, dateStr) => {
66
+ const db = await duckdb.createDB();
67
+ await db.run("PRAGMA threads=2");
68
+ await db.run("PRAGMA enable_object_cache");
69
+ const query = `
70
+ SELECT "#ZTIME" AS Date_utc, LON, LAT, MAXSIZE
71
+ FROM 'data/*.parquet'
72
+ WHERE LAT <= ${lat + 1} AND LAT >= ${lat - 1}
73
+ AND LON <= ${lon + 1} AND LON >= ${lon - 1}
74
+ AND "#ZTIME" <= '${dateStr}'
75
+ `;
76
+ const result = await db.query(query);
77
+ return result.toArray();
78
+ };
79
+
80
+ const calculateDistance = (lat1, lon1, lat2, lon2) => {
81
+ const toRad = (value) => (value * Math.PI) / 180;
82
+ const R = 3958.8; // Earth's radius in miles
83
+ const dLat = toRad(lat2 - lat1);
84
+ const dLon = toRad(lon2 - lon1);
85
+ const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
86
+ Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
87
+ Math.sin(dLon / 2) * Math.sin(dLon / 2);
88
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
89
+ return R * c;
90
+ };
91
+
92
+ useEffect(() => {
93
+ const initializeMap = (lat, lon) => {
94
+ const mapInstance = L.map("map").setView([lat, lon], 9);
95
+ L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
96
+ attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
97
+ }).addTo(mapInstance);
98
+ L.marker([lat, lon]).addTo(mapInstance)
99
+ .bindTooltip(`Address: ${address}`);
100
+ setMap(mapInstance);
101
+ };
102
+
103
+ const fetchData = async () => {
104
+ const { lat, lng } = await geocode(address);
105
+ if (lat && lng) {
106
+ const dateStr = date.replace(/-/g, "");
107
+ let hailData = await getData(lat, lng, dateStr);
108
+ hailData = hailData.map(row => ({
109
+ ...row,
110
+ Lat_address: lat,
111
+ Lon_address: lng,
112
+ Miles_to_Hail: calculateDistance(row.LAT, row.LON, lat, lng).toFixed(2),
113
+ MAXSIZE: Number(row.MAXSIZE).toFixed(2),
114
+ Category: row.Miles_to_Hail < 1 ? "Within 1 Mile" :
115
+ row.Miles_to_Hail < 3 ? "Within 3 Miles" :
116
+ row.Miles_to_Hail < 5 ? "Within 5 Miles" :
117
+ row.Miles_to_Hail < 10 ? "Within 10 Miles" : "Other"
118
+ }));
119
+
120
+ const pivotData = {};
121
+ hailData.forEach(row => {
122
+ if (!pivotData[row.Date_utc]) {
123
+ pivotData[row.Date_utc] = {};
124
+ }
125
+ pivotData[row.Date_utc][row.Category] = Math.max(
126
+ pivotData[row.Date_utc][row.Category] || 0,
127
+ row.MAXSIZE
128
+ );
129
+ });
130
+
131
+ const colsFocus = ["Within 1 Mile", "Within 3 Miles", "Within 5 Miles", "Within 10 Miles"];
132
+ let formattedData = Object.keys(pivotData).map(date => {
133
+ const row = { Date_utc: new Date(date).toISOString().split("T")[0] };
134
+ colsFocus.forEach(col => {
135
+ row[col] = pivotData[date][col] || null;
136
+ });
137
+ return row;
138
+ });
139
+
140
+ if (showData !== "Show All") {
141
+ formattedData = formattedData.filter(row => row[`Within ${showData}`]);
142
+ }
143
+
144
+ colsFocus.forEach((col, i) => {
145
+ if (i < colsFocus.length - 1) {
146
+ formattedData = formattedData.map(row => ({
147
+ ...row,
148
+ [colsFocus[i + 1]]: row[colsFocus[i + 1]] && row[colsFocus[i + 1]] > (row[colsFocus[i]] || 0)
149
+ ? row[colsFocus[i + 1]]
150
+ : row[colsFocus[i]]
151
+ }));
152
+ }
153
+ });
154
+
155
+ formattedData.sort((a, b) => new Date(b.Date_utc) - new Date(a.Date_utc));
156
+ setData(formattedData);
157
+ initializeMap(lat, lng);
158
+ }
159
+ };
160
+
161
+ fetchData();
162
+ }, [address, date, showData]);
163
+
164
+ return (
165
+ <div className="flex h-screen">
166
+ <div className="sidebar bg-white p-6 shadow-md">
167
+ <h2 className="text-xl font-bold mb-4">Hail Size Estimator</h2>
168
+ <div className="mb-4">
169
+ <label className="block text-sm font-medium">Address</label>
170
+ <input
171
+ type="text"
172
+ value={address}
173
+ onChange={(e) => setAddress(e.target.value)}
174
+ className="mt-1 p-2 w-full border rounded"
175
+ />
176
+ </div>
177
+ <div className="mb-4">
178
+ <label className="block text-sm font-medium">Loss Date (Max)</label>
179
+ <input
180
+ type="date"
181
+ value={date}
182
+ max="2025-08-08"
183
+ onChange={(e) => setDate(e.target.value)}
184
+ className="mt-1 p-2 w-full border rounded"
185
+ />
186
+ </div>
187
+ <div className="mb-4">
188
+ <label className="block text-sm font-medium">Show Data Within</label>
189
+ <select
190
+ value={showData}
191
+ onChange={(e) => setShowData(e.target.value)}
192
+ className="mt-1 p-2 w-full border rounded"
193
+ >
194
+ <option>Show All</option>
195
+ <option>1 Mile</option>
196
+ <option>3 Miles</option>
197
+ <option>5 Miles</option>
198
+ </select>
199
+ </div>
200
+ </div>
201
+ <div className="flex-1 p-6">
202
+ <div className="flex gap-6">
203
+ <div className="w-3/5">
204
+ <h2 className="text-xl font-bold mb-4">Estimated Maximum Hail Size</h2>
205
+ <p className="text-sm mb-4">Data from 2010 to 2025-08-08</p>
206
+ <table className="w-full border-collapse">
207
+ <thead>
208
+ <tr className="bg-gray-200">
209
+ <th className="border p-2">Date</th>
210
+ <th className="border p-2">Within 1 Mile</th>
211
+ <th className="border p-2">Within 3 Miles</th>
212
+ <th className="border p-2">Within 5 Miles</th>
213
+ <th className="border p-2">Within 10 Miles</th>
214
+ </tr>
215
+ </thead>
216
+ <tbody>
217
+ {data.map((row, index) => (
218
+ <tr key={index} className="border">
219
+ <td className="border p-2">{row.Date_utc}</td>
220
+ <td className="border p-2">{row["Within 1 Mile"] || "-"}</td>
221
+ <td className="border p-2">{row["Within 3 Miles"] || "-"}</td>
222
+ <td className="border p-2">{row["Within 5 Miles"] || "-"}</td>
223
+ <td className="border p-2">{row["Within 10 Miles"] || "-"}</td>
224
+ </tr>
225
+ ))}
226
+ </tbody>
227
+ </table>
228
+ <a
229
+ href={URL.createObjectURL(convertToCSV(data))}
230
+ download={`${address}_${date.replace(/-/g, "")}.csv`}
231
+ className="mt-4 inline-block bg-blue-500 text-white px-4 py-2 rounded"
232
+ >
233
+ Download Data as CSV
234
+ </a>
235
+ </div>
236
+ <div className="w-2/5">
237
+ <h2 className="text-xl font-bold mb-4">Map</h2>
238
+ <div id="map" className="w-full"></div>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ </div>
243
+ );
244
+ };
245
+
246
+ ReactDOM.render(<App />, document.getElementById("root"));
247
+ </script>
248
+ </body>
249
+ </html>