Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
j8seangel committed Feb 4, 2025
2 parents 1801151 + c0ee12a commit 3137f87
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 53 deletions.
7 changes: 6 additions & 1 deletion apps/fishing-map/features/map/popups/PopupByCategory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,12 @@ function PopupByCategory({ interaction, type = 'hover' }: PopupByCategoryProps)
)
}
case DataviewCategory.Buffer: {
return <ReportBufferTooltip features={features as PolygonPickingObject[]} />
return (
<ReportBufferTooltip
key={featureCategory}
features={features as PolygonPickingObject[]}
/>
)
}
case DataviewCategory.User: {
const userPointFeatures = (features as UserLayerPickingObject[]).filter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { groupBy } from 'es-toolkit'

import type { Dataset } from '@globalfishingwatch/api-types'
import type { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client'
import type { ContextPickingObject,UserLayerPickingObject } from '@globalfishingwatch/deck-layers'
import type { ContextPickingObject, UserLayerPickingObject } from '@globalfishingwatch/deck-layers'
import { Icon } from '@globalfishingwatch/ui-components'

import { getDatasetLabel } from 'features/datasets/datasets.utils'
Expand Down Expand Up @@ -37,7 +37,6 @@ export function getUserContextLayerLabel(
) {
return getDatasetLabel(dataset)
}
let label = (feature.value ?? feature.title) as string
if (feature.layerId.includes(OFFSHORE_FIXED_INFRASTRUCTURE_LAYER_ID)) {
const startDate = Number(feature.properties.structure_start_date)
const endDate = Number(feature.properties.structure_end_date)
Expand All @@ -49,11 +48,17 @@ export function getUserContextLayerLabel(
end: formatI18nDate(endDate, i18nParams),
})
: `${t('common.since', 'since')} ${formatI18nDate(startDate, i18nParams)}`
label = `${feature.properties.label} - ${feature.properties.label_confidence} ${t(
return `${feature.properties.label} - ${feature.properties.label_confidence} ${t(
'common.confidence',
'confidence'
)} (${rangeLabel})`
}

const label = (feature.value ?? feature.title) as string
// Check if the string starts with { or [ which would indicate JSON
if (!label || /^[[{]/.test(label.trim())) {
return getDatasetLabel(dataset)
}
return label
}

Expand Down
58 changes: 47 additions & 11 deletions apps/track-labeler/src/features/timebar/timebar.hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ import {
} from '../../routes/routes.selectors'
import { useAppDispatch } from '../../store.hooks'
import type { CoordinatePosition } from '../../types'
import { findNextTimestamp, findPreviousTimestamp } from '../../utils/shared'
import {
findNextPosition,
findNextTimestamp,
findPreviousPosition,
findPreviousTimestamp,
} from '../../utils/shared'
import { getVesselTrackData } from '../tracks/tracks.selectors'

export const useTimerangeConnect = () => {
const dispatch = useAppDispatch()
Expand Down Expand Up @@ -84,6 +90,7 @@ export const useTimebarModeConnect = () => {
export const useSegmentsLabeledConnect = () => {
const dispatch = useAppDispatch()
const timestamps = useSelector(selectTimestamps)
const positions = useSelector(getVesselTrackData)
// Add internal state to track segment being created
const [pendingSegment, setPendingSegment] = useState<{
firstClick: {
Expand All @@ -98,7 +105,19 @@ export const useSegmentsLabeledConnect = () => {
timestamp2: number,
position2: CoordinatePosition
): SelectedTrackType => {
// Ensure the segment is always ordered by timestamp
// If both timestamps are the same, create a single-point segment
if (timestamp1 === timestamp2) {
return {
start: timestamp1,
startLatitude: position1.latitude,
startLongitude: position1.longitude,
end: timestamp1,
endLatitude: position1.latitude,
endLongitude: position1.longitude,
}
}

// Otherwise handle normal case where timestamps differ
if (timestamp1 < timestamp2) {
return {
start: timestamp1,
Expand Down Expand Up @@ -131,18 +150,27 @@ export const useSegmentsLabeledConnect = () => {

// Case 1: Segment completely contains new segment
if (segment.start <= newSegment.start && segment.end >= newSegment.end) {
const endLatitude = findPreviousPosition(timestamps, positions, newSegment.start, 'latitude')
const endLongitude = findPreviousPosition(
timestamps,
positions,
newSegment.start,
'longitude'
)
const startLatitude = findNextPosition(timestamps, positions, newSegment.end, 'latitude')
const startLongitude = findNextPosition(timestamps, positions, newSegment.end, 'longitude')
return [
{
...segment,
end: findPreviousTimestamp(timestamps, newSegment.start),
endLatitude: newSegment.startLatitude,
endLongitude: newSegment.startLongitude,
endLatitude,
endLongitude,
},
{
...segment,
start: findNextTimestamp(timestamps, newSegment.end),
startLatitude: newSegment.endLatitude,
startLongitude: newSegment.endLongitude,
startLatitude,
startLongitude,
},
]
}
Expand All @@ -154,24 +182,33 @@ export const useSegmentsLabeledConnect = () => {

// Case 3: Overlap at start
if (newSegment.start <= segment.start && newSegment.end >= segment.start) {
const startLatitude = findNextPosition(timestamps, positions, newSegment.end, 'latitude')
const startLongitude = findNextPosition(timestamps, positions, newSegment.end, 'longitude')
return [
{
...segment,
start: findNextTimestamp(timestamps, newSegment.end),
startLatitude: newSegment.endLatitude,
startLongitude: newSegment.endLongitude,
startLatitude,
startLongitude,
},
]
}

// Case 4: Overlap at end
if (newSegment.end >= segment.end && newSegment.start <= segment.end) {
const endLatitude = findPreviousPosition(timestamps, positions, newSegment.start, 'latitude')
const endLongitude = findPreviousPosition(
timestamps,
positions,
newSegment.start,
'longitude'
)
return [
{
...segment,
end: findPreviousTimestamp(timestamps, newSegment.start),
endLatitude: newSegment.startLatitude,
endLongitude: newSegment.startLongitude,
endLatitude,
endLongitude,
},
]
}
Expand All @@ -198,7 +235,6 @@ export const useSegmentsLabeledConnect = () => {
return
}

// If we have a first click, we can create a complete segment
const newSegment = createNewSegment(
pendingSegment.firstClick.timestamp,
pendingSegment.firstClick.position,
Expand Down
8 changes: 3 additions & 5 deletions apps/track-labeler/src/features/tracks/tracks.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { createSelector } from 'reselect'
import type { TrackPoint, TrackSegment } from '@globalfishingwatch/api-types'

import { Field } from '../../data/models'
import type { SelectedTrackType } from '../../features/vessels/selectedTracks.slice';
import type { SelectedTrackType } from '../../features/vessels/selectedTracks.slice'
import { selectedtracks } from '../../features/vessels/selectedTracks.slice'
import {
selectOriginalTracks,
Expand All @@ -21,7 +21,7 @@ import {
selectQueryParam,
selectVessel,
} from '../../routes/routes.selectors'
import type { LayersData } from '../../types';
import type { LayersData } from '../../types'
import { ActionType } from '../../types'

export const getVesselTrack = createSelector(
Expand Down Expand Up @@ -233,7 +233,6 @@ export const getVesselParsedTrack = createSelector(
.filter((segment: TrackSegment) => segment.length)
.forEach((segment: any) => {
let trackPoints: TrackPoint[] = []
let previousPoint: TrackPoint = segment[0]
let actionFlag: ActionType | string = getCurrentVesselAction(segment[0], selectedTracksTree)
segment.forEach((point: TrackPoint) => {
const currentAction: ActionType | string | null = getCurrentVesselAction(
Expand All @@ -249,7 +248,7 @@ export const getVesselParsedTrack = createSelector(
})
}
if (currentAction !== ActionType.untracked) {
trackPoints = previousPoint ? [previousPoint, point] : [point]
trackPoints = [point]
} else {
trackPoints = []
}
Expand All @@ -259,7 +258,6 @@ export const getVesselParsedTrack = createSelector(
}
}
actionFlag = currentAction ?? ''
previousPoint = point
})
layersData.push({
trackPoints: trackPoints,
Expand Down
24 changes: 24 additions & 0 deletions apps/track-labeler/src/utils/shared.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { BBox } from 'geojson'

import type { TrackInterface } from '../features/vessels/vessels.slice'

export function typedKeys<T>(o: T): (keyof T)[] {
// type cast should be safe because that's what really Object.keys() does
return Object.keys(o as any) as (keyof T)[]
Expand Down Expand Up @@ -33,3 +35,25 @@ export const findNextTimestamp = (timestamps: number[], timestamp: number): numb

return null
}

export const findPreviousPosition = (
timestamps: number[],
positions: TrackInterface | null,
startCoordinate: number,
coordinateType: 'latitude' | 'longitude'
) => {
const index = timestamps.indexOf(startCoordinate)
if (index === -1) return startCoordinate
return positions?.data?.[0][index][coordinateType]
}

export const findNextPosition = (
timestamps: number[],
positions: TrackInterface | null,
endCoordinate: number,
coordinateType: 'latitude' | 'longitude'
) => {
const index = timestamps.indexOf(endCoordinate)
if (index === -1) return endCoordinate
return positions?.data?.[0][index + 1][coordinateType]
}
10 changes: 6 additions & 4 deletions libs/timebar/src/charts/stacked-activity.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import React, { useContext, useMemo } from 'react'
import { max } from 'd3-array'
import { scaleLinear } from 'd3-scale'
import { area, curveStepAfter,stack, stackOffsetSilhouette } from 'd3-shape'
import { area, curveStepAfter, stack, stackOffsetSilhouette } from 'd3-shape'

import type { UrlDataviewInstance } from '@globalfishingwatch/dataviews-client'

import { DEFAULT_CSS_TRANSITION } from '../constants'
import type { TimelineScale } from '../timelineContext';
import type { TimelineScale } from '../timelineContext'
import TimelineContext from '../timelineContext'

import { useTimeseriesToChartData } from './common/hooks'
import type { HighlighterCallback,Timeseries } from './common/types'
import type { HighlighterCallback, Timeseries } from './common/types'
import { useUpdateChartsData } from './chartsData.atom'

const MARGIN_BOTTOM = 20
Expand Down Expand Up @@ -86,6 +85,9 @@ const StackedActivity = ({
}, [timeseries, graphHeight, overallScale, subLayers])

const middleY = graphHeight / 2 - MARGIN_BOTTOM / 2
if (!svgTransform || !outerWidth || !graphHeight) {
return null
}
return (
<svg width={outerWidth} height={graphHeight}>
<g
Expand Down
61 changes: 33 additions & 28 deletions libs/timebar/src/components/timeline-units.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,34 +85,39 @@ class TimelineUnits extends PureComponent<TimelineUnitsProps> {

return (
<div>
{units.map((d) => (
<div
key={d.id}
style={{
left: d.x,
width: d.width,
transition: 'none',
// transition: immediate
// ? 'none'
// : `width ${DEFAULT_CSS_TRANSITION}, left ${DEFAULT_CSS_TRANSITION}`,
}}
className={styles.unit}
>
{baseUnit === 'hour' ? (
<div>{d.label}</div>
) : (
<button
type="button"
onClick={() => {
this.zoomToUnit(d)
}}
title={d.hoverLabel}
>
{d.label}
</button>
)}
</div>
))}
{units.map((d) => {
if (!d.x || !d.width) {
return null
}
return (
<div
key={d.id}
style={{
left: d.x,
width: d.width,
transition: 'none',
// transition: immediate
// ? 'none'
// : `width ${DEFAULT_CSS_TRANSITION}, left ${DEFAULT_CSS_TRANSITION}`,
}}
className={styles.unit}
>
{baseUnit === 'hour' ? (
<div>{d.label}</div>
) : (
<button
type="button"
onClick={() => {
this.zoomToUnit(d)
}}
title={d.hoverLabel}
>
{d.label}
</button>
)}
</div>
)
})}
</div>
)
}
Expand Down
9 changes: 8 additions & 1 deletion libs/timebar/src/components/timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ class Timeline extends PureComponent<TimelineProps> {
)

getSvgTransform = memoize((overallScale, start, end, innerWidth, innerStartPx) => {
if (!innerWidth || !innerStartPx) {
return ''
}
const startX = overallScale(getUTCDate(start))
const endX = overallScale(getUTCDate(end))
const deltaX = endX - startX
Expand Down Expand Up @@ -212,7 +215,11 @@ class Timeline extends PureComponent<TimelineProps> {
onWindowResize = () => {
if (this.graphContainer !== null && typeof window !== 'undefined') {
const graphStyle = window.getComputedStyle(this.graphContainer)
const outerX = this.graphContainer.getBoundingClientRect().left
const boundingRect = this.graphContainer.getBoundingClientRect()
if (!boundingRect.left || !boundingRect.width) {
return
}
const outerX = boundingRect.left
const relativeOffsetX = -this.node?.offsetLeft
const outerWidth = parseFloat(graphStyle.width)
const outerHeight = parseFloat(graphStyle.height)
Expand Down

0 comments on commit 3137f87

Please sign in to comment.