(vesselIds)
- // TODO:deck better validation of the layer contains data
- const hasTracksData = vesselLayers?.length > 0
+ const hasTracksData = vesselLayers?.some((layer) => layer?.instance.getVesselTracksLayersLoaded())
const activeVesselsDataviews = useSelector(selectActiveVesselsDataviews)
const { timebarVisualisation, dispatchTimebarVisualisation } = useTimebarVisualisationConnect()
const { timebarSelectedEnvId, dispatchTimebarSelectedEnvId } = useTimebarEnvironmentConnect()
diff --git a/apps/fishing-map/features/vessel/activity/VesselActivity.tsx b/apps/fishing-map/features/vessel/activity/VesselActivity.tsx
index 5a1a76fa8c..48bbdade91 100644
--- a/apps/fishing-map/features/vessel/activity/VesselActivity.tsx
+++ b/apps/fishing-map/features/vessel/activity/VesselActivity.tsx
@@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next'
import { Fragment, useMemo } from 'react'
import { useSelector } from 'react-redux'
import { Choice, ChoiceOption, Spinner } from '@globalfishingwatch/ui-components'
+import { useDebounce } from '@globalfishingwatch/react-hooks'
import ActivityByType from 'features/vessel/activity/activity-by-type/ActivityByType'
import ActivityByVoyage from 'features/vessel/activity/activity-by-voyage/ActivityByVoyage'
import { VesselActivitySummary } from 'features/vessel/activity/VesselActivitySummary'
@@ -21,6 +22,7 @@ const VesselActivity = () => {
const activityMode = useSelector(selectVesselActivityMode)
const hasEventsDataset = useSelector(selectVesselHasEventsDatasets)
const eventsLoading = useVesselProfileEventsLoading()
+ const eventsLoadingDebounce = useDebounce(eventsLoading, 400)
const eventsError = useVesselProfileEventsError()
const vesselProfileDataview = useSelector(selectVesselProfileDataview)
const vesselLayer = useVesselProfileLayer()
@@ -49,7 +51,7 @@ const VesselActivity = () => {
[t]
)
- if (hasVesselEvents && (!vesselLayer?.instance || eventsLoading)) {
+ if (hasVesselEvents && (!vesselLayer?.instance || eventsLoadingDebounce)) {
return (
@@ -87,13 +89,13 @@ const VesselActivity = () => {
onSelect={setActivityMode}
/>
- {eventsLoading && (
+ {eventsLoadingDebounce && (
)}
- {!eventsLoading && activityMode === 'type' && }
- {!eventsLoading && activityMode === 'voyage' && }
+ {!eventsLoadingDebounce && activityMode === 'type' && }
+ {!eventsLoadingDebounce && activityMode === 'voyage' && }
)
}
diff --git a/apps/fishing-map/features/vessel/vessel-events.hooks.ts b/apps/fishing-map/features/vessel/vessel-events.hooks.ts
index eed9d20d7f..b8cb4c8f6a 100644
--- a/apps/fishing-map/features/vessel/vessel-events.hooks.ts
+++ b/apps/fishing-map/features/vessel/vessel-events.hooks.ts
@@ -25,7 +25,6 @@ const useVesselProfileEvents = () => {
export const useVesselProfileEventsLoading = () => {
const vesselInstance = useVesselProfileLayer()
- // TODO:deck review this and try to avoid intermediate loading states while toggled on events load
return vesselInstance?.instance && !vesselInstance?.instance?.getVesselEventsLayersLoaded()
}
diff --git a/apps/fishing-map/features/workspace/activity/ActivityLayerPanel.tsx b/apps/fishing-map/features/workspace/activity/ActivityLayerPanel.tsx
index d6ac210863..9726c2c566 100644
--- a/apps/fishing-map/features/workspace/activity/ActivityLayerPanel.tsx
+++ b/apps/fishing-map/features/workspace/activity/ActivityLayerPanel.tsx
@@ -8,7 +8,8 @@ import {
UrlDataviewInstance,
} from '@globalfishingwatch/dataviews-client'
import { DatasetTypes } from '@globalfishingwatch/api-types'
-import { useDeckLayerLoadedState } from '@globalfishingwatch/deck-layer-composer'
+import { useGetDeckLayer } from '@globalfishingwatch/deck-layer-composer'
+import { FourwingsLayer } from '@globalfishingwatch/deck-layers'
import { useDataviewInstancesConnect } from 'features/workspace/workspace.hook'
import { selectBivariateDataviews, selectReadOnly } from 'features/app/selectors/app.selectors'
import { useLocationConnect } from 'routes/routes.hook'
@@ -23,8 +24,10 @@ import { SAR_DATAVIEW_SLUG } from 'data/workspaces'
import DatasetNotFound from 'features/workspace/shared/DatasetNotFound'
import styles from 'features/workspace/shared/LayerPanel.module.css'
import Color from 'features/workspace/common/Color'
+import { selectIsGFWUser } from 'features/user/selectors/user.selectors'
import { TrackCategory, trackEvent } from 'features/app/analytics.hooks'
import MapLegend from 'features/workspace/common/MapLegend'
+import { useActivityDataviewId } from 'features/map/map-layers.hooks'
import DatasetFilterSource from '../shared/DatasetSourceField'
import DatasetFlagField from '../shared/DatasetFlagsField'
import DatasetSchemaField from '../shared/DatasetSchemaField'
@@ -57,14 +60,15 @@ function ActivityLayerPanel({
const { deleteDataviewInstance, upsertDataviewInstance } = useDataviewInstancesConnect()
const { dispatchQueryParams } = useLocationConnect()
+ const isGFWUser = useSelector(selectIsGFWUser)
const bivariateDataviews = useSelector(selectBivariateDataviews)
const hintsDismissed = useSelector(selectHintsDismissed)
const readOnly = useSelector(selectReadOnly)
const layerActive = dataview?.config?.visible ?? true
- const layerLoadedState = useDeckLayerLoadedState()
- const layerLoaded = Object.entries(layerLoadedState).some(
- ([id, state]) => id.split(',').includes(dataview.id) && state.loaded
- )
+ const dataviewId = useActivityDataviewId(dataview)
+ const activityLayer = useGetDeckLayer(dataviewId)
+ const layerLoaded = activityLayer?.loaded
+ const layerError = activityLayer?.instance?.getError?.()
// TODO remove when final decission on stats display is taken
// const urlTimeRange = useSelector(selectUrlTimeRange)
@@ -256,10 +260,25 @@ function ActivityLayerPanel({
{!readOnly && (
)}
+ {!readOnly && layerError && (
+
+ )}
{
@@ -48,15 +44,7 @@ const MapLegendWrapper = ({
showPlaceholder?: boolean
}) => {
const { t } = useTranslation()
- // TODO: restore useTimeCompareTimeDescription and delete the component in the map folder
- const activityMergedDataviewId = useSelector(selectActivityMergedDataviewId)
- const detectionsMergedDataviewId = useSelector(selectDetectionsMergedDataviewId)
- const dataviewId =
- dataview.category === DataviewCategory.Environment
- ? dataview.id
- : dataview.category === DataviewCategory.Detections
- ? detectionsMergedDataviewId
- : activityMergedDataviewId
+ const dataviewId = useActivityDataviewId(dataview)
const deckLegend = getLegendLabelTranslated(useGetDeckLayerLegend(dataviewId))
const isBivariate = deckLegend?.type === LegendType.Bivariate
const legendSublayerIndex = deckLegend?.sublayers?.findIndex(
diff --git a/apps/fishing-map/features/workspace/environmental/EnvironmentalLayerPanel.tsx b/apps/fishing-map/features/workspace/environmental/EnvironmentalLayerPanel.tsx
index 7f17d8a749..fe52505eb8 100644
--- a/apps/fishing-map/features/workspace/environmental/EnvironmentalLayerPanel.tsx
+++ b/apps/fishing-map/features/workspace/environmental/EnvironmentalLayerPanel.tsx
@@ -1,11 +1,13 @@
import { useState, useMemo, useTransition } from 'react'
import cx from 'classnames'
import { useTranslation } from 'react-i18next'
+import { useSelector } from 'react-redux'
import { DatasetStatus, DatasetTypes } from '@globalfishingwatch/api-types'
import { Tooltip, ColorBarOption, IconButton } from '@globalfishingwatch/ui-components'
import { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client'
import { getEnvironmentalDatasetRange } from '@globalfishingwatch/datasets-client'
-import { useDeckLayerLoadedState } from '@globalfishingwatch/deck-layer-composer'
+import { useDeckLayerLoadedState, useGetDeckLayer } from '@globalfishingwatch/deck-layer-composer'
+import { FourwingsLayer } from '@globalfishingwatch/deck-layers'
import styles from 'features/workspace/shared/LayerPanel.module.css'
import { useDataviewInstancesConnect } from 'features/workspace/workspace.hook'
import ExpandedContainer from 'features/workspace/shared/ExpandedContainer'
@@ -22,6 +24,9 @@ import { getDatasetNameTranslated } from 'features/i18n/utils.datasets'
import { isBathymetryDataview } from 'features/dataviews/dataviews.utils'
import { showSchemaFilter } from 'features/workspace/common/LayerSchemaFilter'
import MapLegend from 'features/workspace/common/MapLegend'
+import { useActivityDataviewId } from 'features/map/map-layers.hooks'
+import { selectReadOnly } from 'features/app/selectors/app.selectors'
+import { selectIsGFWUser } from 'features/user/selectors/user.selectors'
import DatasetNotFound from '../shared/DatasetNotFound'
import Color from '../common/Color'
import LayerSwitch from '../common/LayerSwitch'
@@ -41,6 +46,8 @@ function EnvironmentalLayerPanel({ dataview, onToggle }: LayerPanelProps): React
const { t } = useTranslation()
const { upsertDataviewInstance } = useDataviewInstancesConnect()
const [colorOpen, setColorOpen] = useState(false)
+ const isGFWUser = useSelector(selectIsGFWUser)
+ const readOnly = useSelector(selectReadOnly)
const {
items,
attributes,
@@ -51,6 +58,9 @@ function EnvironmentalLayerPanel({ dataview, onToggle }: LayerPanelProps): React
isSorting,
activeIndex,
} = useLayerPanelDataviewSort(dataview.id)
+ const dataviewId = useActivityDataviewId(dataview)
+ const activityLayer = useGetDeckLayer(dataviewId)
+ const layerError = activityLayer?.instance?.getError?.()
const datasetFields: { field: SupportedEnvDatasetSchema; label: string }[] = useMemo(
() => [
@@ -139,7 +149,7 @@ function EnvironmentalLayerPanel({ dataview, onToggle }: LayerPanelProps): React
: false
const hasFilters = dataview.config?.filters && Object.keys(dataview.config?.filters).length > 0
const showVisibleFilterValues = showMinVisibleFilter || showMaxVisibleFilter || hasFilters
- const showSortHandler = items.length > 1
+ const showSortHandler = items.length > 1 && !readOnly
return (
)}
-
+ {!readOnly && (
+
+ )}
{showSortHandler && (
)}
+ {!readOnly && layerError && (
+
+ )}
- {/* {datasetGeometryType === 'tracks' && (
- // TODO:deck
- //
- )} */}
)}
{layerActive &&
diff --git a/apps/fishing-map/public/locales/source/translations.json b/apps/fishing-map/public/locales/source/translations.json
index c2debac217..60b992bd9e 100644
--- a/apps/fishing-map/public/locales/source/translations.json
+++ b/apps/fishing-map/public/locales/source/translations.json
@@ -440,6 +440,7 @@
"generic": "Something went wrong, try again or contact:",
"genericShort": "Something went wrong",
"invalidRange": "Min has to be lower than max value",
+ "layerLoading": "There was an error loading the layer",
"latestCarrierNotFound": "Latest carrier dataset not found",
"missingLatLng": "No latitude or longitude fields found",
"pageNotFound": "Page not found",
diff --git a/libs/deck-layers/src/layers/fourwings/FourwingsLayer.ts b/libs/deck-layers/src/layers/fourwings/FourwingsLayer.ts
index eb480bcad1..a0d8c24390 100644
--- a/libs/deck-layers/src/layers/fourwings/FourwingsLayer.ts
+++ b/libs/deck-layers/src/layers/fourwings/FourwingsLayer.ts
@@ -76,6 +76,10 @@ export class FourwingsLayer extends CompositeLayer {
+ console.warn(error.message)
+ this.setState({ error: error.message })
+ return true
+ }
+
_getColorRanges = () => {
- // TODO:deck research if we can use the context to get other layers so we can enable whiteEnd
- // TODO:deck remove this and calculate values equal to Compare
if (this.props.comparisonMode === FourwingsComparisonMode.Bivariate) {
return getBivariateRamp(this.props.sublayers.map((s) => s?.colorRamp) as ColorRampId[])
}
@@ -393,8 +402,11 @@ export class FourwingsHeatmapTileLayer extends CompositeLayer= 400 && response.status !== 404) {
+ throw new Error(response.statusText)
}
cols = parseInt(response.headers.get('X-columns') as string)
rows = parseInt(response.headers.get('X-rows') as string)
@@ -406,14 +418,15 @@ export class FourwingsHeatmapTileLayer extends CompositeLayer[]
- // TODO:deck decide what to do when a chunk load fails
const settledPromises = await Promise.allSettled(promises)
+ const hasChunkError = settledPromises.some((p) => p.status === 'rejected')
+ if (hasChunkError) {
+ throw new Error('chunk load error')
+ }
+
const arrayBuffers = settledPromises.flatMap((d) => {
return d.status === 'fulfilled' && d.value !== undefined ? d.value : []
})
- if (tile.signal?.aborted) {
- throw new Error('tile aborted')
- }
const data = await parse(arrayBuffers.filter(Boolean) as ArrayBuffer[], FourwingsLoader, {
worker: true,
@@ -473,9 +486,11 @@ export class FourwingsHeatmapTileLayer extends CompositeLayer= 400 && response.status !== 404) {
+ throw new Error(response.statusText)
}
cols = parseInt(response.headers.get('X-columns') as string)
rows = parseInt(response.headers.get('X-rows') as string)
@@ -499,14 +514,18 @@ export class FourwingsHeatmapTileLayer extends CompositeLayer[]
- // TODO:deck decide what to do when a chunk load fails
const settledPromises = await Promise.allSettled(promises)
+
+ const hasChunkError = settledPromises.some(
+ (p) => p.status === 'rejected' && p.reason.status !== 404
+ )
+ if (hasChunkError) {
+ throw new Error('chunk load error')
+ }
+
const arrayBuffers = settledPromises.flatMap((d) => {
return d.status === 'fulfilled' && d.value !== undefined ? d.value : []
})
- if (tile.signal?.aborted) {
- throw new Error('tile aborted')
- }
const data = await parse(arrayBuffers.filter(Boolean) as ArrayBuffer[], FourwingsLoader, {
worker: true,
fourwings: {
@@ -646,6 +665,7 @@ export class FourwingsHeatmapTileLayer extends CompositeLayer
export type FourwingsTileLayerState = {
+ error: string
tilesCache: FourwingsHeatmapTilesCache
colorDomain: FourwingsTileLayerColorDomain
colorRanges: FourwingsTileLayerColorRange
diff --git a/libs/deck-layers/src/layers/fourwings/positions/FourwingsPositionsTileLayer.ts b/libs/deck-layers/src/layers/fourwings/positions/FourwingsPositionsTileLayer.ts
index ab44cf07f9..0ef80690b4 100644
--- a/libs/deck-layers/src/layers/fourwings/positions/FourwingsPositionsTileLayer.ts
+++ b/libs/deck-layers/src/layers/fourwings/positions/FourwingsPositionsTileLayer.ts
@@ -55,6 +55,7 @@ import {
} from './fourwings-positions.types'
type FourwingsPositionsTileLayerState = {
+ error: string
fontLoaded: boolean
viewportDirty: boolean
viewportLoaded: boolean
@@ -90,6 +91,16 @@ export class FourwingsPositionsTileLayer extends CompositeLayer<
)
}
+ getError(): string {
+ return this.state.error
+ }
+
+ _onLayerError = (error: Error) => {
+ console.warn(error.message)
+ this.setState({ error: error.message })
+ return true
+ }
+
initializeState(context: LayerContext) {
super.initializeState(context)
let fontLoaded = true
@@ -109,6 +120,7 @@ export class FourwingsPositionsTileLayer extends CompositeLayer<
})
}
this.state = {
+ error: '',
fontLoaded,
viewportDirty: false,
viewportLoaded: false,
@@ -158,9 +170,14 @@ export class FourwingsPositionsTileLayer extends CompositeLayer<
props.sublayers?.map(({ colorRamp }) => colorRamp).join(',') !==
oldProps.sublayers?.map(({ colorRamp }) => colorRamp).join(',')
) {
- // TODO:deck split this in a separate method to avoid calculate the steps again
- // as we only need to re-calculate the colors here
- this.setState({ colorScale: this._getColorRamp(this.state.positions) })
+ if (this.state.colorScale?.colorDomain?.length) {
+ const colorRange = this.props.sublayers?.map((sublayer) =>
+ getColorRamp({ rampId: sublayer.colorRamp as any })
+ )
+ this.setState({ colorScale: { ...this.state.colorScale, colorRange } })
+ } else {
+ this.setState({ colorScale: this._getColorRamp(this.state.positions) })
+ }
}
const highlightedFeatureIds = new Set()
if (props.highlightedFeatures?.length) {
@@ -387,7 +404,6 @@ export class FourwingsPositionsTileLayer extends CompositeLayer<
sublayerProperties?.join(',')
),
}),
- // TODO:deck make chunks here to filter in the frontend instead of requesting on every change
'date-range': `${getISODateFromTS(start < end ? start : end)},${getISODateFromTS(end)}`,
}
@@ -412,6 +428,7 @@ export class FourwingsPositionsTileLayer extends CompositeLayer<
binary: false,
loaders: [GFWMVTLoader],
fetch: this._fetch,
+ onTileError: this._onLayerError,
onViewportLoad: this._onViewportLoad,
renderSubLayers: () => null,
}),
diff --git a/libs/deck-layers/src/layers/user/UserTracksLayer.ts b/libs/deck-layers/src/layers/user/UserTracksLayer.ts
index 239ac7d223..509ed7a799 100644
--- a/libs/deck-layers/src/layers/user/UserTracksLayer.ts
+++ b/libs/deck-layers/src/layers/user/UserTracksLayer.ts
@@ -182,18 +182,6 @@ export class UserTracksLayer extends CompositeLayer {
- // const featureIndex = this.state.rawDataIndexes.find(({ length }) => index < length)?.index!
- // const color =
- // this.state.rawData?.features?.[featureIndex]?.properties?.color || this.props.color
- // return segment.map((s) => ({ ...s, color }))
- // })
}
_getColorByLineIndex = (_: any, { index }: { index: number }) => {