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 @@
-
+
@@ -11,16 +11,41 @@
Node Profile
-
-
-
-
-
-
-
- Loading data...
-
-
+
+
+
+
+ Loading Reach data...
+
+
+
+
+
+
+ No reach level data available for this reach. Use the
+ Map to select a different reach.
+
+
+
+
+
+
+
+
+ Loading Node data...
+
+
+
+
+
+ No node data available for this reach. Use the
+ Map to make selections, or
+ view the reach-averaged plots.
+
+
+
@@ -94,20 +119,30 @@ onMounted(async () => {
const runQuery = async () => {
querying.value.hydrocron = true
await queryHydroCron(activeFeature.value)
- chartStore.buildChart(selectedFeatures.value)
querying.value.hydrocron = false
+ chartStore.buildChart(selectedFeatures.value)
+ if (!hasReachData.value) {
+ return
+ }
querying.value.nodes = true
await getNodeDataForReach(activeFeature.value)
+
+ // check to see if any node data were actually found
+ if (hasNodeData.value) {
+ chartStore.buildDistanceChart(featuresStore.nodes)
+ // show stats if they are enabled
+ statsStore.toggleSeriesStatistics(showStatistics.value)
+ chartStore.hasNodeData = true
+ } else {
+ chartStore.hasNodeData = false
+ }
querying.value.nodes = false
- chartStore.buildDistanceChart(featuresStore.nodes)
- // show stats if they are enabled
- statsStore.toggleSeriesStatistics(showStatistics.value)
}
-let hasData = computed(() => chartStore.chartData && chartStore.chartData.datasets?.length > 0)
-let fetchingData = computed(
- () => !hasData.value && (querying.value.hydrocron || querying.value.nodes)
+let hasReachData = computed(() => chartStore.chartData && chartStore.chartData.datasets?.length > 0)
+let hasNodeData = computed(
+ () => chartStore.nodeChartData && chartStore.nodeChartData.datasets?.length > 0
)
let activeFeatureIsReach = computed(() => {
return activeFeature.value && activeFeature.value.feature_type.toLowerCase() === 'reach'
diff --git a/frontend/src/views/DistanceCharts.vue b/frontend/src/views/DistanceCharts.vue
index 7d8d2c3..e154799 100644
--- a/frontend/src/views/DistanceCharts.vue
+++ b/frontend/src/views/DistanceCharts.vue
@@ -42,10 +42,7 @@
-
-
- Loading node level data...
-
+ No node level data available for this reach.
diff --git a/frontend/src/views/TimeSeriesCharts.vue b/frontend/src/views/TimeSeriesCharts.vue
index bbe1d40..d5d19de 100644
--- a/frontend/src/views/TimeSeriesCharts.vue
+++ b/frontend/src/views/TimeSeriesCharts.vue
@@ -36,6 +36,10 @@
+
+ No data available for this reach.
+
+