Skip to content

Commit

Permalink
fitBounds vessel group extent in profile
Browse files Browse the repository at this point in the history
  • Loading branch information
j8seangel committed Sep 24, 2024
1 parent d57c9d1 commit fd97648
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 29 deletions.
20 changes: 6 additions & 14 deletions apps/fishing-map/features/reports/activity/ReportActivityGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,14 @@ import {
useReportFeaturesLoading,
useReportFilteredTimeSeries,
} from 'features/reports/activity/reports-activity-timeseries.hooks'
import {
selectReportArea,
selectTimeComparisonValues,
} from 'features/reports/areas/reports.selectors'
import { selectTimeComparisonValues } from 'features/reports/areas/reports.selectors'
import ReportActivityPlaceholder from 'features/reports/areas/placeholders/ReportActivityPlaceholder'
import ReportActivityPeriodComparison from 'features/reports/activity/ReportActivityPeriodComparison'
import ReportActivityPeriodComparisonGraph from 'features/reports/activity/ReportActivityPeriodComparisonGraph'
import UserGuideLink from 'features/help/UserGuideLink'
import { AsyncReducerStatus } from 'utils/async-slice'
import { useFitAreaInViewport, useReportAreaBounds } from 'features/reports/areas/reports.hooks'
import { selectReportActivityGraph } from '../areas/reports.config.selectors'
import { ReportActivityGraph } from '../areas/reports.types'
import { useFetchReportArea, useFitAreaInViewport } from '../areas/reports.hooks'
import ReportActivityEvolution from './ReportActivityEvolution'
import ReportActivityBeforeAfter from './ReportActivityBeforeAfter'
import ReportActivityBeforeAfterGraph from './ReportActivityBeforeAfterGraph'
Expand All @@ -48,21 +44,17 @@ const emptyGraphData = {} as ReportGraphProps

export default function ReportActivity() {
useComputeReportTimeSeries()
const reportArea = useSelector(selectReportArea)
const { status } = useFetchReportArea()

const fitAreaInViewport = useFitAreaInViewport()
const { loaded, bbox } = useReportAreaBounds()

// This ensures that the area is in viewport when then area load finishes
useEffect(() => {
if (status === AsyncReducerStatus.Finished && reportArea?.bounds) {
requestAnimationFrame(() => {
fitAreaInViewport()
})
if (loaded && bbox?.length) {
fitAreaInViewport()
}
// Reacting only to the area status and fitting bounds after load
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [status, reportArea])
}, [loaded])

const { t } = useTranslation()
const { start, end } = useTimerangeConnect()
Expand Down
48 changes: 43 additions & 5 deletions apps/fishing-map/features/reports/areas/reports.hooks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo } from 'react'
import { useSelector } from 'react-redux'
import { useGetStatsByDataviewQuery } from 'queries/stats-api'
import { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client'
import { Dataset, Dataview } from '@globalfishingwatch/api-types'
import { useLocalStorage } from '@globalfishingwatch/react-hooks'
Expand All @@ -21,6 +22,7 @@ import {
selectReportArea,
selectReportAreaDataviews,
selectReportAreaIds,
selectReportAreaStatus,
selectReportDataviewsWithPermissions,
} from 'features/reports/areas/reports.selectors'
import { useDeckMap } from 'features/map/map-context.hooks'
Expand All @@ -30,7 +32,7 @@ import { FIT_BOUNDS_REPORT_PADDING } from 'data/config'
import { RFMO_DATAVIEW_SLUG } from 'data/workspaces'
import { getMapCoordinatesFromBounds } from 'features/map/map-bounds.hooks'
import { LAST_REPORTS_STORAGE_KEY, LastReportStorage } from 'features/reports/areas/reports.config'
import { selectIsVesselGroupReportLocation } from 'routes/routes.selectors'
import { selectIsVesselGroupReportLocation, selectUrlTimeRange } from 'routes/routes.selectors'
import { AsyncReducerStatus } from 'utils/async-slice'
import {
fetchReportVesselsThunk,
Expand Down Expand Up @@ -79,10 +81,47 @@ function useReportAreaCenter(bounds?: Bbox) {
}, [bounds, map])
}

function useVesselGroupReportBounds() {
const isVesselGroupReportLocation = useSelector(selectIsVesselGroupReportLocation)
const dataview = useSelector(selectVGRActivityDataview)!
const urlTimeRange = useSelector(selectUrlTimeRange)
const {
data: stats,
isFetching,
isSuccess,
} = useGetStatsByDataviewQuery(
{
dataview,
timerange: urlTimeRange as any,
fields: [],
},
{
skip: !isVesselGroupReportLocation || !dataview || !urlTimeRange,
}
)

const statsBbox = stats && ([stats.minLon, stats.minLat, stats.maxLon, stats.maxLat] as Bbox)
return {
loaded: !isFetching && isSuccess,
bbox: statsBbox?.some((v) => v === null || v === undefined) ? null : statsBbox!,
}
}

export function useReportAreaBounds() {
const isVesselGroupReportLocation = useSelector(selectIsVesselGroupReportLocation)
const { loaded, bbox } = useVesselGroupReportBounds()
const area = useSelector(selectReportArea)
const areaStatus = useSelector(selectReportAreaStatus)
const areaBbox = isVesselGroupReportLocation ? bbox : area?.geometry?.bbox || area!?.bounds
return {
loaded: isVesselGroupReportLocation ? loaded : areaStatus === AsyncReducerStatus.Finished,
bbox: areaBbox,
}
}

export function useReportAreaInViewport() {
const viewState = useMapViewState()
const area = useSelector(selectReportArea)
const bbox = area?.geometry?.bbox || area!?.bounds
const { bbox } = useReportAreaBounds()
const areaCenter = useReportAreaCenter(bbox as Bbox)
return (
viewState?.latitude === areaCenter?.latitude &&
Expand All @@ -93,8 +132,7 @@ export function useReportAreaInViewport() {

export function useFitAreaInViewport() {
const setMapCoordinates = useSetMapCoordinates()
const area = useSelector(selectReportArea)
const bbox = area?.geometry?.bbox || area!?.bounds
const { bbox } = useReportAreaBounds()
const areaCenter = useReportAreaCenter(bbox as Bbox)
const areaInViewport = useReportAreaInViewport()
return useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ import { useLocationConnect } from 'routes/routes.hook'
import { VGRActivitySubsection } from 'features/vessel-groups/vessel-groups.types'
import { selectVGRActivitySubsection } from 'features/reports/vessel-groups/vessel-group.config.selectors'
import { useReportFeaturesLoading } from 'features/reports/activity/reports-activity-timeseries.hooks'
import { useFitAreaInViewport } from 'features/reports/areas/reports.hooks'
import { resetReportData } from 'features/reports/activity/reports-activity.slice'
import { useAppDispatch } from 'features/app/app.hooks'

function VGRActivitySubsectionSelector() {
const { t } = useTranslation()
const dispatch = useAppDispatch()
const { dispatchQueryParams } = useLocationConnect()
const subsection = useSelector(selectVGRActivitySubsection)
const loading = useReportFeaturesLoading()
const fitAreaInViewport = useFitAreaInViewport()
const options: ChoiceOption<VGRActivitySubsection>[] = [
{
id: 'fishing-effort',
Expand All @@ -31,6 +36,8 @@ function VGRActivitySubsectionSelector() {
// action: `Click on ${option.id} activity graph`,
// })
dispatchQueryParams({ vGRActivitySubsection: option.id })
fitAreaInViewport()
dispatch(resetReportData())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
VesselGroupInsightParams,
} from 'queries/vessel-insight-api'
import { RootState } from 'reducers'
import { InsightType } from '@globalfishingwatch/api-types'
import { DataviewCategory, InsightType } from '@globalfishingwatch/api-types'
import { selectDataviewInstancesResolvedVisible } from 'features/dataviews/selectors/dataviews.instances.selectors'
import { selectReportVesselGroupId } from 'routes/routes.selectors'
import { selectTimeRange } from 'features/app/selectors/app.timebar.selectors'
Expand All @@ -28,13 +28,21 @@ export const IUU_INSIGHT_ID = 'VESSEL-IDENTITY-IUU-VESSEL-LIST' as InsightType
export const FLAG_CHANGE_INSIGHT_ID = 'VESSEL-IDENTITY-FLAG-CHANGES' as InsightType
export const MOU_INSIGHT_ID = 'VESSEL-IDENTITY-MOU-LIST' as InsightType

export const selectVGRDataview = createSelector(
export const selectAllVGRDataviews = createSelector(
[selectDataviewInstancesResolvedVisible, selectReportVesselGroupId],
(dataviews, reportVesselGroupId) => {
return dataviews?.find((dataview) => dataviewHasVesselGroupId(dataview, reportVesselGroupId))
return dataviews?.filter((dataview) => dataviewHasVesselGroupId(dataview, reportVesselGroupId))
}
)

export const selectVGRDataview = createSelector([selectAllVGRDataviews], (dataviews) => {
return dataviews?.find((dataview) => dataview.category === DataviewCategory.VesselGroups)
})

export const selectVGRActivityDataview = createSelector([selectAllVGRDataviews], (dataviews) => {
return dataviews?.find((dataview) => dataview.category === DataviewCategory.Activity)
})

export const selectVGREventsSubsectionDataview = createSelector(
[
selectEventsDataviews,
Expand Down
21 changes: 14 additions & 7 deletions apps/fishing-map/queries/stats-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { uniq } from 'es-toolkit'
import { DateTime } from 'luxon'
import type { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client'
import { getDatasetsExtent } from '@globalfishingwatch/datasets-client'
import { StatField, StatFields, StatType, StatsParams } from '@globalfishingwatch/api-types'
import { StatFields, StatType, StatsParams } from '@globalfishingwatch/api-types'
import type { TimeRange } from 'features/timebar/timebar.slice'

type FetchDataviewStatsParams = {
timerange: TimeRange
dataview: UrlDataviewInstance
fields?: StatField[]
fields?: StatsParams[]
}

interface CustomBaseQueryArg extends FetchBaseQueryArgs {
Expand All @@ -26,7 +26,6 @@ const serializeStatsDataviewKey: SerializeQueryArgs<CustomBaseQueryArg> = ({ que
].join('-')
}

const DEFAULT_STATS_FIELDS: StatsParams[] = ['VESSEL-IDS', 'FLAGS']
// Define a service using a base URL and expected endpoints
export const dataviewStatsApi = createApi({
reducerPath: 'dataviewStatsApi',
Expand All @@ -36,7 +35,7 @@ export const dataviewStatsApi = createApi({
endpoints: (builder) => ({
getStatsByDataview: builder.query<StatFields, FetchDataviewStatsParams>({
serializeQueryArgs: serializeStatsDataviewKey,
query: ({ dataview, timerange, fields = DEFAULT_STATS_FIELDS }) => {
query: ({ dataview, timerange, fields }) => {
const datasets = dataview.datasets?.filter((dataset) =>
dataview.config?.datasets?.includes(dataset.id)
)
Expand All @@ -52,17 +51,25 @@ export const dataviewStatsApi = createApi({
)
const statsParams = {
datasets: [dataview.config?.datasets?.join(',') || []],
filters: [dataview.config?.filter] || [],
'vessel-groups': [dataview.config?.['vessel-groups']] || [],
'date-range': [laterStartDate.toISODate(), earlierEndDate.toISODate()].join(','),
...(fields?.length && {
fields: fields.join(','),
}),
...(dataview.config?.filter && { filters: [dataview.config?.filter] || [] }),
...(dataview.config?.['vessel-groups'] && {
'vessel-groups': [dataview.config?.['vessel-groups']] || [],
}),
}
return {
url: `?fields=${fields.join(',')}&${stringify(statsParams, {
url: `?${stringify(statsParams, {
arrayFormat: 'indices',
})}`,
}
},
transformResponse: (response: StatFields[], meta, args) => {
if (!args.fields?.length) {
return response?.[0]
}
const units = uniq(args?.dataview?.datasets?.flatMap((d) => d.unit || []) || [])
if (units.length > 1) {
console.warn('Incompatible datasets stats unit, using the first type', units[0])
Expand Down

0 comments on commit fd97648

Please sign in to comment.