-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fishing-map/vessel-profile-insights (#2832)
- Loading branch information
Showing
61 changed files
with
1,898 additions
and
478 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
apps/fishing-map/features/vessel-group-report/insights/VGRInsightCoverage.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { useTranslation } from 'react-i18next' | ||
import { useGetVesselGroupInsightQuery } from 'queries/vessel-insight-api' | ||
import { useSelector } from 'react-redux' | ||
import { ParsedAPIError } from '@globalfishingwatch/api-client' | ||
import InsightError from 'features/vessel/insights/InsightErrorMessage' | ||
import DataTerminology from 'features/vessel/identity/DataTerminology' | ||
import { selectFetchVesselGroupReportCoverageParams } from '../vessel-group-report.selectors' | ||
import styles from './VGRInsights.module.css' | ||
import VesselGroupReportInsightCoverageGraph from './VGRInsightCoverageGraph' | ||
|
||
const VesselGroupReportInsightCoveragePlaceholder = () => { | ||
// TODO graph bar placeholder | ||
return <div style={{ width: '20rem' }} className={styles.loadingPlaceholder} /> | ||
} | ||
|
||
const VesselGroupReportInsightCoverage = () => { | ||
const { t } = useTranslation() | ||
const fetchParams = useSelector(selectFetchVesselGroupReportCoverageParams) | ||
const { data, error, isLoading } = useGetVesselGroupInsightQuery(fetchParams) | ||
|
||
return ( | ||
<div id="vessel-group-coverage" className={styles.insightContainer}> | ||
<div className={styles.insightTitle}> | ||
<label className="experimental">{t('vessel.insights.coverage', 'AIS Coverage')}</label> | ||
<DataTerminology | ||
size="tiny" | ||
type="default" | ||
title={t('vessel.insights.coverage', 'AIS Coverage')} | ||
terminologyKey="insightsCoverage" | ||
/> | ||
</div> | ||
{isLoading ? ( | ||
<VesselGroupReportInsightCoveragePlaceholder /> | ||
) : error ? ( | ||
<InsightError error={error as ParsedAPIError} /> | ||
) : data?.coverage && data?.coverage?.length > 0 ? ( | ||
<VesselGroupReportInsightCoverageGraph data={data.coverage} /> | ||
) : null} | ||
</div> | ||
) | ||
} | ||
|
||
export default VesselGroupReportInsightCoverage |
62 changes: 62 additions & 0 deletions
62
apps/fishing-map/features/vessel-group-report/insights/VGRInsightCoverageGraph.module.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
.graph { | ||
width: 100%; | ||
height: 30rem; | ||
margin-block: var(--space-S); | ||
} | ||
|
||
.graph tspan { | ||
text-transform: uppercase; | ||
font: var(--font-XS); | ||
line-height: 1.2rem; | ||
fill: var(--color-secondary-blue); | ||
} | ||
|
||
.tooltipContainer { | ||
background-color: var(--color-white); | ||
border: var(--border); | ||
padding: var(--space-S); | ||
} | ||
|
||
.axisLabel { | ||
cursor: pointer; | ||
} | ||
|
||
.tooltipRow { | ||
display: flex; | ||
align-items: center; | ||
} | ||
|
||
.tooltipLabel { | ||
color: var(--color-secondary-blue); | ||
} | ||
|
||
.graph tspan.info { | ||
text-transform: lowercase; | ||
font-family: serif; | ||
font-style: italic; | ||
font-weight: 600; | ||
} | ||
|
||
.graph :global(.recharts-tooltip-cursor) { | ||
fill: var(--color-terthiary-blue); | ||
} | ||
|
||
.bar { | ||
transition: fill 300ms linear; | ||
} | ||
|
||
.graph :global(.recharts-bar-rectangle):nth-child(1) { | ||
opacity: 0.3; | ||
} | ||
|
||
.graph :global(.recharts-bar-rectangle):nth-child(2) { | ||
opacity: 0.5; | ||
} | ||
|
||
.graph :global(.recharts-bar-rectangle):nth-child(3) { | ||
opacity: 0.7; | ||
} | ||
|
||
.graph :global(.recharts-bar-rectangle):nth-child(4) { | ||
opacity: 0.85; | ||
} |
101 changes: 101 additions & 0 deletions
101
apps/fishing-map/features/vessel-group-report/insights/VGRInsightCoverageGraph.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import React, { Fragment, useMemo } from 'react' | ||
import { useSelector } from 'react-redux' | ||
import { BarChart, Bar, XAxis, ResponsiveContainer, LabelList } from 'recharts' | ||
import { groupBy } from 'es-toolkit' | ||
import { VesselGroupInsightResponse } from '@globalfishingwatch/api-types' | ||
import { COLOR_PRIMARY_BLUE } from 'features/app/app.config' | ||
import { selectVGRDataview } from '../vessel-group-report.selectors' | ||
import styles from './VGRInsightCoverageGraph.module.css' | ||
|
||
const CustomTick = (props: any) => { | ||
const { x, y, payload } = props | ||
|
||
return ( | ||
<text transform={`translate(${x},${y - 3})`}> | ||
<tspan textAnchor="middle" x="0" dy={12}> | ||
{payload.value} | ||
</tspan> | ||
</text> | ||
) | ||
} | ||
|
||
type VesselGroupReportCoverageGraphData = { | ||
name: string | ||
value: number | ||
} | ||
|
||
const COVERAGE_GRAPH_BUCKETS: Record<string, number> = { | ||
'<20%': 20, | ||
'20-40%': 40, | ||
'40-60%': 60, | ||
'60-80%': 80, | ||
'>80%': 100, | ||
} | ||
const CoverageGraphBuckets = Object.keys(COVERAGE_GRAPH_BUCKETS) | ||
function parseCoverageGraphValueBucket(value: number) { | ||
return ( | ||
CoverageGraphBuckets.find((key) => value < COVERAGE_GRAPH_BUCKETS[key]) || | ||
CoverageGraphBuckets[CoverageGraphBuckets.length - 1] | ||
) | ||
} | ||
|
||
function parseCoverageGraphData( | ||
data: VesselGroupInsightResponse['coverage'] | ||
): VesselGroupReportCoverageGraphData[] { | ||
if (!data) return [] | ||
const dataByCoverage = data.map((d) => ({ | ||
name: d.vesselId, | ||
value: parseCoverageGraphValueBucket(d.percentage), | ||
})) | ||
const groupedDataByCoverage = groupBy(dataByCoverage, (entry) => entry.value!) | ||
return Object.keys(COVERAGE_GRAPH_BUCKETS).map((key) => ({ | ||
name: key, | ||
value: groupedDataByCoverage[key]?.length || 0, | ||
})) | ||
} | ||
|
||
export default function VesselGroupReportInsightCoverageGraph({ | ||
data, | ||
}: { | ||
data: VesselGroupInsightResponse['coverage'] | ||
}) { | ||
const dataGrouped = useMemo(() => parseCoverageGraphData(data), [data]) | ||
const reportDataview = useSelector(selectVGRDataview) | ||
return ( | ||
<Fragment> | ||
<div className={styles.graph} data-test="report-vessels-graph"> | ||
{dataGrouped && ( | ||
<ResponsiveContainer width="100%" height="100%"> | ||
<BarChart | ||
width={500} | ||
height={300} | ||
data={dataGrouped} | ||
margin={{ | ||
top: 15, | ||
right: 0, | ||
left: 0, | ||
bottom: 0, | ||
}} | ||
> | ||
<Bar | ||
className={styles.bar} | ||
dataKey="value" | ||
fill={reportDataview?.config?.color || COLOR_PRIMARY_BLUE} | ||
> | ||
<LabelList position="top" valueAccessor={(entry: any) => entry.value} /> | ||
</Bar> | ||
<XAxis | ||
dataKey="name" | ||
interval="equidistantPreserveStart" | ||
tickLine={false} | ||
minTickGap={-1000} | ||
tick={<CustomTick />} | ||
tickMargin={0} | ||
/> | ||
</BarChart> | ||
</ResponsiveContainer> | ||
)} | ||
</div> | ||
</Fragment> | ||
) | ||
} |
Oops, something went wrong.