From 0f27bf7533460c41d73435c7cf72a63d99f49b6c Mon Sep 17 00:00:00 2001 From: Tommy Sharkey Date: Mon, 28 Jul 2025 13:14:20 -0700 Subject: [PATCH 1/6] Adding Wastewater card --- html_js/css/index.css | 13 ++++ html_js/index.html | 53 +++++++++++++ html_js/js/app.js | 161 ++++++++++++++++++++++++++++++++++++++++ html_js/js/language.js | 1 + html_js/locales/en.json | 36 +++++++++ 5 files changed, 264 insertions(+) diff --git a/html_js/css/index.css b/html_js/css/index.css index ba4fb23..fb3b990 100644 --- a/html_js/css/index.css +++ b/html_js/css/index.css @@ -536,6 +536,19 @@ button.button.map-control-button.button { float: right; } +#spills-count { + font-weight: normal; +} + +#wastewater-data tr td:last-child { + width: 55%; +} + +#wastewater-data tr td img { + width: var(--icon); + height: var(--icon); +} + .sublabel { font-size: 0.75rem !important; width: 100%; diff --git a/html_js/index.html b/html_js/index.html index bc5c775..76b3ceb 100644 --- a/html_js/index.html +++ b/html_js/index.html @@ -583,6 +583,59 @@

Public Odo + + + diff --git a/html_js/js/app.js b/html_js/js/app.js index 5040748..52f2ea5 100644 --- a/html_js/js/app.js +++ b/html_js/js/app.js @@ -274,6 +274,151 @@ function renderOdorComplaints(geoData) { } } +function renderWastewaterFlows(data) { + console.log("[app.js] (Spills) Rendering Wastewater Flows with data:", data); + window.latestWastewaterData = data; + + const now = new Date(); + const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const mostRecentSampleTime = new Date(now.getTime() - (window.spill_days) * 24 * 60 * 60 * 1000); + const secondMostRecentSampleTime = new Date(now.getTime() - (window.spill_days*2) * 24 * 60 * 60 * 1000); + + console.log( + "[app.js] (Spills) Current date:", + now, + "most recent sample:", + mostRecentSampleTime, + "second most recent sample:", + secondMostRecentSampleTime + ); + + let mostRecentData = data.features.filter((item) => { + let itemDate = new Date(item.properties["End Time"]); + return itemDate.getTime() >= mostRecentSampleTime.getTime() + }); + console.log(`[app.js] (Spills) Last ${window.spill_days} days data:`, mostRecentData); + + mostRecentData = _.orderBy( + mostRecentData, + [(item) => new Date(item.properties["Start Time"]).getTime()], + ["desc"] + ); + + let secondMostRecentData = data.features.filter((item) => { + let itemDate = new Date(item.properties["End Time"]); + return itemDate.getTime() >= secondMostRecentSampleTime.getTime() && itemDate.getTime() < mostRecentSampleTime.getTime() + }); + console.log(`[app.js] (Spills) Previous ${window.spill_days} days data:`, secondMostRecentData); + + const sumMostRecent = mostRecentData.length; + const sumSecondMostRecent = secondMostRecentData.length; + console.log( + `[app.js] (Spills) Sum of spills in last ${window.spill_days} days:`, + sumMostRecent, + `Previous ${window.spill_days} days:`, + sumSecondMostRecent + ); + + const countSpan = document.getElementById("spills-count"); + const countIndicator = countSpan?.parentElement.querySelector(".indicator"); + if (countSpan && countIndicator) { + countSpan.innerText = i18next.t( + "sidebar.cards.wastewater.overview.count", + { count: sumMostRecent } + ); + countIndicator.className = + "indicator " + (sumMostRecent > 0 ? "high" : "low"); + console.log("[app.js] (Spills) Updated spills count and indicator.", countSpan.innerText, sumMostRecent); + } + + const countSpillsChange = sumMostRecent - sumSecondMostRecent; + console.log("[app.js] (Spills) Change in spills:", countSpillsChange); + + const changeContainer = document.querySelector( + "#wastewater-data-overview tr td:last-child" + ); + if (changeContainer) { + const changeSpan = changeContainer.querySelector("span"); + const changeIcon = changeContainer.querySelector("i"); + let trendKey = "sidebar.cards.wastewater.overview.trend"; + let trendClass = "trend-down"; + let iconClass = "bi-graph-up-arrow"; + + if (countSpillsChange === 0) { + trendKey = "sidebar.cards.wastewater.overview.trend_same"; + trendClass = "trend-flat"; + iconClass = ""; + } else if (countSpillsChange < 0) { + trendClass = "trend-up"; + iconClass = "bi-graph-down-arrow"; + } else { + trendKey = "sidebar.cards.wastewater.overview.trend_positive"; + } + + changeSpan.innerText = i18next.t(trendKey, { + change: Math.abs(countSpillsChange), + spill_days: window.spill_days + }); + changeIcon.className = `bi ${iconClass}`; + changeSpan.className = trendClass; + console.log("[app.js] (Spills) Updated trend indicator with key:", trendKey); + } + + // Table + const jsonDiv = document.querySelector("#wastewater-data tbody"); + if (!jsonDiv) { + console.warn("[app.js] (Spills) Wastewater data table not found."); + return; + } + jsonDiv.innerHTML = ""; + console.log("[app.js] (Spills) Clearing old data and building new table.", jsonDiv); + console.log("[app.js] (Spills) Most recent data:", mostRecentData); + + for (const spill of mostRecentData) { + console.log("[app.js] (Spills) Adding spill entry:", spill); + const startTime = new Date(spill.properties["Start Time"]); + const endTime = new Date(spill.properties["End Time"]); + const singleDayEvent = startTime.toDateString() === endTime.toDateString(); + const dateRange = singleDayEvent ? formatDateTime(startTime) : `${formatDateTime(startTime)} to ${formatDateTime(endTime)}`; + const volume = spill.properties["Approximate Discharge Volume"]; + const notes = spill.properties["Notes"]; + + const template = ` + + ${volume} + ${dateRange} + `; + const rowElm = new DOMParser().parseFromString(template, "text/html").body.firstChild; + + // jsonDiv.appendChild(rowElm); + jsonDiv.innerHTML += template; + console.log("[app.js] (Spills) Added spill entry:", { startTime, endTime, volume, notes }, jsonDiv.lastChild); + } + + const cardFooter = document.querySelector( + "#wastewater-card .card-footer" + ); + if (cardFooter) { + latestDate = dayjs(data.lastUpdated).toDate(); + console.log("[app.js] (Spills) Updating wastewater footer with latest date.", data.lastUpdated, "converted to", latestDate); + const span = cardFooter.querySelector("span"); + const formattedDate = formatDateTime(latestDate, { + month: "long", + day: "numeric", + hour: "numeric", + hour12: true, + }); + span.innerText = i18next.t("sidebar.cards.wastewater.footer.text", { + date: formattedDate, + }); + console.log( + "[app.js] (Spills) Updated wastewater footer with date:", + formattedDate + ); + } + +} + // --- Beach Closures Rendering --- function renderBeachClosures(jsonData) { latestBeachData = jsonData; // Store data @@ -453,6 +598,22 @@ function fetchBeachData() { }); } +function fetchWastewaterData() { + fetch( + `${resilientUrlBase}tijuana/ibwc/output/spills_last_by_site.geojson` + ) + .then((response) => + response.ok ? response.json() : Promise.reject(response.statusText) + ) + .then((jsonData) => { + renderWastewaterFlows(jsonData); + }) + .catch((error) => { + console.error("Error fetching Wastewater Flows JSON:", error); + document.querySelector("#wastewater-flows-card").remove(); + }); +} + function getIndicatorLevelForOdorComplaints(count, accumulatedOverDays = 1) { // FIXME: Tommy just made up these numbers if (count < 3 * accumulatedOverDays) { diff --git a/html_js/js/language.js b/html_js/js/language.js index 969d507..6e069ee 100644 --- a/html_js/js/language.js +++ b/html_js/js/language.js @@ -34,6 +34,7 @@ async function initializeI18next() { // --- Fetch initial data AFTER i18next is ready --- fetchH2SData(); fetchOdorData(); + fetchWastewaterData(); fetchBeachData(); // --- Optional: Language Switcher --- diff --git a/html_js/locales/en.json b/html_js/locales/en.json index 7596fce..74f0ae2 100644 --- a/html_js/locales/en.json +++ b/html_js/locales/en.json @@ -162,6 +162,42 @@ } } }, + "wastewater": { + "title": "Wastewater Flows", + "overview": { + "count": "loading flows", + "count_one": "{{count}} Flow", + "count_other": "{{count}} Flows", + "sampleDuration": "in the last {{spill_days}} days", + "trend": "-{{change}} from {{spill_days}} days ago", + "trend_positive": "+{{change}} from {{spill_days}} days ago", + "trend_same": "same as {{spill_days}} days ago" + }, + "p1": { + "body": "The following are wastewater events in the Tijuana River Valley. Events include Spills, Dry Weather Canyon Collector Transboundary Flows, and Tijuana River Transboundary Flows. Rain events are not included in this data. More information on these events can be found here:", + "link": { + "text": "San Diego Regional Water Board", + "url": "https://www.waterboards.ca.gov/sandiego/water_issues/programs/tijuana_river_valley_strategy/sewage_issue.html" + } + }, + "p2": { + "body": "Detailed data can be found here:", + "link": { + "text": "International Boundary & Water Commission - Tijuana River Basin", + "url": "https://waterdata.ibwc.gov/AQWebportal/Data/Dashboard/8" + } + }, + "tableLabel": "Flows in the last {{spill_days}} days", + "dailyCount_one": "{{count}} flow", + "dailyCount_other": "{{count}} flows", + "footer": { + "text": "Last Updated on {{date}}", + "link": { + "text": "", + "url": "" + } + } + }, "healthConcerns": { "title": "Public Health Data", "p1": { From e691e8d4f2590cc98a41a726d453a0a14432a60e Mon Sep 17 00:00:00 2001 From: Tommy Sharkey Date: Mon, 28 Jul 2025 13:19:09 -0700 Subject: [PATCH 2/6] Adding spanish version of wastewater card --- html_js/locales/es.json | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/html_js/locales/es.json b/html_js/locales/es.json index a1f606b..194a816 100644 --- a/html_js/locales/es.json +++ b/html_js/locales/es.json @@ -162,6 +162,43 @@ } } }, + + "wastewater": { + "title": "Flujos de Aguas Residuales", + "overview": { + "count": "cargando flujos", + "count_one": "{{count}} Flujo", + "count_other": "{{count}} Flujos", + "sampleDuration": "en los últimos {{spill_days}} días", + "trend": "-{{change}} desde {{spill_days}} días atrás", + "trend_positive": "+{{change}} desde {{spill_days}} días atrás", + "trend_same": "igual que {{spill_days}} días atrás" + }, + "p1": { + "body": "Los siguientes son eventos de aguas residuales en el Valle del Río Tijuana. Los eventos incluyen derrames, flujos transfronterizos del colector de cañones en clima seco y flujos transfronterizos del río Tijuana. Los eventos de lluvia no están incluidos en estos datos. Más información sobre estos eventos se puede encontrar aquí:", + "link": { + "text": "Junta Regional del Agua de San Diego", + "url": "https://www.waterboards.ca.gov/sandiego/water_issues/programs/tijuana_river_valley_strategy/sewage_issue.html" + } + }, + "p2": { + "body": "Los datos detallados se pueden encontrar aquí:", + "link": { + "text": "Comisión Internacional de Límites y Aguas - Cuenca del Río Tijuana", + "url": "https://waterdata.ibwc.gov/AQWebportal/Data/Dashboard/8" + } + }, + "tableLabel": "Flujos en los últimos {{spill_days}} días", + "dailyCount_one": "{{count}} flujo", + "dailyCount_other": "{{count}} flujos", + "footer": { + "text": "Última actualización el {{date}}", + "link": { + "text": "", + "url": "" + } + } + }, "healthConcerns": { "title": "Datos de Salud Pública", "p1": { From 836ad4fc4e79bee166a70c186b6de9bae816d5f4 Mon Sep 17 00:00:00 2001 From: Tommy Sharkey Date: Mon, 28 Jul 2025 13:23:31 -0700 Subject: [PATCH 3/6] Making wastewater card autoupdate values when language changes --- html_js/js/language.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/html_js/js/language.js b/html_js/js/language.js index 6e069ee..b03ba49 100644 --- a/html_js/js/language.js +++ b/html_js/js/language.js @@ -108,6 +108,10 @@ function updateContent() { console.log("[language.js] Re-rendering Beach Closures"); renderBeachClosures(window.latestBeachData); } + if (typeof renderWastewaterFlows === "function" && window.latestWastewaterData) { + console.log("[language.js] Re-rendering Wastewater Flows"); + renderWastewaterFlows(window.latestWastewaterData); + } // Close any tooltips try { From 506cdd11349d214ae8583e2102007086ba5b4049 Mon Sep 17 00:00:00 2001 From: David Valentine Date: Fri, 15 Aug 2025 12:39:36 -0700 Subject: [PATCH 4/6] #42 air quality complaints --- html_js/locales/en.json | 6 +++--- html_js/locales/es.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/html_js/locales/en.json b/html_js/locales/en.json index d6edd08..bd9e0af 100644 --- a/html_js/locales/en.json +++ b/html_js/locales/en.json @@ -134,7 +134,7 @@ } }, "odorComplaints": { - "title": "Smell Complaints", + "title": "Air quality Complaints", "overview": { "count": "loading Complaints", "count_one": "{{count}} Complaint", @@ -185,7 +185,7 @@ "topbar": { "beaches": "Beach Water Quality", "h2sLevels": "H₂S Levels", - "odorComplaints": "Smell Complaints", + "odorComplaints": "Air quality Complaints", "wastewaterSpills": "Wastewater Flows", "riverBasin": "River Basin", "currentData": "current data", @@ -200,7 +200,7 @@ "legend": { "label": "Legend ", "spill": "Wastewater flow", - "complaint": "Smell complaint", + "complaint": "Air quality complaint", "h2s": "H2S reading", "outlet": "Outfall", "beach": "Beach" diff --git a/html_js/locales/es.json b/html_js/locales/es.json index a1f606b..77e2da3 100644 --- a/html_js/locales/es.json +++ b/html_js/locales/es.json @@ -134,9 +134,9 @@ } }, "odorComplaints": { - "title": "Quejas por Olores", + "title": "Quejas del aire", "overview": { - "count": "loading Complaints", + "count": "loading Denuncias", "count_one": "{{count}} Queja", "count_other": "{{count}} Quejas", "sampleDuration": "en los últimos 14 días", @@ -185,7 +185,7 @@ "topbar": { "beaches": "Calidad del Agua de Playas", "h2sLevels": "Niveles de H₂S", - "odorComplaints": "Quejas por Olores", + "odorComplaints": "Quejas del aire", "wastewaterSpills": "Flujos de Aguas Negras", "riverBasin": "Cuenca del Río", "currentData": "datos actuales", From 09e3f06b9c037138f0029c65661121e9c293a1f7 Mon Sep 17 00:00:00 2001 From: David Valentine Date: Fri, 15 Aug 2025 12:42:46 -0700 Subject: [PATCH 5/6] #42 air quality complaints. Standardize Title casing --- html_js/locales/en.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/html_js/locales/en.json b/html_js/locales/en.json index bd9e0af..931ac70 100644 --- a/html_js/locales/en.json +++ b/html_js/locales/en.json @@ -134,7 +134,7 @@ } }, "odorComplaints": { - "title": "Air quality Complaints", + "title": "Air Quality Complaints", "overview": { "count": "loading Complaints", "count_one": "{{count}} Complaint", @@ -185,7 +185,7 @@ "topbar": { "beaches": "Beach Water Quality", "h2sLevels": "H₂S Levels", - "odorComplaints": "Air quality Complaints", + "odorComplaints": "Air Quality Complaints", "wastewaterSpills": "Wastewater Flows", "riverBasin": "River Basin", "currentData": "current data", @@ -200,7 +200,7 @@ "legend": { "label": "Legend ", "spill": "Wastewater flow", - "complaint": "Air quality complaint", + "complaint": "Air Quality Complaint", "h2s": "H2S reading", "outlet": "Outfall", "beach": "Beach" From 839e91b1c37ae038cf7b121fc8b4daf5fdbaa7d5 Mon Sep 17 00:00:00 2001 From: David Valentine Date: Fri, 15 Aug 2025 13:44:03 -0700 Subject: [PATCH 6/6] =?UTF-8?q?#42=20air=20quality=20complaints.=20?= =?UTF-8?q?=E2=80=9Cenvironmental=20odors=E2=80=9D=20to=20=E2=80=9Cenviron?= =?UTF-8?q?mental=20gases=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- html_js/locales/en.json | 2 +- html_js/locales/es.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/html_js/locales/en.json b/html_js/locales/en.json index 6e440f3..d1fa8a4 100644 --- a/html_js/locales/en.json +++ b/html_js/locales/en.json @@ -44,7 +44,7 @@ "h2sLink": { "label1": "Recommendations to ", "link1": { - "text": "Protect Yourself from Environmental Odors", + "text": "Protect Yourself from Environmental Gases", "url": "https://www.sandiegocounty.gov/content/sdc/hhsa/programs/phs/community_epidemiology/south-region-health-concerns/about-environmental-odors.html" }, "airpurifiers": "San Diego County Air Pollution Control District (SDAPCD) is providing free air purifiers to residents living in affected communities. Check with the AIRE Program to see if you are eligible", diff --git a/html_js/locales/es.json b/html_js/locales/es.json index a286a24..e0162c0 100644 --- a/html_js/locales/es.json +++ b/html_js/locales/es.json @@ -44,7 +44,7 @@ "h2sLink": { "label1": "Recomendaciones para ", "link1": { - "text": "Protegerte de Olores Ambientales", + "text": "Protégete de los gases ambientales", "url": "https://www.sandiegocounty.gov/content/sdc/hhsa/programs/phs/community_epidemiology/south-region-health-concerns/about-environmental-odors.html" }, "airpurifiers": "El Distrito de Control de la Contaminación del Aire del Condado de San Diego (SDAPCD) está proporcionando purificadores de aire gratuitos a los residentes que viven en comunidades afectadas. Consulta con el Programa AIRE para ver si eres elegible.",