home / skills / saschabrunnerch / arcgis-maps-sdk-js-ai-context / arcgis-interaction

arcgis-interaction skill

/contexts/4.34/skills/arcgis-interaction

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

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

Files (1)
SKILL.md
11.7 KB
---
name: arcgis-interaction
description: Handle user interaction with map features including popups, editing, sketching, and event handling. Use for creating interactive map applications with feature selection, editing workflows, and custom interactions.
---

# ArcGIS Interaction

Use this skill when implementing user interactions like popups, editing, sketching, hit testing, and event handling.

## Popups

### Basic PopupTemplate
```javascript
const layer = new FeatureLayer({
  url: "...",
  popupTemplate: {
    title: "{name}",
    content: "Population: {population}"
  }
});
```

### PopupTemplate with Field Formatting
```javascript
const popupTemplate = {
  title: "Feature: {name}",
  content: [{
    type: "fields",
    fieldInfos: [
      {
        fieldName: "population",
        label: "Population",
        format: {
          digitSeparator: true,
          places: 0
        }
      },
      {
        fieldName: "date_created",
        label: "Created",
        format: {
          dateFormat: "short-date"
        }
      },
      {
        fieldName: "area_sqkm",
        label: "Area (km²)",
        format: {
          places: 2
        }
      }
    ]
  }]
};
```

### Multiple Content Elements
```javascript
const popupTemplate = {
  title: "{name}",
  content: [
    {
      type: "text",
      text: "<b>Description:</b> {description}"
    },
    {
      type: "fields",
      fieldInfos: [...]
    },
    {
      type: "media",
      mediaInfos: [{
        type: "image",
        value: {
          sourceURL: "{image_url}"
        }
      }]
    },
    {
      type: "attachments"
    }
  ]
};
```

### Custom Content Function
```javascript
const popupTemplate = {
  title: "{name}",
  content: (feature) => {
    const div = document.createElement("div");
    div.innerHTML = `
      <p>Custom content for ${feature.graphic.attributes.name}</p>
      <button id="customBtn">Click me</button>
    `;
    return div;
  }
};
```

### Arcade Expressions in Popups
```javascript
const popupTemplate = {
  title: "{name}",
  expressionInfos: [{
    name: "density",
    title: "Population Density",
    expression: "Round($feature.population / $feature.area_sqkm, 2)"
  }],
  content: "Population Density: {expression/density} people/km²"
};
```

### Popup Actions
```javascript
const measureAction = {
  title: "Measure Length",
  id: "measure-this",
  icon: "measure"
};

const popupTemplate = {
  title: "{name}",
  content: "{description}",
  actions: [measureAction]
};

// Listen for action clicks
import reactiveUtils from "@arcgis/core/core/reactiveUtils.js";

reactiveUtils.on(
  () => view.popup,
  "trigger-action",
  (event) => {
    if (event.action.id === "measure-this") {
      const geometry = view.popup.selectedFeature.geometry;
      // Do something with the geometry
    }
  }
);
```

### Programmatic Popup Control
```javascript
// Open popup at location
view.openPopup({
  title: "Custom Popup",
  content: "Hello World",
  location: view.center
});

// Open popup with features
view.openPopup({
  features: [graphic1, graphic2],
  location: mapPoint
});

// Close popup
view.closePopup();

// Access popup properties
const selectedFeature = view.popup.selectedFeature;
const isVisible = view.popup.visible;
```

### Popup Component
```html
<arcgis-map basemap="streets-vector">
  <arcgis-popup slot="popup"></arcgis-popup>
</arcgis-map>
```

## Hit Testing

### Basic Hit Test
```javascript
view.on("click", async (event) => {
  const response = await view.hitTest(event);

  if (response.results.length > 0) {
    const graphic = response.results[0].graphic;
    console.log("Clicked feature:", graphic.attributes);
  }
});
```

### Hit Test with Layer Filter
```javascript
view.on("click", async (event) => {
  const response = await view.hitTest(event, {
    include: [featureLayer] // Only test this layer
  });

  // Or exclude layers
  const response2 = await view.hitTest(event, {
    exclude: [graphicsLayer]
  });
});
```

### Pointer Move Hit Test
```javascript
view.on("pointer-move", async (event) => {
  const response = await view.hitTest(event, {
    include: featureLayer
  });

  if (response.results.length > 0) {
    document.body.style.cursor = "pointer";
  } else {
    document.body.style.cursor = "default";
  }
});
```

## Highlighting

### Highlight Features
```javascript
const layerView = await view.whenLayerView(featureLayer);

// Highlight a single feature
const highlight = layerView.highlight(graphic);

// Highlight multiple features
const highlight = layerView.highlight([graphic1, graphic2]);

// Highlight by object IDs
const highlight = layerView.highlight([1, 2, 3]);

// Remove highlight
highlight.remove();
```

### Highlight on Click
```javascript
let highlightHandle;

view.on("click", async (event) => {
  // Remove previous highlight
  if (highlightHandle) {
    highlightHandle.remove();
  }

  const response = await view.hitTest(event, { include: featureLayer });

  if (response.results.length > 0) {
    const graphic = response.results[0].graphic;
    const layerView = await view.whenLayerView(featureLayer);
    highlightHandle = layerView.highlight(graphic);
  }
});
```

### Highlight Options
```javascript
// Set highlight options on the layer view
layerView.highlightOptions = {
  color: [255, 255, 0, 1],
  haloOpacity: 0.9,
  fillOpacity: 0.2
};
```

## Editing

### Editor Component (Simplest)
```html
<arcgis-map item-id="YOUR_WEBMAP_ID">
  <arcgis-editor slot="top-right"></arcgis-editor>
</arcgis-map>
```

### Editor Widget
```javascript
import Editor from "@arcgis/core/widgets/Editor.js";

const editor = new Editor({
  view: view,
  layerInfos: [{
    layer: featureLayer,
    formTemplate: {
      elements: [
        { type: "field", fieldName: "name" },
        { type: "field", fieldName: "description" }
      ]
    }
  }]
});

view.ui.add(editor, "top-right");
```

### FeatureForm
```javascript
import FeatureForm from "@arcgis/core/widgets/FeatureForm.js";

const featureForm = new FeatureForm({
  container: "formDiv",
  layer: featureLayer,
  formTemplate: {
    title: "Edit Feature",
    elements: [
      {
        type: "field",
        fieldName: "name",
        label: "Name"
      },
      {
        type: "field",
        fieldName: "type",
        label: "Type"
      }
    ]
  }
});

// Set feature to edit
featureForm.feature = graphic;

// Listen for submit
featureForm.on("submit", () => {
  const values = featureForm.getValues();
  // Update feature attributes
  Object.keys(values).forEach(key => {
    graphic.attributes[key] = values[key];
  });
});

// Submit programmatically
featureForm.submit();
```

### applyEdits API

```javascript
// Add features
const edits = {
  addFeatures: [newGraphic]
};
const result = await featureLayer.applyEdits(edits);
console.log("Added:", result.addFeatureResults);

// Update features
const edits = {
  updateFeatures: [updatedGraphic]
};
const result = await featureLayer.applyEdits(edits);

// Delete features
const edits = {
  deleteFeatures: [graphicToDelete]
};
const result = await featureLayer.applyEdits(edits);

// Combined edits
const edits = {
  addFeatures: [newGraphic1, newGraphic2],
  updateFeatures: [updatedGraphic],
  deleteFeatures: [deleteGraphic]
};
const result = await featureLayer.applyEdits(edits);
```

## Sketching

### Sketch Component (Simplest)
```html
<arcgis-map basemap="topo-vector">
  <arcgis-sketch slot="top-right" creation-mode="update"></arcgis-sketch>
</arcgis-map>
```

### Sketch Widget
```javascript
import Sketch from "@arcgis/core/widgets/Sketch.js";
import GraphicsLayer from "@arcgis/core/layers/GraphicsLayer.js";

const graphicsLayer = new GraphicsLayer();
map.add(graphicsLayer);

const sketch = new Sketch({
  view: view,
  layer: graphicsLayer,
  creationMode: "update" // or "single", "continuous"
});

view.ui.add(sketch, "top-right");

// Listen for events
sketch.on("create", (event) => {
  if (event.state === "complete") {
    console.log("Created:", event.graphic);
  }
});

sketch.on("update", (event) => {
  if (event.state === "complete") {
    console.log("Updated:", event.graphics);
  }
});

sketch.on("delete", (event) => {
  console.log("Deleted:", event.graphics);
});
```

### Draw Tool (Low-level)
```javascript
import Draw from "@arcgis/core/views/draw/Draw.js";

const draw = new Draw({ view: view });

// Create a polygon
const action = draw.create("polygon");

action.on("vertex-add", (event) => {
  console.log("Vertex added:", event.vertices);
});

action.on("draw-complete", (event) => {
  const polygon = {
    type: "polygon",
    rings: event.vertices,
    spatialReference: view.spatialReference
  };
  // Create graphic with polygon
});
```

## Event Handling

### View Events
```javascript
// Click
view.on("click", (event) => {
  console.log("Map point:", event.mapPoint);
  console.log("Screen point:", event.x, event.y);
});

// Double-click
view.on("double-click", (event) => {
  event.stopPropagation(); // Prevent default zoom
});

// Pointer move
view.on("pointer-move", (event) => {
  const point = view.toMap(event);
  console.log("Coordinates:", point.longitude, point.latitude);
});

// Drag
view.on("drag", (event) => {
  if (event.action === "start") { }
  if (event.action === "update") { }
  if (event.action === "end") { }
});

// Key events
view.on("key-down", (event) => {
  if (event.key === "Escape") {
    // Cancel operation
  }
});
```

### Property Watching
```javascript
// Watch single property
view.watch("zoom", (newZoom) => {
  console.log("Zoom changed to:", newZoom);
});

// Watch multiple properties
view.watch(["center", "zoom"], ([center, zoom]) => {
  console.log("View changed:", center, zoom);
});

// Watch stationary (after navigation completes)
view.watch("stationary", (isStationary) => {
  if (isStationary) {
    console.log("Navigation complete");
  }
});

// One-time watch
import { when } from "@arcgis/core/core/reactiveUtils.js";

await when(() => view.stationary === true);
console.log("View is now stationary");
```

### Layer Events
```javascript
// Layer view updating
const layerView = await view.whenLayerView(featureLayer);

layerView.watch("updating", (updating) => {
  if (updating) {
    console.log("Layer is updating...");
  } else {
    console.log("Layer update complete");
  }
});
```

### Widget Events
```javascript
// Search widget
searchWidget.on("select-result", (event) => {
  console.log("Selected:", event.result);
});

// Sketch widget
sketchWidget.on("create", (event) => {
  if (event.state === "complete") {
    console.log("Sketch complete");
  }
});
```

## Coordinate Conversion

```javascript
// Screen to map coordinates
const mapPoint = view.toMap({ x: screenX, y: screenY });

// Map to screen coordinates
const screenPoint = view.toScreen(mapPoint);
```

## TypeScript Usage

Popup and symbol configurations use autocasting with `type` properties. For TypeScript safety, use `as const`:

```typescript
// Use 'as const' for popup content types
layer.popupTemplate = {
  title: "{name}",
  content: [{
    type: "fields",
    fieldInfos: [
      { fieldName: "name", label: "Name" }
    ]
  }]
} as const;

// Use 'as const' for symbol configurations
const graphic = new Graphic({
  geometry: point,
  symbol: {
    type: "simple-marker",
    color: "red",
    size: 12
  } as const
});
```

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

## Common Pitfalls

1. **Popup not showing**: Ensure layer has `popupEnabled: true` (default)

2. **Hit test returns nothing**: Check if layers are included/excluded correctly

3. **Highlight not visible**: Make sure to store the highlight handle and call `remove()` before creating new highlights

4. **applyEdits fails**: Ensure layer is editable and user has edit permissions

5. **Events fire multiple times**: Remove event handlers when no longer needed