Skip to content

Commit

Permalink
show fourwings chunk error in layer panels
Browse files Browse the repository at this point in the history
  • Loading branch information
j8seangel committed Aug 5, 2024
1 parent c2f6476 commit 74a62be
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 39 deletions.
19 changes: 18 additions & 1 deletion apps/fishing-map/features/map/map-layers.hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '@globalfishingwatch/deck-layer-composer'
import { GFWAPI } from '@globalfishingwatch/api-client'
import { FourwingsLayer, HEATMAP_ID } from '@globalfishingwatch/deck-layers'
import { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client'
import { AsyncReducerStatus } from 'utils/async-slice'
import {
selectWorkspaceStatus,
Expand All @@ -22,7 +23,11 @@ import {
selectIsUserLocation,
selectIsWorkspaceLocation,
} from 'routes/routes.selectors'
import { selectDataviewInstancesResolvedVisible } from 'features/dataviews/selectors/dataviews.selectors'
import {
selectActivityMergedDataviewId,
selectDataviewInstancesResolvedVisible,
selectDetectionsMergedDataviewId,
} from 'features/dataviews/selectors/dataviews.selectors'
import {
selectBivariateDataviews,
selectActivityVisualizationMode,
Expand All @@ -48,6 +53,18 @@ import { useDrawLayerInstance } from './overlays/draw/draw.hooks'
import { useMapViewState } from './map-viewport.hooks'
import { selectClickedEvent } from './map.slice'

export const useActivityDataviewId = (dataview: UrlDataviewInstance) => {
const activityMergedDataviewId = useSelector(selectActivityMergedDataviewId)
const detectionsMergedDataviewId = useSelector(selectDetectionsMergedDataviewId)
const dataviewId =
dataview.category === DataviewCategory.Environment
? dataview.id
: dataview.category === DataviewCategory.Detections
? detectionsMergedDataviewId
: activityMergedDataviewId
return dataviewId
}

export const useGlobalConfigConnect = () => {
const { start, end } = useTimerangeConnect()
const timebarHighlightedTime = useSelector(selectHighlightedTime)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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'
Expand Down Expand Up @@ -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<FourwingsLayer>(dataviewId)
const layerLoaded = activityLayer?.loaded
const layerError = activityLayer?.instance?.getError?.()

// TODO remove when final decission on stats display is taken
// const urlTimeRange = useSelector(selectUrlTimeRange)
Expand Down Expand Up @@ -256,10 +260,25 @@ function ActivityLayerPanel({
{!readOnly && (
<Remove onClick={onRemoveLayerClick} loading={layerActive && !layerLoaded} />
)}
{!readOnly && layerError && (
<IconButton
icon={'warning'}
type={'warning'}
tooltip={
isGFWUser
? `${t(
'errors.layerLoading',
'There was an error loading the layer'
)} (${layerError})`
: t('errors.layerLoading', 'There was an error loading the layer')
}
size="small"
/>
)}
</div>
<IconButton
icon={layerActive ? 'more' : undefined}
type="default"
icon={layerError ? 'warning' : layerActive ? 'more' : undefined}
type={layerError ? 'warning' : 'default'}
loading={layerActive && !layerLoaded}
className={cx('print-hidden', styles.shownUntilHovered)}
size="small"
Expand Down
16 changes: 2 additions & 14 deletions apps/fishing-map/features/workspace/common/MapLegend.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import cx from 'classnames'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { LegendType, MapLegend, Tooltip, UILegend } from '@globalfishingwatch/ui-components'
import { DataviewCategory } from '@globalfishingwatch/api-types'
import { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client'
import { DeckLegendAtom, useGetDeckLayerLegend } from '@globalfishingwatch/deck-layer-composer'
import {
selectActivityMergedDataviewId,
selectDetectionsMergedDataviewId,
} from 'features/dataviews/selectors/dataviews.selectors'
import { formatI18nNumber } from 'features/i18n/i18nNumber'
import { t } from 'features/i18n/i18n'
import MapLegendPlaceholder from 'features/workspace/common/MapLegendPlaceholder'
import { useActivityDataviewId } from 'features/map/map-layers.hooks'
import styles from './MapLegend.module.css'

const getLegendLabelTranslated = (legend?: DeckLegendAtom, tFn = t) => {
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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'
Expand All @@ -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,
Expand All @@ -51,6 +58,9 @@ function EnvironmentalLayerPanel({ dataview, onToggle }: LayerPanelProps): React
isSorting,
activeIndex,
} = useLayerPanelDataviewSort(dataview.id)
const dataviewId = useActivityDataviewId(dataview)
const activityLayer = useGetDeckLayer<FourwingsLayer>(dataviewId)
const layerError = activityLayer?.instance?.getError?.()

const datasetFields: { field: SupportedEnvDatasetSchema; label: string }[] = useMemo(
() => [
Expand Down Expand Up @@ -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 (
<div
Expand Down Expand Up @@ -207,7 +217,9 @@ function EnvironmentalLayerPanel({ dataview, onToggle }: LayerPanelProps): React
/>
)}
<InfoModal dataview={dataview} />
<Remove dataview={dataview} loading={!showSortHandler && layerActive && !layerLoaded} />
{!readOnly && (
<Remove dataview={dataview} loading={!showSortHandler && layerActive && !layerLoaded} />
)}
{showSortHandler && (
<IconButton
size="small"
Expand All @@ -218,10 +230,25 @@ function EnvironmentalLayerPanel({ dataview, onToggle }: LayerPanelProps): React
className={styles.dragger}
/>
)}
{!readOnly && layerError && (
<IconButton
icon={'warning'}
type={'warning'}
tooltip={
isGFWUser
? `${t(
'errors.layerLoading',
'There was an error loading the layer'
)} (${layerError})`
: t('errors.layerLoading', 'There was an error loading the layer')
}
size="small"
/>
)}
</div>
<IconButton
icon={layerActive ? 'more' : undefined}
type="default"
icon={layerError ? 'warning' : layerActive ? 'more' : undefined}
type={layerError ? 'warning' : 'default'}
loading={layerActive && !layerLoaded}
className={cx('print-hidden', styles.shownUntilHovered)}
size="small"
Expand Down
1 change: 1 addition & 0 deletions apps/fishing-map/public/locales/source/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions libs/deck-layers/src/layers/fourwings/FourwingsLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ export class FourwingsLayer extends CompositeLayer<FourwingsLayerProps & TileLay
return this.getLayer()?.getData()
}

getError() {
return this.getLayer()?.getError()
}

getIsPositionsAvailable() {
if (this.props.visualizationMode?.includes(HEATMAP_ID)) {
const heatmapLayer = this.getLayer() as FourwingsHeatmapTileLayer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export class FourwingsHeatmapTileLayer extends CompositeLayer<FourwingsHeatmapTi
initializeState(context: LayerContext) {
super.initializeState(context)
this.state = {
error: '',
scales: [],
tilesCache: this._getTileDataCache({
startTime: this.props.startTime,
Expand All @@ -104,6 +105,16 @@ export class FourwingsHeatmapTileLayer extends CompositeLayer<FourwingsHeatmapTi
return super.isLoaded && !this.state.rampDirty
}

getError(): string {
return this.state.error
}

_onLayerError = (error: Error) => {
console.warn(error.message)
this.setState({ error: error.message })
return true
}

_getColorRanges = () => {
if (this.props.comparisonMode === FourwingsComparisonMode.Bivariate) {
return getBivariateRamp(this.props.sublayers.map((s) => s?.colorRamp) as ColorRampId[])
Expand Down Expand Up @@ -391,8 +402,11 @@ export class FourwingsHeatmapTileLayer extends CompositeLayer<FourwingsHeatmapTi
signal: tile.signal,
responseType: 'default',
})
if (tile.signal?.aborted || response.status !== 200) {
throw new Error()
if (tile.signal?.aborted) {
throw new Error('aborted')
}
if (response.status >= 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)
Expand All @@ -404,14 +418,15 @@ export class FourwingsHeatmapTileLayer extends CompositeLayer<FourwingsHeatmapTi
}

const promises = sublayers.map(getSublayerData) as Promise<ArrayBuffer>[]
// 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,
Expand Down Expand Up @@ -471,8 +486,11 @@ export class FourwingsHeatmapTileLayer extends CompositeLayer<FourwingsHeatmapTi
signal: tile.signal,
responseType: 'default',
})
if (tile.signal?.aborted || response.status !== 200) {
throw new Error()
if (tile.signal?.aborted) {
throw new Error('aborted')
}
if (response.status >= 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)
Expand All @@ -496,14 +514,18 @@ export class FourwingsHeatmapTileLayer extends CompositeLayer<FourwingsHeatmapTi
}

const promises = visibleSublayers.map(getSublayerData) as Promise<ArrayBuffer>[]
// 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: {
Expand Down Expand Up @@ -643,6 +665,7 @@ export class FourwingsHeatmapTileLayer extends CompositeLayer<FourwingsHeatmapTi
tilesCache,
scales,
minZoom: 0,
onTileError: this._onLayerError,
maxZoom: FOURWINGS_MAX_ZOOM,
zoomOffset: getZoomOffsetByResolution(resolution!, zoom),
opacity: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export type FourwingsHeatmapTilesCache = {

export type FourwinsTileLayerScale = ScaleLinear<FourwingsColorObject, FourwingsColorObject, never>
export type FourwingsTileLayerState = {
error: string
tilesCache: FourwingsHeatmapTilesCache
colorDomain: FourwingsTileLayerColorDomain
colorRanges: FourwingsTileLayerColorRange
Expand Down
Loading

0 comments on commit 74a62be

Please sign in to comment.