<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>FlowRenderer in a 3D scene | Sample | ArcGIS Maps SDK for JavaScript</title>
<!-- Load the ArcGIS Maps SDK for JavaScript from CDN -->
<script type="module" src="https://js.arcgis.com/5.0/"></script>
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(3, auto);
<div class="calcite-mode-dark container">
<arcgis-scene item-id="e3d224978b384d7288bafee99e34c75a">
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle>
<arcgis-compass slot="top-left"></arcgis-compass>
<arcgis-expand slot="bottom-left">
<arcgis-legend></arcgis-legend>
icon-start="image-collection-explorer"
kind="neutral"></calcite-button>
reference-element="filter-panel-button"
<span>Multidimensional filter panel</span>
heading="Multidimensional Filter"
description="Filter the multidimensional layer to visualize a single slice defined as a combination of pressure and time.">
heading="Vertical Z dimension (StdPressure)"
selection-appearance="border"
selection-mode="single-persist">
heading="Wind at ~8km (Aviation)"
description="Winds data used for JetStream or aviation use cases.
They are collected at a pressure of 17,500 Pa (~8km above the sea level)">
heading="Wind at ~1.5km (Bird migration)"
description="Winds data used to study the bird migration. They are collected and visualized at a pressure of 85,000 Pa (~1.5 kilometers above the sea level)">
icon="relative-to-ground-elevation"
heading="Wind at ~10m (Surface winds)"
description="Winds data used for surface winds analysis. They are collected at a pressure of 100,000 Pa (~10m above the terrain)">
<calcite-block collapsible expanded heading="Time dimension (StdTime)" icon-start="clock">
<div id="month-grid" class="month-grid"></div>
<calcite-notice scale="s" open icon="open-book">
The wind data shown are monthly averages from 2005 to 2024, providing insight into
long-term patterns at each pressure level.
// Import necessary modules from the ArcGIS API for JavaScript
const [ImageryTileLayer, DimensionalDefinition, ColorVariable, reactiveUtils] =
"@arcgis/core/layers/ImageryTileLayer.js",
"@arcgis/core/layers/support/DimensionalDefinition.js",
"@arcgis/core/renderers/visualVariables/ColorVariable",
"@arcgis/core/core/reactiveUtils.js",
/*****************************************************************
* Component and view setup
*****************************************************************/
const viewElement = document.querySelector("arcgis-scene");
await viewElement.viewOnReady();
viewElement.constraints = {
/*****************************************************************
*****************************************************************/
// Get references to the main UI components from the DOM
const scenarioTileGroup = document.querySelector("calcite-tile-group");
const scenarioTiles = document.querySelectorAll("calcite-tile");
const filterPanel = document.getElementById("filter-panel");
const filterPanelButton = document.getElementById("filter-panel-button");
const loader = document.getElementById("loader");
let currentPressureValue = 17500; // Pressure value in Pa for "Aviation" scenario
let currentTimeSlice = 1; // January
/*****************************************************************
*****************************************************************/
// Create the imagery layer for displaying global wind data
// The initial definition uses the default pressure and time values
const windTileLayer = new ImageryTileLayer({
url: "https://tiledimageservices.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/Global_Average_Wind_Speeds_Month_Altitude/ImageServer",
{ value: 0, color: [0, 100, 255, 1], label: "0 m/s" },
{ value: 0.1, color: [0, 150, 255, 1] },
{ value: 3, color: [0, 200, 255, 1] },
{ value: 7, color: [0, 220, 130, 1] },
{ value: 15, color: [80, 255, 70, 1] },
{ value: 25, color: [200, 255, 40, 1] },
{ value: 40, color: [255, 255, 0, 1], label: ">40 m/s" },
multidimensionalDefinition: [
new DimensionalDefinition({
variableName: "Vector-MagDir",
dimensionName: "StdPressure",
values: [currentPressureValue],
new DimensionalDefinition({
variableName: "Vector-MagDir",
dimensionName: "StdTime",
values: [currentTimeSlice],
// Add the wind layer to the scene
viewElement.map.add(windTileLayer);
// Get the layer view for the windTileLayer
const windLayerView = await viewElement.view.whenLayerView(windTileLayer);
() => windLayerView.updating,
// Hide loader when updating completes
loader.style.display = isUpdating ? "flex" : "none";
/*****************************************************************
* Event Listeners and Initial Setup
*****************************************************************/
scenarioTiles.forEach((tile) => {
tile.addEventListener("click", () => {
filterPanelButton.addEventListener("click", () => {
isPanelOpen = !isPanelOpen;
filterPanel.style.display = isPanelOpen ? "flex" : "none";
* Function to update the layer's multidimensional definition
function updateLayerDefinition() {
windTileLayer.multidimensionalDefinition = [
new DimensionalDefinition({
variableName: "Vector-MagDir",
dimensionName: "StdPressure",
values: [currentPressureValue],
new DimensionalDefinition({
variableName: "Vector-MagDir",
dimensionName: "StdTime",
values: [currentTimeSlice],
/*****************************************************************
* Scenario and Elevation modes functions
*****************************************************************/
* Function to handle Z(Pressure) dimension changes
function zDimensionFilter() {
const selectedScenarioTile = scenarioTileGroup.querySelector("calcite-tile[selected]");
const scenarioTitle = selectedScenarioTile?.heading;
case "Wind at ~1.5km (Bird migration)":
currentPressureValue = 85000;
case "Wind at ~10m (Surface winds)":
currentPressureValue = 100000;
default: // "Wind at ~8km (Aviation)"
currentPressureValue = 17500;
// Adjust the layer elevation offset to the new pressure level
windTileLayer.elevationInfo = {
offset: newOffset, //meters
/*****************************************************************
*****************************************************************/
* Function to handle time dimension changes
function timeDimensionFilter() {
{ name: "Jan", value: 1 },
{ name: "Apr", value: 4 },
{ name: "Jul", value: 7 },
{ name: "Oct", value: 10 },
const grid = document.getElementById("month-grid");
monthData.forEach(({ name, value }, i) => {
const btn = document.createElement("calcite-button");
btn.setAttribute("round", "true");
btn.setAttribute("appearance", i === 0 ? "solid" : "outline-fill"); // First month selected
btn.setAttribute("label", value.toString()); // Actual month number
grid.addEventListener("click", (event) => {
const target = event.target.closest("calcite-button");
grid.querySelectorAll("calcite-button").forEach((btn) => {
btn.setAttribute("appearance", "outline-fill");
target.setAttribute("appearance", "solid");
const monthNumber = parseInt(target.getAttribute("label")); // Actual month number
currentTimeSlice = monthNumber;
/*****************************************************************
*****************************************************************/
* Function to add an offset to labels
const labelsLayer = viewElement.map.allLayers.items.find(
(layer) => layer.title === "Places and Labels",
labelsLayer.labelingInfo.forEach((labelClass) => {
labelClass.labelPlacement = "above-center";
labelClass.symbol.verticalOffset = { screenLength: 8 };