ArcGIS Spatial Analysis
Use this skill for spatial analysis objects (3D analyses), feature queries with statistics, and feature reduction (clustering/binning).
For geometry operators (buffer, union, intersect, clip, offset, etc.), see
arcgis-geometry-operations. For routing, geocoding, and geoprocessing REST services, seearcgis-rest-services.
Import Patterns
Direct ESM Imports
import ViewshedAnalysis from "@arcgis/core/analysis/ViewshedAnalysis.js";
import Viewshed from "@arcgis/core/analysis/Viewshed.js";
import LineOfSightAnalysis from "@arcgis/core/analysis/LineOfSightAnalysis.js";
import ElevationProfileAnalysis from "@arcgis/core/analysis/ElevationProfileAnalysis.js";
import SliceAnalysis from "@arcgis/core/analysis/SliceAnalysis.js";
import ShadowCastAnalysis from "@arcgis/core/analysis/ShadowCastAnalysis.js";
Dynamic Imports (CDN)
const [ViewshedAnalysis, Viewshed] = await $arcgis.import([
"@arcgis/core/analysis/ViewshedAnalysis.js",
"@arcgis/core/analysis/Viewshed.js"
]);
Analysis Objects (3D)
Analysis objects are added to a SceneView's analyses collection. All analysis types require SceneView (not MapView).
ElevationProfileAnalysis
import ElevationProfileAnalysis from "@arcgis/core/analysis/ElevationProfileAnalysis.js";
import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js";
const analysis = new ElevationProfileAnalysis({
profiles: [
{ type: "ground", color: "brown" },
{ type: "input", color: "blue" }
],
displayUnits: {
distance: "meters",
elevation: "meters"
}
});
view.analyses.add(analysis);
analysis.geometry = polyline;
// Get analysis view for results
const analysisView = await view.whenAnalysisView(analysis);
reactiveUtils.watch(
() => analysisView.progress,
(progress) => {
if (progress === 1) {
console.log("Results:", analysisView.results);
}
}
);
Interactive Analysis Placement
Analysis views support interactive placement via the place() method:
import * as promiseUtils from "@arcgis/core/core/promiseUtils.js";
const analysisView = await view.whenAnalysisView(analysis);
const abortController = new AbortController();
try {
await analysisView.place({ signal: abortController.signal });
} catch (error) {
if (!promiseUtils.isAbortError(error)) throw error;
}
// To cancel placement
abortController.abort();
ViewshedAnalysis
Uses a viewsheds collection of Viewshed objects (not direct observer property).
import ViewshedAnalysis from "@arcgis/core/analysis/ViewshedAnalysis.js";
import Viewshed from "@arcgis/core/analysis/Viewshed.js";
const viewshed = new Viewshed({
observer: {
type: "point",
x: -122.4, y: 37.8, z: 100,
spatialReference: { wkid: 4326 }
},
farDistance: 1000,
heading: 45,
tilt: 90,
horizontalFieldOfView: 120,
verticalFieldOfView: 90
});
const analysis = new ViewshedAnalysis({
viewsheds: [viewshed]
});
view.analyses.add(analysis);
// Add more viewsheds dynamically
analysis.viewsheds.add(new Viewshed({
observer: { type: "point", x: -122.41, y: 37.79, z: 80, spatialReference: { wkid: 4326 } },
farDistance: 500,
heading: 0,
tilt: 90,
horizontalFieldOfView: 360,
verticalFieldOfView: 90
}));
// Clear all viewsheds
analysis.clear();
LineOfSightAnalysis
Uses LineOfSightAnalysisObserver and LineOfSightAnalysisTarget wrapper objects.
import LineOfSightAnalysis from "@arcgis/core/analysis/LineOfSightAnalysis.js";
import LineOfSightAnalysisObserver from "@arcgis/core/analysis/LineOfSightAnalysisObserver.js";
import LineOfSightAnalysisTarget from "@arcgis/core/analysis/LineOfSightAnalysisTarget.js";
const analysis = new LineOfSightAnalysis({
observer: new LineOfSightAnalysisObserver({
position: {
type: "point",
x: -122.4, y: 37.8, z: 100,
spatialReference: { wkid: 4326 }
}
}),
targets: [
new LineOfSightAnalysisTarget({
position: { type: "point", x: -122.41, y: 37.81, z: 50, spatialReference: { wkid: 4326 } }
}),
new LineOfSightAnalysisTarget({
position: { type: "point", x: -122.42, y: 37.79, z: 75, spatialReference: { wkid: 4326 } }
})
]
});
view.analyses.add(analysis);
ShadowCastAnalysis
Uses date, startTimeOfDay, endTimeOfDay, and mode properties.
import ShadowCastAnalysis from "@arcgis/core/analysis/ShadowCastAnalysis.js";
const analysis = new ShadowCastAnalysis({
date: new Date("2025-06-21"),
startTimeOfDay: 21600000, // 6:00 AM (milliseconds from midnight)
endTimeOfDay: 57600000, // 4:00 PM
mode: "total-duration", // "total-duration", "discrete", "min-duration"
utcOffset: -7 // UTC offset in hours
});
view.analyses.add(analysis);
SliceAnalysis
Uses a shape property of type SlicePlane (not plane).
import SliceAnalysis from "@arcgis/core/analysis/SliceAnalysis.js";
import SlicePlane from "@arcgis/core/analysis/SlicePlane.js";
const analysis = new SliceAnalysis({
shape: new SlicePlane({
position: { type: "point", x: -122.4, y: 37.8, z: 50, spatialReference: { wkid: 4326 } },
heading: 0,
tilt: 0,
width: 200,
height: 200
}),
tiltEnabled: false,
excludeGroundSurface: false
});
view.analyses.add(analysis);
AreaMeasurementAnalysis
import AreaMeasurementAnalysis from "@arcgis/core/analysis/AreaMeasurementAnalysis.js";
const analysis = new AreaMeasurementAnalysis();
view.analyses.add(analysis);
// Set geometry after user draws or programmatically
analysis.geometry = polygon;
// Watch for results
const analysisView = await view.whenAnalysisView(analysis);
reactiveUtils.watch(
() => analysisView.result,
(result) => {
if (result) {
console.log("Area:", result.area?.value, result.area?.unit);
console.log("Perimeter:", result.perimeterLength?.value, result.perimeterLength?.unit);
}
},
{ initial: true }
);
DistanceMeasurementAnalysis
Uses a geometry property (Polyline), not startPoint/endPoint.
import DistanceMeasurementAnalysis from "@arcgis/core/analysis/DistanceMeasurementAnalysis.js";
const analysis = new DistanceMeasurementAnalysis({
geometry: {
type: "polyline",
paths: [[
[-122.4, 37.8, 0],
[-122.41, 37.81, 0]
]],
spatialReference: { wkid: 4326 }
}
});
view.analyses.add(analysis);
VolumeMeasurementAnalysis
import VolumeMeasurementAnalysis from "@arcgis/core/analysis/VolumeMeasurementAnalysis.js";
const analysis = new VolumeMeasurementAnalysis();
view.analyses.add(analysis);
analysis.geometry = polygon;
// Watch for results
const analysisView = await view.whenAnalysisView(analysis);
reactiveUtils.watch(
() => analysisView.result,
(result) => {
if (result) {
console.log("Cut volume:", result.cutVolume?.value, result.cutVolume?.unit);
console.log("Fill volume:", result.fillVolume?.value, result.fillVolume?.unit);
}
},
{ initial: true }
);
DimensionAnalysis
import DimensionAnalysis from "@arcgis/core/analysis/DimensionAnalysis.js";
import LengthDimension from "@arcgis/core/analysis/LengthDimension.js";
const dimension = new LengthDimension({
startPoint: { type: "point", x: -122.4, y: 37.8, z: 0, spatialReference: { wkid: 4326 } },
endPoint: { type: "point", x: -122.41, y: 37.81, z: 0, spatialReference: { wkid: 4326 } }
});
const analysis = new DimensionAnalysis({
dimensions: [dimension]
});
view.analyses.add(analysis);
Analysis Types Summary
| Analysis | Use Case | Constructor Key |
|----------|----------|-----------------|
| ViewshedAnalysis | Visible area from observer | viewsheds (Collection of Viewshed) |
| LineOfSightAnalysis | Visibility between points | observer, targets (wrapper objects) |
| ShadowCastAnalysis | Shadow visualization | date, startTimeOfDay, endTimeOfDay, mode |
| SliceAnalysis | Cross-section of 3D content | shape (SlicePlane) |
| ElevationProfileAnalysis | Terrain profile along a line | profiles, geometry (Polyline) |
| AreaMeasurementAnalysis | 3D area measurement | geometry (Polygon) |
| DistanceMeasurementAnalysis | 3D distance measurement | geometry (Polyline) |
| VolumeMeasurementAnalysis | Cut/fill volume | geometry (Polygon) |
| DimensionAnalysis | Length dimensions in 3D | dimensions (LengthDimension[]) |
Feature Queries with Statistics
Basic Statistics
const query = featureLayer.createQuery();
query.outStatistics = [
{ statisticType: "sum", onStatisticField: "population", outStatisticFieldName: "totalPop" },
{ statisticType: "avg", onStatisticField: "population", outStatisticFieldName: "avgPop" },
{ statisticType: "max", onStatisticField: "population", outStatisticFieldName: "maxPop" },
{ statisticType: "min", onStatisticField: "population", outStatisticFieldName: "minPop" },
{ statisticType: "count", onStatisticField: "population", outStatisticFieldName: "count" },
{ statisticType: "stddev", onStatisticField: "population", outStatisticFieldName: "stdDev" }
];
const result = await featureLayer.queryFeatures(query);
console.log(result.features[0].attributes);
Group By Statistics
const query = featureLayer.createQuery();
query.groupByFieldsForStatistics = ["state"];
query.outStatistics = [{
statisticType: "sum",
onStatisticField: "population",
outStatisticFieldName: "totalPop"
}];
query.orderByFields = ["totalPop DESC"];
const result = await featureLayer.queryFeatures(query);
// Returns one feature per state with total population
Spatial Statistics
const query = featureLayer.createQuery();
query.geometry = view.extent;
query.spatialRelationship = "intersects";
query.outStatistics = [{
statisticType: "count",
onStatisticField: "ObjectID",
outStatisticFieldName: "featureCount"
}];
const result = await featureLayer.queryFeatures(query);
console.log("Features in view:", result.features[0].attributes.featureCount);
Feature Reduction
Clustering
const clusterConfig = {
type: "cluster",
clusterRadius: "100px",
clusterMinSize: "24px",
clusterMaxSize: "60px",
popupTemplate: {
title: "Cluster summary",
content: "This cluster represents {cluster_count} features.",
fieldInfos: [{
fieldName: "cluster_count",
format: { digitSeparator: true, places: 0 }
}]
},
labelingInfo: [{
deconflictionStrategy: "none",
labelExpressionInfo: {
expression: "Text($feature.cluster_count, '#,###')"
},
symbol: {
type: "text",
color: "white",
font: { size: "12px", weight: "bold" }
},
labelPlacement: "center-center"
}]
};
featureLayer.featureReduction = clusterConfig;
// Toggle clustering
featureLayer.featureReduction = null; // Disable
featureLayer.featureReduction = clusterConfig; // Enable
Cluster with Aggregated Fields
const clusterConfig = {
type: "cluster",
clusterRadius: "100px",
fields: [{
name: "avg_magnitude",
statisticType: "avg",
onStatisticField: "magnitude"
}, {
name: "total_count",
statisticType: "count",
onStatisticField: "ObjectID"
}],
renderer: {
type: "simple",
symbol: {
type: "simple-marker",
style: "circle",
color: "#69dcff"
},
visualVariables: [{
type: "size",
field: "total_count",
stops: [
{ value: 1, size: 8 },
{ value: 100, size: 40 }
]
}]
}
};
Binning
const binConfig = {
type: "binning",
fixedBinLevel: 4,
renderer: {
type: "simple",
symbol: {
type: "simple-fill",
outline: { color: "white", width: 0.5 }
},
visualVariables: [{
type: "color",
field: "aggregateCount",
stops: [
{ value: 1, color: "#feebe2" },
{ value: 50, color: "#fbb4b9" },
{ value: 100, color: "#f768a1" },
{ value: 500, color: "#c51b8a" },
{ value: 1000, color: "#7a0177" }
]
}]
},
popupTemplate: {
title: "Bin",
content: "{aggregateCount} features in this bin"
}
};
featureLayer.featureReduction = binConfig;
Common Pitfalls
-
Analysis objects are SceneView-only: All analysis types (viewshed, line of sight, shadow cast, etc.) require 3D SceneView. They do not work in MapView.
-
ViewshedAnalysis uses a viewsheds collection: Do not set
observerdirectly on ViewshedAnalysis. CreateViewshedobjects and add them to theviewshedscollection:// Anti-pattern new ViewshedAnalysis({ observer: point, farDistance: 1000 }); // Correct new ViewshedAnalysis({ viewsheds: [new Viewshed({ observer: point, farDistance: 1000 })] }); -
Cluster field names: Use
cluster_count(notclusterCount) to access the feature count in cluster popups and labels. -
Statistics queries return features: Statistics queries return features with calculated attributes, not raw numbers. Access results via
result.features[0].attributes. -
Analysis progress: Check
analysisView.progress === 1or watchanalysisView.resultto know when analysis results are ready. Don't read results before they're computed. -
SliceAnalysis uses
shapenotplane: The property isshapeof typeSlicePlane, notplane:// Anti-pattern new SliceAnalysis({ plane: slicePlane }); // Correct new SliceAnalysis({ shape: slicePlane });
Reference Samples
analysis-viewshed- Interactive viewshed analysis in 3Danalysis-objects- Multiple analysis types in a single sceneanalysis-elevation-profile- Elevation profile along a lineanalysis-shadow-cast- Shadow cast duration visualizationanalysis-area-measurement- Area measurement on 3D surfacesanalysis-volume-measurement- Cut/fill volume measurementline-of-sight- Line of sight visibility analysisfeaturereduction-cluster- Feature clusteringfeaturereduction-binning- Feature binning for aggregationfeaturereduction-cluster-aggregate-fields- Clusters with aggregated statistics
Related Skills
- See
arcgis-geometry-operationsfor client-side geometry operations (buffer, union, intersect, etc.). - See
arcgis-rest-servicesfor server-side REST services (routing, geocoding, geoprocessing). - See
arcgis-coordinates-projectionfor coordinate projection and formatting. - See
arcgis-layersfor FeatureLayer queries and layer configuration.