diff --git a/api/swotvis/app/routers/hydrocron/router.py b/api/swotvis/app/routers/hydrocron/router.py index 0a50c81..7a323f4 100644 --- a/api/swotvis/app/routers/hydrocron/router.py +++ b/api/swotvis/app/routers/hydrocron/router.py @@ -47,6 +47,9 @@ async def proxy_hydrocron_timeseries( try: async with httpx.AsyncClient() as client: + print( + f"Original request url with params: {client.build_request('GET', HYDROCRON_BASE_URL, params=params).url}" + ) response = await client.get(HYDROCRON_BASE_URL, params=params, timeout=30.0) # If the response is successful, return the content @@ -57,7 +60,15 @@ async def proxy_hydrocron_timeseries( return JSONResponse(content=response.json()) else: # Forward the error from the external API - raise HTTPException(status_code=response.status_code, detail=f"HydroCron API error: {response.text}") + # check if response has json content + if response.headers.get("Content-Type") == "application/json": + error_detail = response.json() + else: + error_detail = response.text + # ensure we include statusText as well + if "statusText" in response.headers: + error_detail += f": {response.headers['statusText']}" + return JSONResponse(content=error_detail, status_code=response.status_code) except httpx.TimeoutException: raise HTTPException(status_code=504, detail="Request timeout") diff --git a/frontend/docker-entrypoint.sh b/frontend/docker-entrypoint.sh index a6de2f8..f5703de 100755 --- a/frontend/docker-entrypoint.sh +++ b/frontend/docker-entrypoint.sh @@ -7,7 +7,6 @@ for file in $DIST_DIR/assets/*.js $DIST_DIR/index.html; do sed -i 's|VITE_APP_API_URL_PLACEHOLDER|'${VITE_APP_API_URL}'|g' $file sed -i 's|VITE_APP_FULL_URL_PLACEHOLDER|'${VITE_APP_FULL_URL}'|g' $file sed -i 's|VITE_APP_BASE_PLACEHOLDER|'${VITE_APP_BASE}'|g' $file - sed -i 's|VITE_HYDROCRON_URL_PLACEHOLDER|'${VITE_HYDROCRON_URL}'|g' $file sed -i 's|VITE_HYDROSHARE_NOTEBOOKS_COLLECTION_PLACEHOLDER|'${VITE_HYDROSHARE_NOTEBOOKS_COLLECTION}'|g' $file done diff --git a/frontend/src/_helpers/hydroCron.js b/frontend/src/_helpers/hydroCron.js index a4bff1b..c588ded 100644 --- a/frontend/src/_helpers/hydroCron.js +++ b/frontend/src/_helpers/hydroCron.js @@ -1,4 +1,4 @@ -import { HYDROCRON_URL } from '@/constants' +import { ENDPOINTS } from '@/constants' import { useFeaturesStore } from '@/stores/features' import { useAlertStore } from '@/stores/alerts' import { useHydrologicStore } from '@/stores/hydrologic' @@ -87,6 +87,12 @@ const queryHydroCron = async (swordFeature = null, output = 'geojson') => { const start_time = EARLIEST_HYDROCRON_DATETIME const end_time = new Date(Date.now() + MS_TO_KEEP_CACHE).toISOString().split('.')[0] + 'Z' + // determine which collection name to use based on feature type ('Reach' or 'PriorLake') + let collection_name = 'SWOT_L2_HR_RiverSP_D' + if (feature_type === 'PriorLake') { + collection_name = 'SWOT_L2_HR_LakeSP_D' + } + params = { feature: feature_type, feature_id, @@ -95,9 +101,15 @@ const queryHydroCron = async (swordFeature = null, output = 'geojson') => { output, fields, // https://podaac.github.io/hydrocron/timeseries.html#compact-string-required-no - compact: 'true' + compact: 'true', + // https://podaac.github.io/hydrocron/timeseries.html#collection-name-string-required-no + collection_name } - let response = await fetchHydroCronData(HYDROCRON_URL, params, swordFeature) + + // Use our API proxy URL instead of the direct HydroCron URL + // This is due to CORS issues with the HydroCron server + // https://github.com/podaac/hydrocron/issues/306 + let response = await fetchHydroCronData(ENDPOINTS.hydrocron, params, swordFeature) if (response == null) { return } @@ -134,9 +146,15 @@ const fetchHydroCronData = async (url, params, swordFeature) => { }) if (response.status < 500) { if (response.status == 400) { + let text = 'No data found for: ' + if (params.feature && params.feature_id) { + text += `${params.feature} ${params.feature_id}` + } else { + text += JSON.stringify(params) + } alertStore.displayAlert({ title: 'No data found', - text: `No data found for ${JSON.stringify(params)}`, + text, type: 'warning', closable: true, duration: 6 @@ -144,9 +162,15 @@ const fetchHydroCronData = async (url, params, swordFeature) => { return null } } else { + let text = 'Error while fetching SWOT data: ' + if (response.statusText) { + text += response.statusText + } else { + text += 'Unknown error' + } alertStore.displayAlert({ title: 'Error fetching SWOT data', - text: `Error while fetching SWOT data: ${response.statusText}`, + text, type: 'error', closable: true, duration: 3 diff --git a/frontend/src/components/TheLeafletMap.vue b/frontend/src/components/TheLeafletMap.vue index 7fa5bbe..a4debc2 100644 --- a/frontend/src/components/TheLeafletMap.vue +++ b/frontend/src/components/TheLeafletMap.vue @@ -118,7 +118,7 @@ onMounted(async () => { mapStore.generateLakesFeatures() - let url = 'https://arcgis.cuahsi.org/arcgis/services/SWOT/world_swot_lakes/MapServer/WmsServer?' + let url = 'https://arcgis.cuahsi.org/arcgis/services/SWOT/SWOT_PLD_v201/MapServer/WmsServer?' const lakesWMS = L.tileLayer.wms(url, { layers: 0, transparent: 'true', @@ -140,7 +140,7 @@ onMounted(async () => { // add reaches layer to map url = - 'https://arcgis.cuahsi.org/arcgis/services/SWOT/world_SWORD_reaches_mercator/MapServer/WMSServer?' + 'https://arcgis.cuahsi.org/arcgis/services/SWOT/world_SWORD_reaches_mercator_v17b/MapServer/WMSServer?' const reachesWMS = L.tileLayer.wms(url, { layers: 0, transparent: 'true', @@ -153,7 +153,7 @@ onMounted(async () => { // add nodes layer to map url = - 'https://arcgis.cuahsi.org/arcgis/services/SWOT/world_SWORD_nodes_mercator/MapServer/WMSServer?' + 'https://arcgis.cuahsi.org/arcgis/services/SWOT/world_SWORD_nodes_mercator_v17b/MapServer/WMSServer?' L.tileLayer.wms(url, { layers: 0, transparent: 'true', diff --git a/frontend/src/constants.js b/frontend/src/constants.js index 18db198..8673ad0 100644 --- a/frontend/src/constants.js +++ b/frontend/src/constants.js @@ -4,12 +4,12 @@ let APP_FULL_URL_IN = import.meta.env.VITE_APP_FULL_URL || 'VITE_APP_FULL_URL_PL export const APP_FULL_URL = APP_FULL_URL_IN.endsWith('/') ? APP_FULL_URL_IN : `${APP_FULL_URL_IN}/` export const APP_API_URL = import.meta.env.VITE_APP_API_URL || 'VITE_APP_API_URL_PLACEHOLDER' -export const HYDROCRON_URL = import.meta.env.VITE_HYDROCRON_URL || 'VITE_HYDROCRON_URL_PLACEHOLDER' export const VITE_HYDROSHARE_NOTEBOOKS_COLLECTION = import.meta.env.VITE_HYDROSHARE_NOTEBOOKS_COLLECTION || 'VITE_HYDROSHARE_NOTEBOOKS_COLLECTION_PLACEHOLDER' export const ENDPOINTS = { - openapi: `${APP_API_URL}/openapi.json` + openapi: `${APP_API_URL}/openapi.json`, + hydrocron: `${APP_API_URL}/hydrocron/timeseries` } export const NODE_DATETIME_VARIATION = 1 // minutes diff --git a/frontend/src/stores/map.js b/frontend/src/stores/map.js index 3d73ab7..3de50ff 100644 --- a/frontend/src/stores/map.js +++ b/frontend/src/stores/map.js @@ -219,8 +219,7 @@ export const useMapStore = defineStore('map', () => { console.warn('Lakes features already generated, skipping.') return lakesFeatures.value } - const url = - 'https://arcgis.cuahsi.org/arcgis/rest/services/SWOT/world_swot_lakes/FeatureServer/0' + const url = 'https://arcgis.cuahsi.org/arcgis/rest/services/SWOT/SWOT_PLD_v201/FeatureServer/0' lakesFeatures.value = esriLeaflet.featureLayer({ url: url, simplifyFactor: 0.35, diff --git a/frontend/src/views/ChartsView.vue b/frontend/src/views/ChartsView.vue index ddbb3e0..1408f48 100644 --- a/frontend/src/views/ChartsView.vue +++ b/frontend/src/views/ChartsView.vue @@ -1,6 +1,6 @@