arcgis-interaction

📁 saschabrunnerch/arcgis-maps-sdk-js-ai-context 📅 Jan 22, 2026
14
总安装量
8
周安装量
#23733
全站排名
安装命令
npx skills add https://github.com/saschabrunnerch/arcgis-maps-sdk-js-ai-context --skill arcgis-interaction

Agent 安装分布

opencode 6
antigravity 5
claude-code 5
gemini-cli 5
github-copilot 4

Skill 文档

ArcGIS Interaction

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

Popups

Basic PopupTemplate

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

PopupTemplate with Field Formatting

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

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

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

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

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

// 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

<arcgis-map basemap="streets-vector">
  <arcgis-popup slot="popup"></arcgis-popup>
</arcgis-map>

Hit Testing

Basic Hit Test

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

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

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

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

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

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

Editing

Editor Component (Simplest)

<arcgis-map item-id="YOUR_WEBMAP_ID">
  <arcgis-editor slot="top-right"></arcgis-editor>
</arcgis-map>

Editor Widget

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

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

// 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)

<arcgis-map basemap="topo-vector">
  <arcgis-sketch slot="top-right" creation-mode="update"></arcgis-sketch>
</arcgis-map>

Sketch Widget

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)

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

// 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

// 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

// 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

// 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

// 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:

// 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 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