arcgis-interaction について
ArcGIS Maps SDK の開発者向けに、ヒットテスト、ハイライト、スケッチ、イベント、座標変換の実装方法を示すスキルです。ハンドルやリスナーの後処理にも触れています。
主な機能
- 01レイヤーフィルター付きヒットテスト
- 02単一または複数フィーチャーの強調表示
- 03ジオメトリーの描画と編集
- 04ビューとレイヤーのイベント処理
- 05画面座標とマップ座標の変換
活用シーン
- 01マップのクリック後に属性を取得
- 02ホバーやクリックでフィーチャーを強調表示
- 03描画、取り消し、やり直し操作の追加
- 04ビュー状態やレイヤー更新への応答
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ saschabrunnerch/ arcgis-maps-sdk-js-ai-context --skill arcgis-interactionインストール後、Claude Code で/arcgis-interactionを入力して読み込まれたか確認します。
同梱ファイルAGENTS.md
詳細
ArcGIS の操作
ヒットテスト、フィーチャのハイライト、スケッチ、座標変換、イベント処理などのユーザー操作を実装する際に、このスキルを使用します。
インポートのパターン
直接 ESM インポート
import Draw from "@arcgis/core/views/draw/Draw.js";
import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js";
動的インポート(CDN)
const Draw = await $arcgis.import("@arcgis/core/views/draw/Draw.js");
const reactiveUtils = await $arcgis.import(
"@arcgis/core/core/reactiveUtils.js",
);
注: このスキルの例では、直接 ESM インポートを使用しています。CDN を使用する場合は、
import X from "path"をconst X = await $arcgis.import("path")に置き換えてください。
ヒットテスト
基本的なヒットテスト
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);
}
});
レイヤーフィルターを使ったヒットテスト
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],
});
});
ポインター移動時のヒットテスト
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";
}
});
ヒットテスト結果の型
hitTest は、結果オブジェクトの配列を含む ViewHitTestResult を返します。各結果には type があります。
| 結果の型 | 説明 |
|---|---|
graphic | レイヤーまたは GraphicsLayer のグラフィック |
media | ヒットしたメディア要素(ポップアップ内の画像) |
route | RouteLayer からヒットしたルート |
view.on("click", async (event) => {
const response = await view.hitTest(event);
response.results.forEach((result) => {
if (result.type === "graphic") {
console.log("Layer:", result.graphic.layer.title);
console.log("Attributes:", result.graphic.attributes);
}
});
});
ハイライト
フィーチャをハイライト
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();
クリック時にハイライト
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);
}
});
ハイライトのオプション
// Set highlight options on the view
view.highlightOptions = {
color: [255, 255, 0, 1],
haloOpacity: 0.9,
fillOpacity: 0.2,
};
スケッチ
Sketch コンポーネント(最も簡単な方法)
<arcgis-map basemap="topo-vector" center="139.5716,35.696" zoom="18">
<arcgis-sketch slot="top-right" creation-mode="update"></arcgis-sketch>
</arcgis-map>
arcgis-sketch コンポーネントは、スナップ、元に戻す/やり直し、選択に対応した、ポイント、ポリライン、ポリゴン、長方形、円のジオメトリーを描画するツールを提供します。
主な属性:
| 属性 | 型 | 説明 |
|---|---|---|
creation-mode | "single" | "update" | "continuous" | 作成後のスケッチグラフィックの処理方法 |
available-create-tools | string | 有効な作成ツールをカンマ区切りで指定したリスト |
multiple-selection-enabled | boolean | 複数のグラフィックを選択できるようにする |
update-on-graphic-click | boolean | グラフィックのクリック時に更新を開始する |
主なイベント:
| イベント | 説明 |
|---|---|
arcgisCreate | グラフィックの作成中に発生します |
arcgisUpdate | グラフィックの更新中に発生します |
arcgisDelete | グラフィックの削除時に発生します |
arcgisUndo | 元に戻す操作時に発生します |
arcgisRedo | やり直し操作時に発生します |
主なメソッド:
| メソッド | 説明 |
|---|---|
create(tool) | グラフィックの作成を開始します("point"、"polyline"、"polygon"、"rectangle"、"circle") |
update(graphics) | グラフィックの更新を開始します |
delete() | 選択したグラフィックを削除します |
undo() / redo() | 最後の操作を元に戻す/やり直します |
イベントを使った Sketch コンポーネント
<arcgis-map basemap="topo-vector">
<arcgis-sketch slot="top-right" creation-mode="update"></arcgis-sketch>
</arcgis-map>
<script type="module">
const sketch = document.querySelector("arcgis-sketch");
sketch.addEventListener("arcgisCreate", (event) => {
const { state, graphic } = event.detail;
if (state === "complete") {
console.log("Created:", graphic.geometry.type);
}
});
sketch.addEventListener("arcgisUpdate", (event) => {
const { state, graphics } = event.detail;
if (state === "complete") {
console.log("Updated:", graphics.length, "graphics");
}
});
sketch.addEventListener("arcgisDelete", (event) => {
console.log("Deleted:", event.detail.graphics.length, "graphics");
});
</script>
Sketch ウィジェット(コア API)
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);
});
描画ツール(低レベル)
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
});
イベント処理
ビューイベント
// Click (waits for potential double-click delay)
view.on("click", (event) => {
console.log("Map point:", event.mapPoint);
console.log("Screen point:", event.x, event.y);
});
// Immediate-click (fires without delay, recommended for hit testing)
view.on("immediate-click", (event) => {
console.log("Immediate click at:", event.mapPoint);
});
// Double-click
view.on("double-click", (event) => {
event.stopPropagation(); // Prevent default zoom
});
// Immediate-double-click (cannot be prevented by immediate-click stopPropagation)
view.on("immediate-double-click", (event) => {
console.log("Double clicked");
});
// 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
}
});
ヒント: ユーザーのクリックに遅延なく応答する場合(ヒットテストなど)は、
clickではなくimmediate-clickを使用してください。clickイベントはダブルクリックかどうかを確認するために待機するので、遅延が発生します。
reactiveUtils を使ったプロパティの監視
import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js";
// Watch for view stationary state
reactiveUtils.when(
() => view.stationary === true,
() => {
console.log("Navigation complete, extent:", view.extent);
},
);
// Watch zoom changes
reactiveUtils.watch(
() => view.zoom,
(zoom) => {
console.log("Zoom changed to:", zoom);
},
);
// Watch multiple properties
reactiveUtils.watch(
() => [view.center, view.zoom],
([center, zoom]) => {
console.log("View changed:", center, zoom);
},
);
// One-time wait
await reactiveUtils.whenOnce(() => view.ready);
console.log("View is ready");
レイヤーイベント
const layerView = await view.whenLayerView(featureLayer);
reactiveUtils.watch(
() => layerView.updating,
(updating) => {
if (!updating) {
console.log("Layer update complete");
}
},
);
座標変換
// Screen to map coordinates
const mapPoint = view.toMap({ x: screenX, y: screenY });
// Map to screen coordinates
const screenPoint = view.toScreen(mapPoint);
プログラムによるポップアップ制御
PopupTemplate の設定方法の詳細については、
arcgis-popup-templatesを参照してください。
// Open popup programmatically
view.openPopup({
title: "Custom Popup",
content: "Hello World",
location: view.center,
});
// Close popup
view.closePopup();
完全な例:ヒットテストとハイライト
マップコンポーネント
<!DOCTYPE html>
<html>
<head>
<script src="https://js.arcgis.com/5.0/"></script>
<script
type="module"
src="https://js.arcgis.com/5.0/map-components/"
></script>
<style>
html,
body {
height: 100%;
margin: 0;
}
</style>
</head>
<body>
<arcgis-map
basemap="streets-navigation-vector"
center="-118.805,34.027"
zoom="13"
>
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>
<script type="module">
const [FeatureLayer] = await $arcgis.import([
"@arcgis/core/layers/FeatureLayer.js",
]);
const mapElement = document.querySelector("arcgis-map");
const view = await mapElement.view;
await view.when();
const featureLayer = new FeatureLayer({
url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0",
});
mapElement.map.add(featureLayer);
let highlightHandle;
view.on("pointer-move", async (event) => {
const response = await view.hitTest(event, { include: featureLayer });
if (highlightHandle) {
highlightHandle.remove();
highlightHandle = null;
}
if (response.results.length > 0) {
const graphic = response.results[0].graphic;
const layerView = await view.whenLayerView(featureLayer);
highlightHandle = layerView.highlight(graphic);
document.body.style.cursor = "pointer";
} else {
document.body.style.cursor = "default";
}
});
</script>
</body>
</html>
参考サンプル
view-hittest- 画面座標にあるフィーチャーを特定するヒットテストmap-component-hittest- Map Components で hitTest を使ってフィーチャーにアクセスするsketch- ジオメトリーを描画する Sketch コンポーネントsketch-geometries- Sketch ウィジェットを使ったジオメトリーのスケッチsketch-update-validation- スケッチ更新の検証sketch-snapping-magnifier- Sketch でのスナップと拡大鏡
よくある落とし穴
-
ハイライトハンドルのリーク:以前のハイライトを削除せずに新しいハイライトを作成すると、メモリリークが発生します。
// Anti-pattern: creating highlights without cleaning up previous ones view.on("pointer-move", async (event) => { const response = await view.hitTest(event); if (response.results.length > 0) { const feature = response.results[0].graphic; // New highlight created every pointer-move - old ones never removed layerView.highlight(feature); } });// Correct: store handle and remove before creating a new highlight let highlightHandle = null; view.on("pointer-move", async (event) => { const response = await view.hitTest(event); if (highlightHandle) { highlightHandle.remove(); highlightHandle = null; } if (response.results.length > 0) { const feature = response.results[0].graphic; highlightHandle = layerView.highlight(feature); } });影響: ポインターを移動するたびに、以前のハイライトを削除せずに新しいハイライトが追加されます。ハイライトが画面上とメモリ内に蓄積し、パフォーマンスが低下します。
-
イベントが複数回発火する:クリーンアップせずにイベントリスナーを繰り返し追加すると、ハンドラーが蓄積します。
// Anti-pattern: adding listeners in a function that gets called multiple times function setupInteraction() { view.on("click", async (event) => { const result = await view.hitTest(event); console.log("Clicked:", result); }); } setupInteraction(); // First handler setupInteraction(); // Second handler - now click fires twice// Correct: store handle and remove before re-adding, or add once let clickHandle = null; function setupInteraction() { if (clickHandle) { clickHandle.remove(); } clickHandle = view.on("click", async (event) => { const result = await view.hitTest(event); console.log("Clicked:", result); }); }影響: セットアップ関数を呼び出すたびに、新しいリスナーが追加されます。クリックすると、同じコールバックが重複して複数回実行されます。
-
ヒットテストで何も返されない:レイヤーが正しく含まれているか、除外されているかを確認し、ビューの準備ができていることを確認してください。
-
immediate-click ではなく click を使用する:
clickイベントには、ダブルクリックかどうかを確認するための遅延が組み込まれています。応答性の高いヒットテストにはimmediate-clickを使用してください。 -
ポップアップが表示されない:レイヤーで
popupEnabled: true(デフォルト)が設定され、popupTemplateが指定されていることを確認してください。
関連するスキル
- 詳細な PopupTemplate の設定については、
arcgis-popup-templatesを参照してください。 - フィーチャー編集のワークフローについては、
arcgis-editingを参照してください。 - ビューの初期化とナビゲーションについては、
arcgis-core-mapsを参照してください。