**Leaflow-GL** is a high-performance WebGL extension for **Leaflet maps** that uses **GPU-accelerated rendering** to create dynamic visualizations of **vector field flows**, such as currents, wind, waves and others. It is developed by [Amphitrite](https:/
Leaflow-GL is a high-performance WebGL extension for Leaflet maps that uses GPU-accelerated rendering to create dynamic visualizations of vector field flows, such as currents, wind, waves and others. It is developed by Amphitrite, a French startup specializing in real-time ocean data intelligence, so it provides extensions and API Keys to access their data and forecasts.
The library externalizes components that draw animated particle flows over the map, representing dynamic vector fields like ocean currents or atmospheric winds, and displays a colored heatmap background, indicating intensity variations of the underlying flow data.
Users can choose one of the two main components to use in the main application depending on their needs:
1. AmphitriteLeaflowLayer: Uses Amphitrite available data for currents, winds, waves and others with API key authentication, simple and straightforward integration.
2. LeaflowLayer: Requires users to have their own data, and has a complex integration.
The layers are built using pure Three.js for GPU rendering, and can be seamlessly integrated into Leaflet-based projects.
The library is highly customizable and users can:
- Make use of the core API (vanilla JavaScript/TypeScript, extending Leaflet's L.GridLayer and L.Layer);
- Or opt out for the React wrapper (easy-to-use React components, compatible with react-leaflet);
- Tweak and fine tune particles parameters as they wish: opacity, trail, size, count, speed, color, etc...;
- Modify the colors/intensity association of the heatmap backgrounds for each visualization system;
---
``bash`
npm install leaflow-gl
โ This will install the Leaflow-GL library directly into your project with public npm registry.
---
Leaflow-GL relies on the following libraries, which must be installed separately in your project:
| Dependency | Recommended Version |
| --------------- | ------------------------------------------------------------------ |
| leaflet | ^1.9.4 |three
| | ^0.176.0 |react
| | ^18.0.0 or ^19.0.0 (if using React components) |react-leaflet
| | ^5.0.0 (only if using React components) |lil-gui
| | ^0.20.0 (optional, only needed if you want to enable debug mode) |
You can install all required peer dependencies with:
`bash`
npm install leaflet three
If you are using the React components, also install:
`bash`
npm install react react-leaflet
If you plan to use debug mode to tweak particle settings visually during runtime, install:
`bash`
npm install lil-gui
โ All peer dependencies must already exist in your project at compatible versions.
- Three.js and Leaflet are mandatory.
- React and React-Leaflet are needed only if you use the React components.
- lil-gui is optional and used only for debug/visual parameter tweaking.
---
Users may choose this component for a straightforward and easy integration, since all the data is already available for you through Amphitrite's API. This component is highly customizable and users can choose between multiple types of environmental data visualizations. You will be provided an API key which is mandatory to have access to the data, simply input the apiKey inside the component and it'll do the rest.
AmphitriteLeaflowLayer supports the following data types:
| Type | Description | Icon |
| --------------------- | ------------------------------------------ | ---- |
| currents | Oceanic currents | ๐ |currents-tides
| | Oceanic + Tidal Currents | ๐ |wind
| | Wind (10m above surface) | ๐ |wind-gust
| | Wind Gust | ๐ |wave-height
| | Significant Wave Height | ๐ |wave-period
| | Mean Wave Period | ๐ |swell-height
| | Swell Height | ๐ |swell-period
| | Swell Period | ๐ |wind-wave-height
| | Wind-generated Wave Height | ๐ |wind-wave-period
| | Wind-generated Wave Period | ๐ |sea-temperature
| | Sea Surface Temperature | ๐ก๏ธ |salinity
| | Sea Surface Salinity | ๐ง |ice
| | Ice Concentration | ๐ง |cloud
| | Total Cloud Coverage | ๐ฅ๏ธ |snow
| | Snow Precipitation | โ๏ธ |precipitation
| | Rain Precipitation | ๐ง๏ธ |humidity
| | Humidity | ๐ง |pressure
| | Surface Air Pressure | ๐ |air-temperature
| | Air Temperature (2m above surface) | ๐ก๏ธ |min-air-temperature
| | Minimum Air Temperature (2m above surface) | ๐ก๏ธ |max-air-temperature
| | Maximum Air Temperature (2m above surface) | ๐ก๏ธ |
Here's an example on how to use AmphitriteLeaflowLayer + React to integrate it in your platform:
`tsx
import { AmphitriteLeaflowLayer } from "leaflow-gl/react";
import { MapContainer } from "react-leaflet";
function Main() {
return (
apiKey="your-api-key-here"
date={new Date()}
landArea="gray"
clickInfoBoxEnabled={true}
/>
);
}
`
Here's an example on how to use AmphitriteLeaflowLayer with Vanilla Javascript/Typescript to integrate it in your platform:
`typescript
import { AmphitriteLeaflowLayer } from "leaflow-gl";
const amphiLayer = new AmphitriteLeaflowLayer({
type: "wind",
apiKey: "your-api-key-here",
date: new Date(),
landArea: "gray",
clickInfoBoxEnabled: true,
});
leafletMap.addLayer(amphiLayer);
`
The AmphitriteLeaflowLayer accepts the following parameters:
#### Required Parameters
| Parameter | Type | Description |
| --------- | -------------------- | ------------------------------------------------------------------------------ |
| type | AmphitriteDataType | Type of data to retrieve from Amphitrite's database (see Available Data Types) |apiKey
| | string | API Key provided by Amphitrite to authenticate data requests |
#### Core Optional Parameters
| Parameter | Type | Default | Description |
| ----------- | --------------------- | -------------- | ----------------------------------------------------------------------------- |
| date | Date | new Date() | Date for which to retrieve data (supports both historical data and forecasts) |landArea
| | "gray" \| "white" | varies by type | Color of land areas on the map (some data types default to no land overlay) |heatmap
| | LeaflowHeatmap | auto | Custom heatmap color mapping (defaults to Amphitrite's optimized settings) |particles
| | ParticlesProps | auto | Custom particle behavior configuration (defaults to Amphitrite's settings) |hurricane
| | string | undefined | Name of hurricane to display (e.g., "Katrina") |
#### Feature Components
| Parameter | Type | Default | Description |
| ---------------------- | -------------------- | ------- | ----------------------------------------------------------------------------- |
| clickInfoBoxEnabled | boolean | false | Enable clicking on the map to display point information |clickInfoBoxOptions
| | ClickInfoBoxProps | โ | Customize appearance of the click info box (immutable, set at construction) |timeSliderOptions
| | TimeSliderProps | โ | Configure the time slider for date selection (immutable, set at construction) |heatmapLegendOptions
| | HeatmapLegendProps | โ | Configure the heatmap legend display (immutable, set at construction) |latLonGrid
| | LatLonGridOptions | โ | Configure latitude/longitude grid overlay |downloadPdfOptions
| | DownloadPdfOptions | enabled | Configure PDF download feature for point forecasts |
#### Callbacks
| Parameter | Type | Description |
| ------------------------ | --------------------------------------- | --------------------------------------------------------------------------- |
| onLeaflowDataReady | (data?: LeaflowData) => void | Called when flow data is loaded and ready |onLeaflowLandDataReady
| | (landData?: LeaflowLandData) => void | Called when land mask data is loaded |onDateChanged
| | (date: Date) => void | Called when the selected date changes (via time slider or programmatically) |onHurricaneDataReady
| | (hurricanes: HurricaneData[]) => void | Called when list of available hurricanes is fetched |
#### Advanced Options
| Parameter | Type | Default | Description |
| ------------------- | --------- | ------- | ---------------------------------------------------------------- |
| showDebugControls | boolean | false | Show real-time controls to tweak particle and heatmap parameters |
#### Inherited Leaflet Options
All standard L.GridLayerOptions are supported, including:
| Parameter | Type | Description |
| --------------- | ---------------- | -------------------------------------------------------- |
| tileSize | number | Size of tiles in pixels |opacity
| | number | Layer opacity (0-1) |zIndex
| | number | Z-index for layer stacking |bounds
| | L.LatLngBounds | Geographical bounds (auto-configured based on data type) |maxNativeZoom
| | number | Maximum zoom level with native tile data (default: 6) |attribution
| | string | Attribution text |
---
#### UnitSpec
Defines a unit of measurement with optional scale and offset transformations.
`typescript`
type UnitSpec = {
label: string; // Display label (e.g., "m/s", "kn", "ยฐC")
scale?: number; // Multiplication factor for conversion
offset?: number; // Offset to add after scaling
};
Example transformations:
- Speed: { label: "kn", scale: 1.94384 } - converts m/s to knots{ label: "ยฐF", scale: 1.8, offset: 32 }
- Temperature: - converts ยฐC to ยฐF
#### ClickInfoBoxProps
Customizes the appearance of the click information box.
`typescript`
interface ClickInfoBoxProps {
textColor?: string; // Color of text (e.g., "#FFFFFF")
backgroundColor?: string; // Background color (e.g., "rgba(0,0,0,0.8)")
fontFamily?: string; // Font family (e.g., "Arial, sans-serif")
}
Example:
`typescript`
clickInfoBoxOptions: {
textColor: "#FFFFFF",
backgroundColor: "rgba(0, 0, 0, 0.85)",
fontFamily: "Roboto, sans-serif"
}
#### TimeSliderProps
Configures the time slider component for date/time selection.
`typescript`
type TimeSliderProps = {
daysToShow?: number; // Number of days visible in slider (default varies)
daysToForecast?: number; // Number of forecast days to show
style?: { [key: string]: string }; // Custom CSS styles
className?: string; // CSS class name
width?: string; // Width in CSS units (e.g., "400px")
color?: string; // Color of the slider tracker
backgroundColor?: string; // Background color
nowLineColor?: string; // Color of the "now" indicator line
textColor?: string; // Color of labels
textSize?: string; // Font size (e.g., "12px")
fontFamily?: string; // Font family
sliderTextColor?: string; // Color of text on slider handle
sliderTextSize?: string; // Font size of slider handle text
onDatetimeChanged?: (date: Date) => void; // Callback when date changes
};
Example:
`typescript`
timeSliderOptions: {
daysToShow: 7,
width: "500px",
color: "#4CAF50",
backgroundColor: "rgba(255, 255, 255, 0.9)",
textColor: "#333333",
onDatetimeChanged: (date) => console.log("Selected:", date)
}
#### HeatmapLegendProps
Configures the heatmap legend display.
`typescript
type HeatmapLegendOrientation = "horizontal" | "vertical";
interface HeatmapLegendProps {
labelSteps?: number[] | "all"; // Which values to show labels for (e.g., [0, 10, 20, 30])
orientation?: HeatmapLegendOrientation; // "vertical" (default) or "horizontal"
className?: string; // CSS class name
style?: { [key: string]: string }; // Custom CSS styles
}
`
Example:
`typescript`
heatmapLegendOptions: {
labelSteps: [0, 5, 10, 15, 20, 25, 30],
orientation: "vertical",
style: { right: "20px", top: "100px" }
}
#### LatLonGridOptions
Configures the latitude/longitude grid overlay.
`typescript`
interface LatLonGridOptions extends L.LayerOptions {
enabled?: boolean; // Whether grid is enabled
latitudeLabelsLeft?: boolean; // Show latitude labels on left side
latitudeLabelsRight?: boolean; // Show latitude labels on right side
longitudeLabelsTop?: boolean; // Show longitude labels on top
longitudeLabelsBottom?: boolean; // Show longitude labels on bottom
marginLeft?: number; // Left margin for labels (pixels)
marginRight?: number; // Right margin for labels (pixels)
marginTop?: number; // Top margin for labels (pixels)
marginBottom?: number; // Bottom margin for labels (pixels)
}
Example:
`typescript`
latLonGrid: {
enabled: true,
latitudeLabelsLeft: true,
longitudeLabelsBottom: true,
marginLeft: 10,
marginBottom: 10
}
#### DownloadPdfOptions
Configures the PDF download feature for point forecasts.
`typescript`
type DownloadPdfOptions = {
enabled?: boolean; // Whether download button is enabled (default: true)
onStart?: () => boolean; // Called before download; return false to cancel
onFinish?: () => void; // Called after successful download
onError?: () => void; // Called if download fails
};
Example:
`typescript`
downloadPdfOptions: {
enabled: true,
onStart: () => {
console.log("Starting PDF download...");
return true; // Proceed with download
},
onFinish: () => console.log("Download complete!"),
onError: () => alert("Download failed")
}
#### ParticlesProps
Defines particle visibility and behavior per zoom level.
`typescript
type ParticlesProps = {
visibility: boolean;
paramsPerZoom?: ParticlesParamsZoomMap;
};
type ParticlesParamsZoomMap = {
[zoom: number]: LeaflowParticlesParams;
};
`
Example:
`typescript`
particles: {
visibility: true,
paramsPerZoom: {
3: { particlesCount: 1500, particlesOpacity: 1, ... },
6: { particlesCount: 1000, particlesOpacity: 0.9, ... }
}
}
#### LeaflowHeatmap
Defines the color gradient for heatmap visualization.
`typescript
type ColorMagnitude = {
m: number; // Magnitude value (after channelMapping transformation)
color: string; // CSS color (hex, rgb, rgba)
};
type LeaflowHeatmap = {
colors: ColorMagnitude[];
legend: {
min?: number; // Minimum value for legend
max?: number; // Maximum value for legend
units: UnitSpec[]; // Available units for display
};
};
`
Example:
`typescript`
heatmap: {
colors: [
{ m: 0, color: "#0000FF" },
{ m: 10, color: "#00FF00" },
{ m: 20, color: "#FFFF00" },
{ m: 30, color: "#FF0000" }
],
legend: {
min: 0,
max: 30,
units: [
{ label: "m/s" },
{ label: "kn", scale: 1.94384 },
{ label: "km/h", scale: 3.6 }
]
}
}
#### HurricaneData
Information about available hurricanes.
`typescript`
type HurricaneData = {
name: string; // Hurricane name (e.g., "Katrina", "Ian")
};
---
| Method | Description |
| --------------------------------------------------- | ------------------------------------------------------------------------ |
| setType(type: AmphitriteDataType) | Changes the data type being visualized (e.g., from "wind" to "currents") |setDate(date: Date)
| | Changes the date for data retrieval (supports past data and forecasts) |setHeatmap(heatmap?: LeaflowHeatmap)
| | Updates the heatmap color mapping (undefined resets to default) |setParticlesProps(props?: ParticlesProps)
| | Updates particle configuration (undefined resets to default) |setLandAreaVisibility(visible: boolean)
| | Shows/hides land areas |setLandArea(landArea: "gray" \| "white")
| | Changes land area color |setHurricane(hurricane?: string)
| | Shows/hides hurricane track (undefined to hide) |setDateChangedCallback(fn?: (date: Date) => void)
| | Updates the callback for date changes |setTimeSliderProps(props: TimeSliderProps)
| | Updates time slider configuration |
Note: AmphitriteLeaflowLayer extends LeaflowLayer, so all LeaflowLayer methods are also available.
---
`typescript
import { AmphitriteLeaflowLayer } from "leaflow-gl";
import L from "leaflet";
// Create map
const map = L.map("map").setView([20, -30], 4);
// Add base layer
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(map);
// Create Amphitrite layer with full configuration
const amphiLayer = new AmphitriteLeaflowLayer({
// Required
type: "wind",
apiKey: "your-api-key-here",
// Core options
date: new Date("2024-01-15T12:00:00Z"),
landArea: "gray",
// Features
clickInfoBoxEnabled: true,
clickInfoBoxOptions: {
textColor: "#FFFFFF",
backgroundColor: "rgba(0, 0, 0, 0.85)",
fontFamily: "Arial, sans-serif",
},
timeSliderOptions: {
daysToShow: 7,
daysToForecast: 3,
width: "500px",
color: "#4CAF50",
onDatetimeChanged: (date) => {
console.log("Date changed to:", date);
},
},
heatmapLegendOptions: {
labelSteps: [0, 10, 20, 30],
orientation: "vertical",
},
latLonGrid: {
enabled: true,
latitudeLabelsLeft: true,
longitudeLabelsBottom: true,
},
downloadPdfOptions: {
enabled: true,
onStart: () => {
console.log("Starting download...");
return true;
},
onFinish: () => console.log("Download complete!"),
},
// Callbacks
onLeaflowDataReady: (data) => {
console.log("Flow data ready:", data);
},
onDateChanged: (date) => {
console.log("Active date:", date);
},
onHurricaneDataReady: (hurricanes) => {
console.log("Available hurricanes:", hurricanes);
},
// Leaflet options
zIndex: 10,
opacity: 0.8,
});
// Add to map
amphiLayer.addTo(map);
// Programmatic control
document.getElementById("nextDay").addEventListener("click", () => {
const currentDate = new Date(amphiLayer.userDefined.date);
currentDate.setDate(currentDate.getDate() + 1);
amphiLayer.setDate(currentDate);
});
document.getElementById("showCurrents").addEventListener("click", () => {
amphiLayer.setType("currents");
});
`
---
Users may use this component if they have their own data and want to display it in their main application. Additional steps are required to set up the visualization using this component. Its parameters, details and inner workings are described below.
Note: If you are not familiar with Leaflet, it's worth checking out their official documentation. It can be useful to understand how this library works in parts.
The LeaflowLayer is the core component of the library. It extends Leaflet's class L.GridLayer and is extended by AmphitriteLeaflowLayer.
This class works similarly to L.TileLayer: it fetches images from a pre-specified URL template whenever the user moves the map (pan or zoom). The URL looks something like this: www.my-server.com/path/to/tiles/{z}/{x}/{y}.png, where z stands for the current zoom, and x and y stand for the coordinates of the tile to be loaded for that particular zoom. The x, y and z values are automatically updated and background layer will automatically fetch the images from your server when Leaflet API demands the creation of a new tile (i.e. when the user moves the map to a region where tiles were not loaded yet).
LeaflowLayer will then use GPU rendering to transform the images into a heatmap for the user to see. It will pre-process all images and display the colors specified by the user. More details are described below. Finally, it will pack all loaded tiles together using an offscreen canvas that covers just the size of the screen, and use it to create an instance of a LeaflowData class. This is the data that serves as input of ParticlesLayer. It is composed of a THREE.CanvasTexture and some other information on how to interpret the particle's movement out of that texture.
The LeaflowLayer accepts the following parameters:
| Parameter | Type | Required | Description |
| ---------------------- | ------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| url | string | โ
| URL template for fetching remote image tiles. Example: www.my-server.com/path/to/tiles/{z}/{x}/{y}.png |channelMapping
| | ChannelMapping | โ
| Defines how to map image channels (RGB values 0-255) into their equivalent physical values through linear interpolation. See examples below. |heatmap
| | LeaflowHeatmap | โ
| Defines the heatmap color mapping used to visualize flow intensity. See examples below. |particles
| | ParticlesProps | โ
| Determines the particle parameters to be used for each Leaflet zoom level. See examples below. |dataChannel
| | ChannelString | โ
| Which channel(s) to use for the heatmap background. Can be a single channel ("r", "g", "b", "a") or two channels ("rg", "gb", etc.). When two channels are specified, the magnitude is calculated as sqrt(uยฒ + vยฒ). |onLeaflowDataReady
| | (data?: LeaflowData) => void | optional | Callback function triggered when the output LeaflowData is ready to load (or undefined in case it failed). |flowDataFilter
| | LeaflowDataFilter | optional | Determines how the LeaflowData texture will be filtered. "Linear" does GPU interpolation of neighbor values to smooth particle movement. "Nearest" takes the nearest value only. Default: "Linear" |flowDataChannel
| | ChannelString | optional | Determines how to interpret the texture for particle movement. If two characters are passed (e.g., "rg", "gb"), they are interpreted as U and V (horizontal and vertical) components. If one character is passed (e.g., "r", "b"), it's interpreted as an angle-based mapping. Default: "rg" |showDebugControls
| | boolean | optional | Activates real-time controls to tweak particle and heatmap parameters visually. Requires lil-gui to be installed. |clickInfoBoxEnabled
| | boolean | optional | Whether the click info box is enabled. Default: false |clickInfoBoxOptions
| | ClickInfoBoxProps | optional | Customize the appearance of the click info box (immutable, set at construction only). |heatmapLegendOptions
| | HeatmapLegendProps | optional | Configure the heatmap legend display (immutable, set at construction only). |showLatLonGrid
| | boolean | optional | Whether to show the latitude/longitude grid overlay. Default: false |L.GridLayerOptions
| _(inherited options)_ | | optional | Standard Leaflet GridLayer options (e.g., tileSize, opacity, attribution, bounds, maxNativeZoom, zIndex, etc.). |
---
These types allow fine control over how flow data is interpreted and visualized, both for the heatmap background and animated particles.
---
Defines how image color channels (R, G, B, A) map to physical values using linear interpolation.
Each channel maps from a min to a max value, optionally including a description and multiple units.
`typescript
type ChannelInfo = {
min: number;
max: number;
desc?: string;
units: UnitSpec[]; // Array of available units for display
};
type Channel = "r" | "g" | "b" | "a";
type ChannelMapping = {
[key in Channel]?: ChannelInfo;
};
`
โ Used to convert raw image data (0โ255 per channel) into real physical values (like current speed or direction).
#### ๐ Example:
``typescript`
const mapping: ChannelMapping = {
r: {
min: -2,
max: 2,
desc: "Zonal current (u)",
units: [
{ label: "m/s" },
{ label: "kn", scale: 1.94384 },
md``
{ label: "km/h", scale: 3.6 },
{ label: "mph", scale: 2.23694 },
]
},
g: {
min: -2,
max: 2,
desc: "Meridional current (v)",
units: [
{ label: "m/s" },
{ label: "kn", scale: 1.94384 },
{ label: "km/h", scale: 3.6 },
{ label: "mph", scale: 2.23694 },
]
},
};
---
Defines the color gradient used to visualize scalar values (like intensity) on the background layer.
`typescript
type ColorMagnitude = {
m: number; // Magnitude value (after channelMapping transformation)
color: string; // CSS color (hex, rgb, rgba)
};
type LeaflowHeatmap = {
colors: ColorMagnitude[];
legend: {
min?: number; // Minimum value for legend
max?: number; // Maximum value for legend
units: UnitSpec[]; // Available units for display
};
};
`
โ
The channel used to paint the heatmap is defined by dataChannel in LeaflowLayer constructor (e.g. "b" for magnitude, or "rg" for sqrt(uยฒ+vยฒ)).
#### ๐ Example
`typescript`
const heatmap: LeaflowHeatmap = {
colors: [
{ m: 0, color: "#404D90" },
{ m: 10, color: "#0c7b74" },
{ m: 20, color: "#328C32" },
{ m: 30, color: "#8C8531" },
{ m: 40, color: "#753444" },
{ m: 50, color: "#909090" },
],
legend: {
min: 0,
max: 50,
units: [
{ label: "m/s" },
{ label: "kn", scale: 1.94384 },
{ label: "km/h", scale: 3.6 },
],
},
};
---
Defines particle visibility and behavior per Leaflet zoom level.
`typescript
type ParticlesProps = {
visibility: boolean;
paramsPerZoom?: ParticlesParamsZoomMap;
};
type ParticlesParamsZoomMap = {
[zoom: number]: LeaflowParticlesParams;
};
`
#### ๐ Example
`typescript`
const particles: ParticlesProps = {
visibility: true,
paramsPerZoom: {
3: {
particlesCount: 1500,
particlesOpacity: 1,
particlesTrail: 0.98,
particlesMinSpeed: 0.1,
particlesMaxSpeed: 0.6,
particlesSpeedScale: 20,
particlesWidth: 2,
particlesBrightness: 0.45,
particlesMinAge: 1200,
particlesMaxAge: 3600,
particlesFadeInDurationMs: 0,
particlesFadeOutDurationMs: 0,
particlesRespawnOnLand: true,
},
6: {
particlesCount: 900,
particlesOpacity: 0.9,
particlesTrail: 0.975,
particlesMinSpeed: 0.1,
particlesMaxSpeed: 0.6,
particlesSpeedScale: 24,
particlesWidth: 1.7,
particlesBrightness: 0.45,
particlesMinAge: 900,
particlesMaxAge: 2600,
particlesFadeInDurationMs: 0,
particlesFadeOutDurationMs: 0,
particlesRespawnOnLand: true,
},
},
};
---
Determines how the flow texture is sampled when computing particle motion.
`typescript`
type LeaflowDataFilter = "Linear" | "Nearest";
- "Linear": interpolates values between neighboring pixels (smoother) โ
default"Nearest"
- : uses nearest pixel only (sharper, sometimes better for angle-based data)
---
| Name | Description |
| ---------------------------- | -------------------------------------------------------------------------------- |
| particlesCount | Number of particles rendered. |particlesOpacity
| | Particle opacity (0 to 1). |particlesTrail
| | Trail persistence (0 to 1), closer to 1 means longer trails. |particlesMinSpeed
| | Minimum advection speed used by the shader. |particlesMaxSpeed
| | Maximum advection speed used by the shader. |particlesSpeedScale
| | Global speed multiplier (visual speed, not units conversion). |particlesWidth
| | Stroke width in pixels. |particlesBrightness
| | Brightness multiplier (useful when particles look โdimโ). |particlesMinAge
| | Min lifetime (ms). |particlesMaxAge
| | Max lifetime (ms). |particlesFadeInDurationMs
| | Fade-in duration (ms). |particlesFadeOutDurationMs
| | Fade-out duration (ms). |particlesRespawnOnLand
| | If true, particles that reach land respawn over water (useful for ocean fields). |
---
`ts
import L from "leaflet";
import {
LeaflowLayer,
type ChannelMapping,
type LeaflowHeatmap,
type ParticlesProps,
} from "leaflow-gl";
const channelMapping: ChannelMapping = {
r: {
min: -2,
max: 2,
desc: "U - Horizontal component",
units: [{ label: "m/s" }, { label: "kn", scale: 1.94384 }],
},
g: {
min: -2,
max: 2,
desc: "V - Vertical component",
units: [{ label: "m/s" }, { label: "kn", scale: 1.94384 }],
},
// Optional: store magnitude in another channel if your tiles encode it (e.g. b)
b: {
min: 0,
max: 3,
desc: "Speed magnitude",
units: [{ label: "m/s" }, { label: "kn", scale: 1.94384 }],
},
};
const heatmap: LeaflowHeatmap = {
colors: [
{ m: 0, color: "#404D90" },
{ m: 0.5, color: "#0c7b74" },
{ m: 1.0, color: "#328C32" },
{ m: 1.5, color: "#8C8531" },
{ m: 2.0, color: "#753444" },
{ m: 2.5, color: "#909090" },
],
legend: {
min: 0,
max: 3,
units: [{ label: "m/s" }, { label: "kn", scale: 1.94384 }],
},
};
const particles: ParticlesProps = {
visibility: true,
paramsPerZoom: {
3: {
particlesCount: 1500,
particlesOpacity: 1,
particlesTrail: 0.981,
particlesMinSpeed: 0.1,
particlesMaxSpeed: 0.6,
particlesSpeedScale: 20,
particlesWidth: 2,
particlesBrightness: 0.45,
particlesMinAge: 1200,
particlesMaxAge: 3600,
particlesFadeInDurationMs: 0,
particlesFadeOutDurationMs: 0,
particlesRespawnOnLand: true,
},
},
};
const map = L.map("map").setView([0, 0], 3);
const leaflowLayer = new LeaflowLayer({
url: "https://your-server.com/tiles/{z}/{x}/{y}.png",
channelMapping,
heatmap,
particles,
// Heatmap uses this channel:
// - "b" if your tiles encode magnitude in blue
// - "rg" if you want magnitude computed as sqrt(uยฒ+vยฒ)
dataChannel: "rg",
// Particles use this channel mapping (u,v):
flowDataChannel: "rg",
flowDataFilter: "Linear",
bounds: [
[-90, -180],
[90, 180],
],
maxNativeZoom: 6,
opacity: 0.85,
onLeaflowDataReady: (data) => {
if (!data) return;
console.log("LeaflowData ready:", data.id);
},
});
leaflowLayer.addTo(map);
`
---
`tsx
import { MapContainer, TileLayer } from "react-leaflet";
import { LeaflowLayerComponent } from "leaflow-gl/react";
export function Main() {
return (
channelMapping={channelMapping}
heatmap={heatmap}
particles={particles}
dataChannel="rg"
flowDataChannel="rg"
flowDataFilter="Linear"
maxNativeZoom={6}
opacity={0.85}
/>
);
}
`
---
LeaflowLayer exposes its two internal layers:
- BackgroundLayer: extends L.GridLayer (tiles + heatmap)
- ParticlesLayer: WebGL/Three.js layer (particles renderer)
| Getter | Description |
| ---------------- | ------------------------------------ |
| get background | Returns the BackgroundLayer instance |get particles
| | Returns the ParticlesLayer instance |
> After changing background properties, call redraw() to force tiles refresh.
`ts`
const bg = leaflowLayer.background;
bg.tilesUrl = "https://another-server.com/tiles/{z}/{x}/{y}.png";
bg.redraw();
---
(CPU-side decoder)LeaflowData is a small utility to read physical values from the packed flow texture.
`ts`
data.at(x, y, "r"); // mapped physical value at pixel (x,y) for channel "r"
data.at(x, y, "rg"); // if supported by your helper, magnitude from 2 channels
---
Founded in 2021 and incubated at the รcole Polytechnique, Amphitrite merges satellite and in-situ data using artificial intelligence to provide accurate, high-resolution oceanographic information. Their technologies aim to optimize maritime operations, reduce environmental impact, and support sustainable shipping practices.
---
ยฉ 2025 Amphitrite. All rights reserved.
LeaflowGL is proprietary software developed and maintained by Amphitrite. Unauthorized reproduction, distribution, or modification of this software is prohibited without prior written consent from Amphitrite.
`
``