home / skills / saschabrunnerch / arcgis-maps-sdk-js-ai-context / arcgis-analysis-services

arcgis-analysis-services skill

/contexts/4.34/skills/arcgis-analysis-services

npx playbooks add skill saschabrunnerch/arcgis-maps-sdk-js-ai-context --skill arcgis-analysis-services

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
14.9 KB
---
name: arcgis-analysis-services
description: Perform spatial analysis, geometry operations, and use ArcGIS REST services. Use for routing, geocoding, geoprocessing, elevation analysis, viewshed, and feature queries with statistics.
---

# ArcGIS Analysis & Services

Use this skill for spatial analysis, geometry operations, REST services, and feature reduction (clustering/binning).

## Geometry Operators

ArcGIS Maps SDK provides geometry operators for client-side spatial operations.

### Loading Operators

```javascript
import bufferOperator from "@arcgis/core/geometry/operators/bufferOperator.js";
import intersectOperator from "@arcgis/core/geometry/operators/intersectOperator.js";
import centroidOperator from "@arcgis/core/geometry/operators/centroidOperator.js";

// Load before use (most operators require this)
await bufferOperator.load();
```

### Buffer
```javascript
import bufferOperator from "@arcgis/core/geometry/operators/bufferOperator.js";
await bufferOperator.load();

const buffered = bufferOperator.execute(point, 1000); // 1000 meters
```

### Geodesic Buffer (accurate over large distances)
```javascript
import geodesicBufferOperator from "@arcgis/core/geometry/operators/geodesicBufferOperator.js";
await geodesicBufferOperator.load();

const buffered = geodesicBufferOperator.execute(point, {
  distances: [100],
  unit: "kilometers"
});
```

### Intersect
```javascript
import intersectOperator from "@arcgis/core/geometry/operators/intersectOperator.js";
await intersectOperator.load();

const intersection = intersectOperator.execute(geometry1, geometry2);
```

### Union
```javascript
import unionOperator from "@arcgis/core/geometry/operators/unionOperator.js";
await unionOperator.load();

const unioned = unionOperator.execute([polygon1, polygon2, polygon3]);
```

### Centroid
```javascript
import centroidOperator from "@arcgis/core/geometry/operators/centroidOperator.js";
await centroidOperator.load();

const centroid = centroidOperator.execute(polygon);
```

### Contains / Within
```javascript
import containsOperator from "@arcgis/core/geometry/operators/containsOperator.js";
await containsOperator.load();

const isContained = containsOperator.execute(polygon, point); // true/false
```

### Distance
```javascript
import geodeticLengthOperator from "@arcgis/core/geometry/operators/geodeticLengthOperator.js";
await geodeticLengthOperator.load();

const length = geodeticLengthOperator.execute(polyline, { unit: "miles" });
```

### Area
```javascript
import geodeticAreaOperator from "@arcgis/core/geometry/operators/geodeticAreaOperator.js";
await geodeticAreaOperator.load();

const area = geodeticAreaOperator.execute(polygon, { unit: "square-kilometers" });
```

### Available Operators

| Operator | Purpose |
|----------|---------|
| `bufferOperator` | Create buffer around geometry |
| `geodesicBufferOperator` | Geodetic buffer (accurate) |
| `intersectOperator` | Find intersection |
| `unionOperator` | Combine geometries |
| `differenceOperator` | Subtract geometries |
| `clipOperator` | Clip geometry by envelope |
| `convexHullOperator` | Create convex hull |
| `centroidOperator` | Get centroid point |
| `containsOperator` | Test if contains |
| `withinOperator` | Test if within |
| `intersectsOperator` | Test if intersects |
| `distanceOperator` | Calculate planar distance |
| `geodeticLengthOperator` | Calculate geodetic length |
| `geodeticAreaOperator` | Calculate geodetic area |
| `simplifyOperator` | Simplify geometry |
| `densifyOperator` | Add vertices to geometry |
| `projectOperator` | Project to spatial reference |

## Analysis Objects

### ElevationProfileAnalysis
```javascript
import ElevationProfileAnalysis from "@arcgis/core/analysis/ElevationProfileAnalysis.js";

const analysis = new ElevationProfileAnalysis({
  profiles: [
    { type: "ground", color: "brown" },
    { type: "input", color: "blue" }
  ],
  displayUnits: {
    distance: "meters",
    elevation: "meters"
  }
});

// Add to SceneView
view.analyses.add(analysis);

// Set line geometry
analysis.geometry = polyline;

// Get analysis view for results
const analysisView = await view.whenAnalysisView(analysis);

// Watch progress and results
reactiveUtils.watch(
  () => analysisView.progress,
  (progress) => {
    if (progress === 1) {
      console.log("Results:", analysisView.results);
    }
  }
);
```

### LineOfSightAnalysis
```javascript
import LineOfSightAnalysis from "@arcgis/core/analysis/LineOfSightAnalysis.js";

const analysis = new LineOfSightAnalysis({
  observer: {
    type: "point",
    x: -122.4,
    y: 37.8,
    z: 100,
    spatialReference: { wkid: 4326 }
  },
  targets: [
    { type: "point", x: -122.41, y: 37.81, z: 50 },
    { type: "point", x: -122.42, y: 37.79, z: 75 }
  ]
});

view.analyses.add(analysis);
```

### ViewshedAnalysis
```javascript
import ViewshedAnalysis from "@arcgis/core/analysis/ViewshedAnalysis.js";

const analysis = new ViewshedAnalysis({
  observer: {
    type: "point",
    x: -122.4,
    y: 37.8,
    z: 100,
    spatialReference: { wkid: 4326 }
  },
  farDistance: 1000,
  heading: 45,
  tilt: 90,
  horizontalFieldOfView: 120,
  verticalFieldOfView: 90
});

view.analyses.add(analysis);
```

### ShadowCastAnalysis
```javascript
import ShadowCastAnalysis from "@arcgis/core/analysis/ShadowCastAnalysis.js";

const analysis = new ShadowCastAnalysis();
view.analyses.add(analysis);

// Configure date/time for shadow calculation
view.environment.lighting.date = new Date("2024-06-21T12:00:00");
```

### SliceAnalysis
```javascript
import SliceAnalysis from "@arcgis/core/analysis/SliceAnalysis.js";

const analysis = new SliceAnalysis({
  plane: {
    position: { type: "point", x: -122.4, y: 37.8, z: 50 },
    heading: 0,
    tilt: 0
  }
});

view.analyses.add(analysis);
```

## REST Services

### Routing

```javascript
import route from "@arcgis/core/rest/route.js";

const routeParams = {
  stops: [
    { geometry: { x: -122.4, y: 37.8 } },
    { geometry: { x: -122.5, y: 37.7 } }
  ],
  outSpatialReference: { wkid: 4326 },
  returnDirections: true,
  returnRoutes: true
};

const result = await route.solve(
  "https://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World",
  routeParams
);

const routeGeometry = result.routeResults[0].route.geometry;
const directions = result.routeResults[0].directions;
```

### Geocoding (Address to Location)

```javascript
import locator from "@arcgis/core/rest/locator.js";

const results = await locator.addressToLocations(
  "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer",
  {
    address: { SingleLine: "380 New York St, Redlands, CA" },
    outFields: ["*"],
    maxLocations: 5
  }
);

results.forEach(result => {
  console.log(result.address, result.location);
});
```

### Reverse Geocoding (Location to Address)

```javascript
import locator from "@arcgis/core/rest/locator.js";

const result = await locator.locationToAddress(
  "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer",
  {
    location: { x: -117.195, y: 34.057 }
  }
);

console.log(result.address);
```

### Geoprocessing

```javascript
import geoprocessor from "@arcgis/core/rest/geoprocessor.js";

const params = {
  inputLayer: featureSet,
  distance: 1000,
  distanceUnits: "Meters"
};

const result = await geoprocessor.execute(
  "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Utilities/Buffer/GPServer/Buffer",
  params
);

const outputFeatures = result.results[0].value;
```

### Print

```javascript
import print from "@arcgis/core/rest/print.js";

const params = {
  view: view,
  template: {
    format: "pdf",
    layout: "letter-ansi-a-landscape",
    layoutOptions: {
      titleText: "My Map"
    }
  }
};

const result = await print.execute(
  "https://utility.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task",
  params
);

// Download the PDF
window.open(result.url);
```

### Places Service

```javascript
import places from "@arcgis/core/rest/places.js";
import PlacesQueryParameters from "@arcgis/core/rest/support/PlacesQueryParameters.js";
import FetchPlaceParameters from "@arcgis/core/rest/support/FetchPlaceParameters.js";

// Query places near a point
const queryParams = new PlacesQueryParameters({
  categoryIds: ["4d4b7104d754a06370d81259"], // Arts and Entertainment
  radius: 500, // meters
  point: { type: "point", longitude: -87.626, latitude: 41.882 },
  icon: "png"
});

const results = await places.queryPlacesNearPoint(queryParams);

// Process results
results.results.forEach(place => {
  console.log(place.name, place.location, place.categories[0].label);
  console.log("Distance:", place.distance / 1000, "km");
});

// Fetch detailed information about a place
const fetchParams = new FetchPlaceParameters({
  placeId: results.results[0].placeId,
  requestedFields: ["all"]
});

const details = await places.fetchPlace(fetchParams);
const placeDetails = details.placeDetails;

console.log("Address:", placeDetails.address.streetAddress);
console.log("Phone:", placeDetails.contactInfo.telephone);
console.log("Website:", placeDetails.contactInfo.website);
```

### Places Category IDs

| Category | ID |
|----------|-----|
| Arts and Entertainment | `4d4b7104d754a06370d81259` |
| Business Services | `4d4b7105d754a06375d81259` |
| Community and Government | `63be6904847c3692a84b9b9a` |
| Dining and Drinking | `63be6904847c3692a84b9bb5` |
| Health and Medicine | `63be6904847c3692a84b9bb9` |
| Landmarks and Outdoors | `4d4b7105d754a06377d81259` |
| Retail | `4d4b7105d754a06378d81259` |
| Sports and Recreation | `4f4528bc4b90abdf24c9de85` |
| Travel and Transportation | `4d4b7105d754a06379d81259` |

## Feature Queries with Statistics

### Basic Statistics
```javascript
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
```javascript
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 Query
```javascript
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

```javascript
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
```javascript
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
```javascript
const binConfig = {
  type: "binning",
  fixedBinLevel: 4, // Level of detail
  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;
```

## Projection

```javascript
import projectOperator from "@arcgis/core/geometry/operators/projectOperator.js";
await projectOperator.load();

// Project to Web Mercator
const projected = projectOperator.execute(geometry, { wkid: 3857 });

// Project to WGS 84
const wgs84 = projectOperator.execute(geometry, { wkid: 4326 });
```

## TypeScript Usage

Feature reduction configurations use autocasting with `type` properties. For TypeScript safety, use `as const`:

```typescript
// Use 'as const' for type safety
const clusterConfig = {
  type: "cluster",
  clusterRadius: "100px",
  renderer: {
    type: "simple",
    symbol: {
      type: "simple-marker",
      color: "#69dcff"
    }
  } as const,
  labelingInfo: [{
    labelExpressionInfo: {
      expression: "Text($feature.cluster_count, '#,###')"
    },
    symbol: {
      type: "text",
      color: "white"
    } as const
  }]
} as const;

featureLayer.featureReduction = clusterConfig;
```

> **Tip:** See [arcgis-core-maps skill](../arcgis-core-maps/SKILL.md) for detailed guidance on autocasting vs explicit classes.

## Common Pitfalls

1. **Operator not loaded**: Most operators require `await operator.load()` before use

2. **API key required**: Some REST services (routing, geocoding) require an API key

3. **Analysis only in SceneView**: Some analyses (viewshed, line of sight) only work in 3D

4. **Cluster fields**: Use `cluster_count` to access feature count in clusters

5. **Statistics query returns features**: Statistics queries return features with calculated attributes, not raw numbers