Skip to content

Commit

Permalink
render speed and direction current layer
Browse files Browse the repository at this point in the history
  • Loading branch information
j8seangel committed Jan 17, 2025
1 parent f3c702e commit 5f8a3e8
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 53 deletions.
12 changes: 11 additions & 1 deletion apps/fishing-map/features/dataviews/dataviews.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@ const dataviews: Dataview[] = [
},
],
endpoint: '4wings-tiles',
datasetId: 'public-global-presence:v3.0',
datasetId: 'public-global-currents-uo:v20231213',
},
{
params: [
{
id: 'type',
value: 'heatmap',
},
],
endpoint: '4wings-tiles',
datasetId: 'public-global-currents-vo:v20231213',
},
],
category: DataviewCategory.Environment,
Expand Down
29 changes: 15 additions & 14 deletions libs/deck-layer-composer/src/resolvers/currents.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { uniq } from 'es-toolkit'
import { DatasetTypes, EndpointId } from '@globalfishingwatch/api-types'
import { getDatasetsExtent, resolveEndpoint } from '@globalfishingwatch/datasets-client'
import type {
ColorRampId,
FourwingsCurrentsTileLayerProps,
FourwingsDeckSublayer,
FourwingsVisualizationMode,
} from '@globalfishingwatch/deck-layers'
import { getUTCDateTime } from '@globalfishingwatch/deck-layers'
import { DatasetTypes, EndpointId } from '@globalfishingwatch/api-types'
import type { ColorRampId } from '@globalfishingwatch/deck-layers'
import { getDatasetsExtent, resolveEndpoint } from '@globalfishingwatch/datasets-client'

import { getDataviewAvailableIntervals } from './dataviews'
import type { DeckResolverFunction } from './types'

Expand All @@ -17,17 +17,18 @@ export const resolveDeckCurrentsLayerProps: DeckResolverFunction<
const startTime = start ? getUTCDateTime(start).toMillis() : 0
const endTime = end ? getUTCDateTime(end).toMillis() : Infinity

const visibleSublayers = dataview.config?.sublayers?.filter((sublayer) => sublayer?.visible)
const sublayers: FourwingsDeckSublayer[] = (visibleSublayers || []).map((sublayer) => {
return {
id: sublayer.id,
visible: sublayer?.visible ?? true,
datasets: sublayer?.datasets.map((dataset) => dataset.id),
color: (sublayer?.color || dataview.config?.color) as string,
colorRamp: sublayer?.colorRamp as ColorRampId,
label: sublayer?.datasets?.[0]?.name,
const sublayers: FourwingsDeckSublayer[] = (dataview.datasetsConfig || [])?.map(
(datasetConfig) => {
return {
id: datasetConfig.datasetId,
visible: dataview.config?.visible ?? true,
datasets: [datasetConfig.datasetId],
color: dataview.config?.color as string,
colorRamp: '' as ColorRampId,
label: dataview?.datasets?.find((dataset) => dataset.id === datasetConfig.datasetId)?.name,
}
}
})
)

const maxZoomLevels = (dataview.config?.sublayers || [])?.flatMap(({ maxZoom }) =>
maxZoom !== undefined ? (maxZoom as number) : []
Expand Down
13 changes: 7 additions & 6 deletions libs/deck-layer-composer/src/resolvers/dataviews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,12 +383,13 @@ export function getDataviewsResolved(
d.config?.type === DataviewType.HeatmapStatic ? false : singleHeatmapDataview,
}) || []
)
const currentsDataviewsParsed = currentsDataviews.flatMap(
(d) =>
getFourwingsDataviewsResolved(d, {
visualizationMode: params.environmentVisualizationMode,
}) || []
)
const currentsDataviewsParsed = currentsDataviews.flatMap((dataview) => {
return {
...dataview,
config: { ...dataview.config, visualizationMode: params.environmentVisualizationMode },
}
})

const vesselGroupDataviewParsed = vesselGroupDataview.flatMap((d) => {
const comparisonMode = getComparisonMode([d], params)
return (
Expand Down
50 changes: 38 additions & 12 deletions libs/deck-layers/src/layers/fourwings/currents/CurrentsLayer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Geometry } from '@luma.gl/engine'
import { Model } from '@luma.gl/engine'
import type { LayerProps, Accessor, DefaultProps, LayerDataSource } from '@deck.gl/core'
import type { Accessor, DefaultProps, LayerDataSource, LayerProps } from '@deck.gl/core'
import { ScatterplotLayer } from '@deck.gl/layers'
import { Geometry, Model } from '@luma.gl/engine'

type _CurrentsLayerProps<DataT> = {
data: LayerDataSource<DataT>
Expand Down Expand Up @@ -30,15 +29,25 @@ export default class CurrentsLayer<
protected _getModel() {
// a square that minimally cover the unit circle
// const positions = [-1, -1, 0, 0.5, 0.5, 0, 1, 1, 0];
const positions = [0.0, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0]
// Updated positions for square model
const positions = [
-0.5,
-0.5, // Bottom left
0.5,
-0.5, // Bottom right
-0.5,
0.5, // Top left
0.5,
0.5, // Top right
]
return new Model(this.context.device, {
...this.getShaders(),
id: this.props.id,
bufferLayout: this.getAttributeManager()!.getBufferLayouts(),
geometry: new Geometry({
topology: 'triangle-list',
topology: 'triangle-strip', // Using triangle-strip to create a square
attributes: {
positions: { size: 3, value: new Float32Array(positions) },
positions: { size: 2, value: new Float32Array(positions) }, // Size remains 2 for x, y coordinates
},
}),
isInstanced: true,
Expand Down Expand Up @@ -83,6 +92,7 @@ export default class CurrentsLayer<
'vs:#decl': `
in float instanceDirections;
in float instanceVelocity;
out float vVelocity;
vec2 rotate_by_angle(vec2 vertex, float angle) {
float angle_radian = angle * PI / 180.0;
Expand All @@ -93,21 +103,37 @@ export default class CurrentsLayer<
}
`,
'vs:DECKGL_FILTER_SIZE': `
float minWidth = 0.0; // Minimum width in pixels
float maxWidth = 1.0; // Define your maximum width here
float normalizedVelocity = min(instanceVelocity / 100.0, 1.0);
float widthFactor = mix(minWidth, maxWidth, normalizedVelocity); // Interpolate between min and max based on instanceVelocity
float minWidth = 0.1; // Minimum width in pixels
float maxWidth = 0.4; // Define your maximum width here
float minHeight = 0.4; // Minimum width in pixels
float maxHeight = 1.0; // Define your maximum width here
// float normalizedVelocity = min(instanceVelocity / 2.0, 1.0);
float widthFactor = mix(minWidth, maxWidth, instanceVelocity); // Interpolate between min and max based on instanceVelocity
float heightFactor = mix(minHeight, maxHeight, instanceVelocity); // Interpolate between min and max based on instanceVelocity
size.x *= widthFactor; // Scale the size based on the variable
size.y *= heightFactor; // Scale the size based on the variable
size.xy = rotate_by_angle(size.xy, instanceDirections);
`,
'vs:#main-end': `
vVelocity = instanceVelocity;
`,
'fs:#decl': `
uniform float uTime;
in float vVelocity;
`,
'fs:DECKGL_FILTER_COLOR': `
// float yPos = abs(geometry.uv.y - uTime);
// color.a = mix(0.0, 1.0, 1.0 - (yPos * 2.0));
float yPos = mod(geometry.uv.y - uTime, 1.0); // Use modulo for smooth transition
color.a = mix(0.2, 1.0, 1.0 - sin(yPos * 3.14)); // Inverted opacity calculation
float minSpeed = 0.0; // Minimum speed in pixels
float maxSpeed = 1.5; // Define your maximum speed here
float speedFactor = mix(minSpeed, maxSpeed, vVelocity);
float minOpacity = 0.4 * speedFactor; // Minimum speed in pixels
float maxOpacity = 1.4 * speedFactor; // Define your maximum speed here
float alphaFactor = mix(minOpacity, maxOpacity, geometry.uv.y); // Interpolate between min and max based on instanceVelocity
// float yPos = mod(geometry.uv.y - uTime, 1.0 ); // Use modulo for smooth transition
// float yPos = mod(geometry.uv.y - uTime, 1.0 ); // Use modulo for smooth transition
// color.a = mix(0.2, 1.0, 1.0 - sin(yPos * 3.14)); // Inverted opacity calculation
color.a *= alphaFactor; // Inverted opacity calculation
`,
},
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import type { LayersList, LayerContext } from '@deck.gl/core'
import type { LayerContext, LayersList } from '@deck.gl/core'
import { CompositeLayer } from '@deck.gl/core'

import {
FourwingsAggregationOperation,
getLayerGroupOffset,
LayerGroup,
} from '@globalfishingwatch/deck-layers'
import type { FourwingsFeature } from '@globalfishingwatch/deck-loaders'
import { getTimeRangeKey } from '@globalfishingwatch/deck-loaders'
import { getLayerGroupOffset } from '@globalfishingwatch/deck-layers'
import { LayerGroup } from '@globalfishingwatch/deck-layers'

import type { FourwingsHeatmapLayerProps } from '../fourwings.types'
import { getIntervalFrames } from '../heatmap/fourwings-heatmap.utils'
import {
aggregateCell,
EMPTY_CELL_COLOR,
getIntervalFrames,
} from '../heatmap/fourwings-heatmap.utils'

import CurrentsLayer from './CurrentsLayer'

type CurrentsLayerState = {
time: number
}

const RAD_TO_DEG = 180 / Math.PI
function getUTime() {
return (Date.now() % 3000) / 3000
}
Expand Down Expand Up @@ -113,6 +124,48 @@ export class FourwingsCurrentsLayer extends CompositeLayer<FourwingsHeatmapLayer
// return target
// }

getVelocity = (feature: FourwingsFeature, { target }: { target: number }) => {
const forces = feature.properties.values[0].map((value, i) => {
const u = value
const v = feature.properties.values[1][i]
return Math.sqrt(u * u + v * v)
})
const [force] = aggregateCell({
// TODO:currents get U instead of by index
cellValues: [forces],
aggregationOperation: FourwingsAggregationOperation.Avg,
startFrame: this.startFrame,
endFrame: this.endFrame,
cellStartOffsets: [31],
})
if (force) {
target = force
return target
}
return EMPTY_CELL_COLOR
}
getDirection = (feature: FourwingsFeature, { target }: { target: number }) => {
const angles = feature.properties.values[1].map((value, i) => {
const v = value
const u = feature.properties.values[0][i]
return Math.round(180 + ((RAD_TO_DEG * Math.atan2(v, u)) % 360))
})
const [angle] = aggregateCell({
// TODO:currents get U instead of by index
cellValues: [angles],
aggregationOperation: FourwingsAggregationOperation.Avg,
startFrame: this.startFrame,
endFrame: this.endFrame,
cellStartOffsets: [31],
})

if (angle) {
target = angle
return target
}
return EMPTY_CELL_COLOR
}

renderLayers() {
const { data, endTime, startTime, tilesCache } = this.props

Expand All @@ -139,20 +192,16 @@ export class FourwingsCurrentsLayer extends CompositeLayer<FourwingsHeatmapLayer
getFillColor: [255, 255, 255, 255],
getLineColor: [0, 0, 0, 255],
getRadius: 1,
radiusMinPixels: 8,
radiusMinPixels: 12,
stroked: false,
positionFormat: 'XY',
filled: true,
billboard: false,
antialiasing: true,
time: this.state.time,
// time: this.state.time,
getPolygonOffset: (params: any) => getLayerGroupOffset(LayerGroup.HeatmapStatic, params),
getVelocity: (d: FourwingsFeature) => {
return Math.floor(Math.random() * 100)
},
getDirection: (d: FourwingsFeature) => {
return Math.floor(Math.random() * 361)
},
getVelocity: this.getVelocity,
getDirection: this.getDirection,
getPosition: (d: FourwingsFeature) => {
return [d.coordinates[0], d.coordinates[1]]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
import type { Layer, LayerContext, LayersList, DefaultProps, UpdateParameters } from '@deck.gl/core'
import type { DefaultProps, Layer, LayerContext, LayersList, UpdateParameters } from '@deck.gl/core'
import { CompositeLayer } from '@deck.gl/core'
import type { TileLayerProps } from '@deck.gl/geo-layers'
import { TileLayer } from '@deck.gl/geo-layers'
import { parse } from '@loaders.gl/core'
import type { TileLoadProps } from '@deck.gl/geo-layers/dist/tileset-2d'
import { parse } from '@loaders.gl/core'

import { GFWAPI } from '@globalfishingwatch/api-client'
import type {
FourwingsFeature,
FourwingsInterval,
ParseFourwingsOptions,
} from '@globalfishingwatch/deck-loaders'
import { FourwingsLoader, getFourwingsInterval } from '@globalfishingwatch/deck-loaders'
import { GFWAPI } from '@globalfishingwatch/api-client'
import { HEATMAP_API_TILES_URL, FOURWINGS_MAX_ZOOM } from '../fourwings.config'

import { FOURWINGS_MAX_ZOOM, HEATMAP_API_TILES_URL } from '../fourwings.config'
import type {
BaseFourwingsLayerProps,
FourwingsDeckSublayer,
FourwingsVisualizationMode,
} from '../fourwings.types'
import type { FourwingsHeatmapTilesCache } from '../heatmap/fourwings-heatmap.types'
import {
getFourwingsChunk,
getDataUrlBySublayer,
getTileDataCache,
getFourwingsChunk,
getResolutionByVisualizationMode,
getTileDataCache,
getZoomOffsetByResolution,
} from '../heatmap/fourwings-heatmap.utils'
import type { FourwingsHeatmapTilesCache } from '../heatmap/fourwings-heatmap.types'

import { FourwingsCurrentsLayer } from './FourwingsCurrentsLayer'

export type FourwingsCurrentsTileLayerState = {
Expand Down Expand Up @@ -161,6 +164,7 @@ export class FourwingsCurrentsTileLayer extends CompositeLayer<FourwingsCurrents
),
} as ParseFourwingsOptions,
})

return data
}

Expand Down Expand Up @@ -221,7 +225,7 @@ export class FourwingsCurrentsTileLayer extends CompositeLayer<FourwingsCurrents
}
const { tilesCache } = this.state
const { visualizationMode, maxRequests, debounceTime } = this.props
console.log('🚀 ~ renderLayers ~ visualizationMode:', visualizationMode)

const cacheKey = this._getTileDataCacheKey()
const resolution = getResolutionByVisualizationMode(visualizationMode)

Expand Down

0 comments on commit 5f8a3e8

Please sign in to comment.