Perform spatial analysis using analysis objects, REST services for routing/geocoding/geoprocessing, and feature reduction with clustering/binning.
72
66%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Optimize this skill with Tessl
npx tessl skill review --optimize ./contexts/5.0/skills/arcgis-spatial-analysis/SKILL.mdUse 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 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";const [ViewshedAnalysis, Viewshed] = await $arcgis.import([
"@arcgis/core/analysis/ViewshedAnalysis.js",
"@arcgis/core/analysis/Viewshed.js"
]);Analysis objects are added to a SceneView's analyses collection. All analysis types require SceneView (not MapView).
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);
}
}
);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();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();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);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);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);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 }
);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);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 }
);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 | 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[]) |
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);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 populationconst 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);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; // Enableconst 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 }
]
}]
}
};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;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 observer directly on ViewshedAnalysis. Create Viewshed objects and add them to the viewsheds collection:
// Anti-pattern
new ViewshedAnalysis({ observer: point, farDistance: 1000 });
// Correct
new ViewshedAnalysis({
viewsheds: [new Viewshed({ observer: point, farDistance: 1000 })]
});Cluster field names: Use cluster_count (not clusterCount) 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 === 1 or watch analysisView.result to know when analysis results are ready. Don't read results before they're computed.
SliceAnalysis uses shape not plane: The property is shape of type SlicePlane, not plane:
// Anti-pattern
new SliceAnalysis({ plane: slicePlane });
// Correct
new SliceAnalysis({ shape: slicePlane });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 statisticsarcgis-geometry-operations for client-side geometry operations (buffer, union, intersect, etc.).arcgis-rest-services for server-side REST services (routing, geocoding, geoprocessing).arcgis-coordinates-projection for coordinate projection and formatting.arcgis-layers for FeatureLayer queries and layer configuration.d84407b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.