1// This val calculates driving/transit time from given origins to the nearest grocery store, gym, FiDi, Roblox HQ, and Samsara in San Francisco.
2// It uses data from SF_Grocery and SF_Gyms blobs, and the Google Maps Directions API for travel times.
3// It also looks up the neighborhood based on the ZIP code using the SF_Neighborhood_ZIP blob.
4// Results are saved and displayed for each new address added, with options to delete individual results.
219 }
220
221 const apiKey = Deno.env.get("GOOGLE_MAPS_API_KEY");
222 if (!apiKey) {
223 console.error("API key is missing");
224 return new Response(JSON.stringify({ error: "API key is not configured" }), {
225 headers: { "Content-Type": "application/json" },
226 });
237
238 console.log("Finding nearest grocery");
239 const nearestGrocery = await findNearest(origin, groceries, apiKey);
240 console.log("Nearest grocery:", nearestGrocery);
241
242 console.log("Finding nearest gym");
243 const nearestGym = await findNearest(origin, gyms, apiKey);
244 console.log("Nearest gym:", nearestGym);
245
246 console.log("Calculating driving time to FiDi");
247 const fidiDestination = "548 Market St, San Francisco, CA 94104";
248 const fidiDrivingTime = await getDrivingTime(origin, fidiDestination, apiKey);
249 console.log("FiDi driving time:", fidiDrivingTime);
250
251 console.log("Calculating driving time to Roblox");
252 const robloxDestination = "910 Park Pl Ste 300, San Mateo, CA 94403";
253 const robloxDrivingTime = await getDrivingTime(origin, robloxDestination, apiKey, "09:00:00", "Tuesday");
254 console.log("Roblox driving time:", robloxDrivingTime);
255
256 console.log("Calculating transit time to Samsara");
257 const samsaraDestination = "1 De Haro St, San Francisco, CA 94103";
258 const samsaraTransitTime = await getTransitTime(origin, samsaraDestination, apiKey);
259 console.log("Samsara transit time:", samsaraTransitTime);
260
261 console.log("Extracting ZIP code and looking up neighborhood");
262 const zipCode = await getZipCode(origin, apiKey);
263 const neighborhoodZipMap = await blob.getJSON("SF_Neighborhood_ZIP");
264 const neighborhood = neighborhoodZipMap[zipCode] || "Unknown";
315}
316
317async function findNearest(origin: string, locations: any[], apiKey: string): Promise<any> {
318 console.log(`Finding nearest location among ${locations.length} options`);
319 const batchSize = 25; // Google Maps API typically allows up to 25 destinations per request
320 let nearestLocation = null;
321 let shortestTime = Infinity;
324 const batch = locations.slice(i, i + batchSize);
325 const destinations = batch.map(location => `${location.gps.lat},${location.gps.lng}`).join("|");
326 const distanceMatrixUrl = `https://maps.googleapis.com/maps/api/distancematrix/json?origins=${
327 encodeURIComponent(origin)
328 }&destinations=${encodeURIComponent(destinations)}&mode=driving&key=${apiKey}`;
329
330 console.log(`Fetching from Distance Matrix API for batch ${i / batchSize + 1}`);
331 const response = await fetch(distanceMatrixUrl);
332 const data = await response.json();
333 console.log("Distance Matrix API response status:", data.status);
334
335 if (data.status !== "OK") {
336 console.error("Distance Matrix API failed:", data);
337 throw new Error(`Distance Matrix API failed. Status: ${data.status}`);
338 }
339
362 origin: string,
363 destination: string,
364 apiKey: string,
365 arrivalTime?: string,
366 arrivalDay?: string,
367): Promise<string> {
368 let directionsUrl = `https://maps.googleapis.com/maps/api/directions/json?origin=${
369 encodeURIComponent(origin)
370 }&destination=${encodeURIComponent(destination)}&mode=driving&key=${apiKey}`;
371
372 if (arrivalTime && arrivalDay) {
388}
389
390async function getTransitTime(origin: string, destination: string, apiKey: string): Promise<string> {
391 const directionsUrl = `https://maps.googleapis.com/maps/api/directions/json?origin=${
392 encodeURIComponent(origin)
393 }&destination=${encodeURIComponent(destination)}&mode=transit&key=${apiKey}`;
394
395 const directionsResponse = await fetch(directionsUrl);
405}
406
407async function getZipCode(address: string, apiKey: string): Promise<string> {
408 const geocodeUrl = `https://maps.googleapis.com/maps/api/geocode/json?address=${
409 encodeURIComponent(address)
410 }&key=${apiKey}`;
411 const response = await fetch(geocodeUrl);
412 const data = await response.json();