Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Responsive-visualization/core #2988

Merged
merged 65 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
1e1bb59
initial visualization library commit
j8seangel Jan 8, 2025
844e10c
use responsive-visualization in vessels list graph
j8seangel Jan 8, 2025
669f7c7
individual tooltip component
j8seangel Jan 9, 2025
0b084b1
support dataKey and labelKey and improve typings
j8seangel Jan 9, 2025
627b0f7
extract reusable logic from BarChart to hooks
j8seangel Jan 9, 2025
895912e
wip: timeseries aggregated graph
j8seangel Jan 9, 2025
a0fbe62
individual timeseries points
j8seangel Jan 10, 2025
f79a22b
align timeseries points
j8seangel Jan 10, 2025
7ebf76e
fix deck core version
satellitestudiodesign Jan 10, 2025
b8c5a7a
fix timeseries layout
satellitestudiodesign Jan 10, 2025
9c6aed7
fix vessel group vessels graph labels
satellitestudiodesign Jan 10, 2025
d6c00c1
fix tooltip overflow
satellitestudiodesign Jan 10, 2025
83bf263
enhance TimeseriesIndividual single time data representation
satellitestudiodesign Jan 10, 2025
91a9000
use local containerRef instead of passing it as a prop
satellitestudiodesign Jan 13, 2025
0396787
use in vessel-groups coverage insight
satellitestudiodesign Jan 13, 2025
4c93694
reuse vessel grouping
satellitestudiodesign Jan 13, 2025
f23b25a
match covegare opacity in individual graph
satellitestudiodesign Jan 13, 2025
c8710ea
fix crashes and warnings
satellitestudiodesign Jan 13, 2025
70df1bf
make opacity change only the list
satellitestudiodesign Jan 13, 2025
a8627a9
individual point events
j8seangel Jan 13, 2025
344d962
fix typing
j8seangel Jan 13, 2025
69a2ce0
better typings
j8seangel Jan 13, 2025
20b1102
add validation for MAX_INDIVIDUAL_ITEMS
j8seangel Jan 13, 2025
2f7882c
ensure isIndividualSupported when received individualData
j8seangel Jan 14, 2025
8e01b3e
add includes in individual data requests
j8seangel Jan 14, 2025
2ee6fb9
responsive visualiztion placeholders
j8seangel Jan 14, 2025
e23b163
adjust density calculation
satellitestudiodesign Jan 14, 2025
49e84de
include individual items filters in request
j8seangel Jan 14, 2025
2d5d3db
refactor dates parse fn
j8seangel Jan 14, 2025
31b3c92
fix timeseries in hour
j8seangel Jan 14, 2025
ad22fc1
render individual points in vessel group events vessels
j8seangel Jan 14, 2025
18f13c4
render individual points in port report events vessels
j8seangel Jan 14, 2025
e74da3e
use event icons in individual timeseries
satellitestudiodesign Jan 14, 2025
0fd97db
vessel tooltip
satellitestudiodesign Jan 14, 2025
0802a68
encounter tooltip
satellitestudiodesign Jan 14, 2025
90a92db
Merge branch 'develop' into responsive-visualization/core
j8seangel Jan 14, 2025
f625c64
fix build
j8seangel Jan 14, 2025
5bb0e00
calculate pointSize based on space
j8seangel Jan 14, 2025
8d6fb9c
render 1 layer of activity graph data in responsive visualization
j8seangel Jan 15, 2025
420b759
loitering and port visit tooltips
satellitestudiodesign Jan 15, 2025
e0a029d
vessel click interaction
satellitestudiodesign Jan 15, 2025
6308f12
fix build
satellitestudiodesign Jan 15, 2025
a0de0d6
support barchar values as number or object
j8seangel Jan 15, 2025
1c74734
support stacked responsive bar chart
j8seangel Jan 16, 2025
9a659c6
Merge branch 'develop' into responsive-visualization/core
j8seangel Jan 16, 2025
7a23cee
run lint in responsive-visualization
j8seangel Jan 16, 2025
e4a3289
render individual point with its color for vessels in activity tab
j8seangel Jan 16, 2025
c89b4e1
fix labels for stacks of more than 2 bars
satellitestudiodesign Jan 16, 2025
d782335
sort bars
satellitestudiodesign Jan 16, 2025
b1c0017
sort individual data in stacked bars
satellitestudiodesign Jan 16, 2025
e4c72b7
fix MMSI in area report vessels tooltip
satellitestudiodesign Jan 16, 2025
6f7abf8
make mmsi a fallback of ssvid
satellitestudiodesign Jan 16, 2025
ee1588f
avoid unneded memoize
j8seangel Jan 16, 2025
a73cd12
fix build
j8seangel Jan 16, 2025
366d998
fix column sorting
satellitestudiodesign Jan 16, 2025
47e9d55
reove animation
satellitestudiodesign Jan 16, 2025
5605cfd
fix info tooltip in Other bar
satellitestudiodesign Jan 16, 2025
9501ead
fixes and TODOs
satellitestudiodesign Jan 16, 2025
14588ed
handle if valueKeys not array
j8seangel Jan 16, 2025
c6c5e40
fix details
j8seangel Jan 16, 2025
ee83117
fix vms vessel links
satellitestudiodesign Jan 17, 2025
98d2673
fix onClick crash
satellitestudiodesign Jan 17, 2025
3d62425
fix individual shipType grouping
satellitestudiodesign Jan 17, 2025
924a4ee
disable individual data
j8seangel Jan 24, 2025
6ee1e78
Merge branch 'develop' into responsive-visualization/core
j8seangel Jan 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/fishing-map/features/i18n/i18nDate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import type { DateTimeFormatOptions } from 'luxon'
import { DateTime } from 'luxon'
import type { Locale } from 'types'

import type { SupportedDateType } from 'utils/dates'
import type { SupportedDateType } from '@globalfishingwatch/data-transforms'

import { getUTCDateTime } from 'utils/dates'

import i18n from './i18n'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const EMPTY_ARRAY: [] = []
type ReportVesselWithMeta = ReportVessel & {
// Merging detections or hours depending on the activity unit into the same property
value: number
sourceColor: string
color: string
activityDatasetId: string
category: ReportCategory
dataviewId: string
Expand All @@ -58,7 +58,7 @@ type ReportVesselWithMeta = ReportVessel & {

export type ReportVesselWithDatasets = Pick<ReportVessel, 'vesselId' | 'shipName'> &
Partial<ReportVessel> &
Pick<ReportVesselWithMeta, 'sourceColor' | 'value'> & {
Pick<ReportVesselWithMeta, 'color' | 'value'> & {
infoDataset?: Dataset
trackDataset?: Dataset
dataviewId?: string
Expand Down Expand Up @@ -140,8 +140,8 @@ export const selectReportActivityFlatten = createSelector(
: vessel.shipName,
activityDatasetId: datasetId,
dataviewId: dataview?.id,
color: dataview?.config?.color,
category: getReportCategoryFromDataview(dataview),
sourceColor: dataview?.config?.color,
} as ReportVesselWithMeta
})
})
Expand Down
10 changes: 10 additions & 0 deletions apps/fishing-map/features/reports/ports/PortsReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import parse from 'html-react-parser'
import { DateTime } from 'luxon'
import { useGetReportEventsStatsQuery } from 'queries/report-events-stats-api'

import { getDataviewFilters } from '@globalfishingwatch/dataviews-client'
import { Button } from '@globalfishingwatch/ui-components'

import EventsEmptyState from 'assets/images/[email protected]'
Expand Down Expand Up @@ -41,6 +42,7 @@ import { useFetchPortsReport } from './ports-report.hooks'
import {
selectPortReportsDataview,
selectPortReportVesselsGrouped,
selectPortReportVesselsIndividualData,
selectPortReportVesselsPaginated,
selectPortReportVesselsPagination,
} from './ports-report.selectors'
Expand Down Expand Up @@ -72,6 +74,7 @@ function PortsReport() {
const portsReportData = useSelector(selectPortsReportData)
const portsReportDataStatus = useSelector(selectPortsReportStatus)
const portsReportVesselsGrouped = useSelector(selectPortReportVesselsGrouped)
const portReportIndividualData = useSelector(selectPortReportVesselsIndividualData)
const portsReportVesselsPaginated = useSelector(selectPortReportVesselsPaginated)
const {
data,
Expand Down Expand Up @@ -162,6 +165,12 @@ function PortsReport() {
color={color}
start={start}
end={end}
filters={{
portId,
...(dataview && { ...getDataviewFilters(dataview) }),
}}
includes={['id', 'start', 'end', 'vessel']}
datasetId={datasetId}
timeseries={data.timeseries || []}
/>
)}
Expand Down Expand Up @@ -222,6 +231,7 @@ function PortsReport() {
</div>
<EventsReportVesselsGraph
data={portsReportVesselsGrouped}
individualData={portReportIndividualData}
color={color}
property={portReportVesselsProperty}
filterQueryParam="portsReportVesselsFilter"
Expand Down
15 changes: 13 additions & 2 deletions apps/fishing-map/features/reports/ports/ports-report.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import { MAX_CATEGORIES } from 'features/reports/areas/area-reports.config'
import { getVesselsFiltered } from 'features/reports/areas/area-reports.utils'
import { EMPTY_FIELD_PLACEHOLDER } from 'utils/info'

import { OTHER_CATEGORY_LABEL } from '../vessel-groups/vessel-group-report.config'
import { REPORT_FILTER_PROPERTIES } from '../vessel-groups/vessels/vessel-group-report-vessels.selectors'
import { getVesselIndividualGroupedData } from '../shared/reports.utils'
import {
OTHER_CATEGORY_LABEL,
REPORT_FILTER_PROPERTIES,
} from '../vessel-groups/vessel-group-report.config'

import {
selectPortReportVesselsFilter,
Expand Down Expand Up @@ -119,6 +122,14 @@ export const selectPortReportVesselsGrouped = createSelector(
}
)

export const selectPortReportVesselsIndividualData = createSelector(
[selectPortReportVesselsFiltered, selectPortReportVesselsProperty],
(vessels, groupBy) => {
if (!vessels || !groupBy) return []
return getVesselIndividualGroupedData(vessels, groupBy)
}
)

export const selectPortReportVesselsPaginated = createSelector(
[
selectPortReportVesselsFiltered,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.hiddenLink {
width: 100%;
height: 100%;
}
25 changes: 25 additions & 0 deletions apps/fishing-map/features/reports/shared/VesselGraphLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { ReportVesselWithDatasets } from 'features/reports/areas/area-reports.selectors'
import type { VesselGroupVesselTableParsed } from 'features/reports/vessel-groups/vessels/vessel-group-report-vessels.selectors'
import VesselLink from 'features/vessel/VesselLink'

import styles from './VesselGraphLink.module.css'

export default function VesselGraphLink({
data,
}: {
data?: VesselGroupVesselTableParsed | ReportVesselWithDatasets
}) {
if (!data) {
return null
}
const { vesselId, dataset } = data
const datasetId = dataset || (data as ReportVesselWithDatasets).infoDataset?.id
return (
<VesselLink
className={styles.hiddenLink}
vesselId={vesselId}
datasetId={datasetId}
showTooltip={false}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default function ReportActivity() {
if (loaded && bbox?.length) {
fitAreaInViewport()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loaded, bboxHash])

const { t } = useTranslation()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React, { Fragment } from 'react'
import React, { Fragment, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import cx from 'classnames'
import { Bar, BarChart, LabelList,ResponsiveContainer, Tooltip, XAxis } from 'recharts'
import type { CategoricalChartFunc } from 'recharts/types/chart/generateCategoricalChart'

import {
getResponsiveVisualizationItemValue,
ResponsiveBarChart,
} from '@globalfishingwatch/responsive-visualizations'
import { Tooltip as GFWTooltip } from '@globalfishingwatch/ui-components'

import {
Expand All @@ -15,18 +18,21 @@ import {
import { selectReportVesselGraph } from 'features/app/selectors/app.reports.selector'
import I18nNumber, { formatI18nNumber } from 'features/i18n/i18nNumber'
import { EMPTY_API_VALUES, OTHERS_CATEGORY_LABEL } from 'features/reports/areas/area-reports.config'
import { selectReportDataviewsWithPermissions } from 'features/reports/areas/area-reports.selectors'
import type { ReportVesselGraph } from 'features/reports/areas/area-reports.types'
import {
REPORT_GRAPH_LABEL_KEY,
selectReportVesselsGraphDataGrouped,
selectReportVesselsGraphDataKeys,
selectReportVesselsGraphDataOthers,
selectReportVesselsGraphIndividualData,
} from 'features/reports/shared/activity/vessels/report-activity-vessels.selectors'
import { cleanFlagState } from 'features/reports/shared/activity/vessels/report-activity-vessels.utils'
import { ReportBarGraphPlaceholder } from 'features/reports/shared/placeholders/ReportBarGraphPlaceholder'
import VesselGraphLink from 'features/reports/shared/VesselGraphLink'
import VesselGroupReportVesselsIndividualTooltip from 'features/reports/vessel-groups/vessels/VesselGroupReportVesselsIndividualTooltip'
import { useLocationConnect } from 'routes/routes.hook'
import { getVesselGearTypeLabel } from 'utils/info'

import { ReportBarGraphPlaceholder } from '../../placeholders/ReportBarGraphPlaceholder'

import styles from './ReportVesselsGraph.module.css'

const MAX_OTHER_TOOLTIP_ITEMS = 10
Expand Down Expand Up @@ -101,7 +107,7 @@ const CustomTick = (props: any) => {
? cleanFlagState(
(
othersData?.flatMap((d) =>
EMPTY_API_VALUES.includes(d.name) ? [] : getTickLabel(d.name)
EMPTY_API_VALUES.includes(d.name as string) ? [] : getTickLabel(d.name as string)
) || []
).join('|')
)
Expand All @@ -115,9 +121,13 @@ const CustomTick = (props: any) => {

const tooltip = isOtherCategory ? (
<ul>
{othersData?.slice(0, MAX_OTHER_TOOLTIP_ITEMS).map(({ name, value }) => (
<li key={`${name}-${value}`}>{`${getTickLabel(name)}: ${value}`}</li>
))}
{othersData
?.slice(0, MAX_OTHER_TOOLTIP_ITEMS)
.map(({ name, value }) => (
<li
key={`${name}-${value}`}
>{`${getTickLabel(name)}: ${getResponsiveVisualizationItemValue(value)}`}</li>
))}
{othersData && othersData.length > MAX_OTHER_TOOLTIP_ITEMS && (
<li>
+ {othersData.length - MAX_OTHER_TOOLTIP_ITEMS} {t('analysis.others', 'Others')}
Expand Down Expand Up @@ -165,8 +175,9 @@ const CustomTick = (props: any) => {
}

export default function ReportVesselsGraph() {
const dataviews = useSelector(selectReportDataviewsWithPermissions)
const valueKeys = useSelector(selectReportVesselsGraphDataKeys)
const data = useSelector(selectReportVesselsGraphDataGrouped)
const individualData = useSelector(selectReportVesselsGraphIndividualData)
const selectedReportVesselGraph = useSelector(selectReportVesselGraph)
const othersData = useSelector(selectReportVesselsGraphDataOthers)
const { dispatchQueryParams } = useLocationConnect()
Expand All @@ -186,6 +197,7 @@ export default function ReportVesselsGraph() {
return label
}
}
// TODO: add this interaction
const onLabelClick: CategoricalChartFunc = (e) => {
const { payload } = e.activePayload?.[0] || {}
if (!payload) return
Expand All @@ -195,7 +207,7 @@ export default function ReportVesselsGraph() {
? cleanFlagState(
(
othersData?.flatMap((d) =>
EMPTY_API_VALUES.includes(d.name) ? [] : getTickLabel(d.name)
EMPTY_API_VALUES.includes(d.name as string) ? [] : getTickLabel(d.name as string)
) || []
).join('|')
)
Expand All @@ -206,56 +218,41 @@ export default function ReportVesselsGraph() {
})
}
}

// const getIndividualData = useCallback(async () => {
// return individualData
// }, [individualData])

const getAggregatedData = useCallback(async () => {
return data as any[]
}, [data])

if (!data) {
return (
<div className={styles.graph} data-test="activity-report-vessels-graph">
<ReportBarGraphPlaceholder animate={false} />
</div>
)
}

return (
<Fragment>
<div className={styles.graph} data-test="report-vessels-graph">
{data ? (
<ResponsiveContainer width="100%" height="100%">
<BarChart
width={500}
height={300}
data={data}
margin={{
top: 15,
right: 0,
left: 0,
bottom: 0,
}}
onClick={onLabelClick}
>
{data && (
<Tooltip content={<ReportGraphTooltip type={selectedReportVesselGraph} />} />
)}
{dataviews.map((dataview, index) => {
return (
<Bar
key={dataview.id}
dataKey={dataview.id}
stackId="a"
fill={dataview.config?.color}
>
{index === dataviews.length - 1 && (
<LabelList
position="top"
valueAccessor={(entry: any) => formatI18nNumber(entry.value[1])}
/>
)}
</Bar>
)
})}
<XAxis
dataKey="name"
interval="preserveStart"
tickLine={false}
minTickGap={-1000}
tick={<CustomTick getTickLabel={getTickLabel} />}
tickMargin={0}
/>
</BarChart>
</ResponsiveContainer>
) : (
<ReportBarGraphPlaceholder animate={false} />
)}
<div className={styles.graph} data-test="activity-report-vessels-graph">
<ResponsiveBarChart
// getIndividualData={getIndividualData}
getAggregatedData={getAggregatedData}
// onAggregatedItemClick={onBarClick}
// onIndividualItemClick={onPointClick}
aggregatedValueKey={valueKeys}
barValueFormatter={(value: any) => {
return formatI18nNumber(value).toString()
}}
barLabel={<CustomTick getTickLabel={getTickLabel} />}
labelKey={REPORT_GRAPH_LABEL_KEY}
individualTooltip={<VesselGroupReportVesselsIndividualTooltip />}
individualItem={<VesselGraphLink />}
aggregatedTooltip={<ReportGraphTooltip type={selectedReportVesselGraph} />}
/>
</div>
</Fragment>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,8 @@ export default function ReportVesselsTable({ activityUnit, reportName }: ReportV
/>
</div>
<div className={cx({ [styles.border]: !isLastRow })}>
{vessel.sourceColor && (
<span
className={styles.dot}
style={{ backgroundColor: vessel.sourceColor }}
></span>
{vessel.color && (
<span className={styles.dot} style={{ backgroundColor: vessel.color }}></span>
)}
<VesselLink
className={styles.link}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ export default function ReportVesselsTableFooter({ reportName }: ReportVesselsTa
reportVesselFilter
)?.map((vessel) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { dataviewId, category, sourceColor, flagTranslatedClean, hours, value, ...rest } =
vessel
const { dataviewId, category, color, flagTranslatedClean, hours, value, ...rest } = vessel
return { ...rest, value: formatI18nNumber(hours || value) }
})
trackEvent({
Expand Down
Loading
Loading