@saschabrunnerch/arcgis-maps-sdk-js-ai-context 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contexts/4.34/skills/arcgis-authentication/AGENTS.md +109 -0
- package/contexts/4.34/skills/arcgis-authentication/SKILL.md +39 -2
- package/contexts/4.34/skills/arcgis-core-maps/AGENTS.md +86 -0
- package/contexts/4.34/skills/arcgis-core-maps/SKILL.md +53 -4
- package/contexts/4.34/skills/arcgis-editing/AGENTS.md +98 -0
- package/contexts/4.34/skills/arcgis-editing/SKILL.md +90 -3
- package/contexts/4.34/skills/arcgis-interaction/AGENTS.md +108 -0
- package/contexts/4.34/skills/arcgis-interaction/SKILL.md +65 -2
- package/contexts/4.34/skills/arcgis-layers/AGENTS.md +89 -0
- package/contexts/4.34/skills/arcgis-layers/SKILL.md +69 -4
- package/contexts/4.34/skills/arcgis-performance/SKILL.md +801 -0
- package/contexts/4.34/skills/arcgis-starter-app/AGENTS.md +99 -0
- package/contexts/4.34/skills/arcgis-starter-app/SKILL.md +138 -0
- package/contexts/4.34/skills/arcgis-visualization/AGENTS.md +91 -0
- package/contexts/4.34/skills/arcgis-visualization/SKILL.md +59 -6
- package/contexts/4.34/skills/arcgis-widgets-ui/AGENTS.md +86 -0
- package/contexts/4.34/skills/arcgis-widgets-ui/SKILL.md +48 -2
- package/package.json +3 -2
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Agent Guide: arcgis-authentication
|
|
2
|
+
|
|
3
|
+
Quick-reference decisions, checklists, and tables for authentication and authorization.
|
|
4
|
+
|
|
5
|
+
## API Key vs OAuth vs Identity Manager Decision Tree
|
|
6
|
+
|
|
7
|
+
1. **Does the app access only public basemaps, geocoding, or routing?**
|
|
8
|
+
- Yes --> **API Key** (simplest)
|
|
9
|
+
|
|
10
|
+
2. **Does the app need to access the user's private content (web maps, feature services)?**
|
|
11
|
+
- Yes --> **OAuth 2.0** (user signs in)
|
|
12
|
+
|
|
13
|
+
3. **Is this a server-side app with no user interaction?**
|
|
14
|
+
- Yes --> **App token** (client credentials OAuth flow, server-side only)
|
|
15
|
+
|
|
16
|
+
4. **Is the app connecting to ArcGIS Enterprise (on-premise)?**
|
|
17
|
+
- Yes --> **OAuth 2.0** with custom `portalUrl`
|
|
18
|
+
|
|
19
|
+
5. **Do you need to access multiple secured services and want automatic token management?**
|
|
20
|
+
- Yes --> **IdentityManager** handles this automatically with OAuth or registered tokens
|
|
21
|
+
|
|
22
|
+
| Approach | User Sign-In | Setup Complexity | Use Case |
|
|
23
|
+
|----------|-------------|-----------------|----------|
|
|
24
|
+
| API Key | No | Low | Public apps with basemaps/services |
|
|
25
|
+
| OAuth 2.0 (redirect) | Yes | Medium | Web apps needing user content |
|
|
26
|
+
| OAuth 2.0 (popup) | Yes | Medium | SPAs that stay on page |
|
|
27
|
+
| `<arcgis-oauth>` component | Yes | Low | Quick OAuth with Map Components |
|
|
28
|
+
| Registered token | No | Low | Testing / known tokens |
|
|
29
|
+
|
|
30
|
+
## OAuth Setup Checklist
|
|
31
|
+
|
|
32
|
+
### Register your app
|
|
33
|
+
- [ ] Go to [developers.arcgis.com](https://developers.arcgis.com) (or your Enterprise portal)
|
|
34
|
+
- [ ] Create a new OAuth application
|
|
35
|
+
- [ ] Note the **App ID** (client ID)
|
|
36
|
+
- [ ] Add **redirect URIs** for your app (e.g., `http://localhost:5173`, `https://yourapp.com`)
|
|
37
|
+
- [ ] For production: add **referrer restrictions** on the app registration
|
|
38
|
+
|
|
39
|
+
### Implement in code
|
|
40
|
+
- [ ] Import `OAuthInfo` and `esriId` (IdentityManager)
|
|
41
|
+
- [ ] Create `OAuthInfo`: `new OAuthInfo({ appId: "YOUR_APP_ID" })`
|
|
42
|
+
- [ ] Set `popup: false` for redirect flow, `popup: true` for popup window
|
|
43
|
+
- [ ] Register: `esriId.registerOAuthInfos([oauthInfo])`
|
|
44
|
+
- [ ] Check existing sign-in: `esriId.checkSignInStatus(portalUrl + "/sharing")`
|
|
45
|
+
- [ ] Trigger sign-in: `esriId.getCredential(portalUrl + "/sharing")`
|
|
46
|
+
- [ ] Load portal: `new Portal({ authMode: "immediate" })` then `await portal.load()`
|
|
47
|
+
|
|
48
|
+
### For Enterprise Portal
|
|
49
|
+
- [ ] Set `portalUrl` on OAuthInfo: `new OAuthInfo({ appId, portalUrl: "https://your-portal/portal" })`
|
|
50
|
+
- [ ] Or set globally: `esriConfig.portalUrl = "https://your-portal/portal"`
|
|
51
|
+
|
|
52
|
+
### Using the OAuth Component
|
|
53
|
+
- [ ] Add `<arcgis-oauth app-id="YOUR_APP_ID">` to HTML
|
|
54
|
+
- [ ] Listen for `arcgisSignIn` and `arcgisSignOut` events
|
|
55
|
+
- [ ] Access credential from `event.detail.credential`
|
|
56
|
+
|
|
57
|
+
## Security Checklist
|
|
58
|
+
|
|
59
|
+
### Token handling
|
|
60
|
+
- [ ] Never commit API keys or tokens to source control
|
|
61
|
+
- [ ] Use environment variables (e.g., `VITE_ARCGIS_API_KEY`) for API keys
|
|
62
|
+
- [ ] For OAuth: tokens are managed by IdentityManager (no manual storage needed)
|
|
63
|
+
- [ ] Do not store tokens in `localStorage` for production apps
|
|
64
|
+
|
|
65
|
+
### CORS and referrers
|
|
66
|
+
- [ ] Configure `esriConfig.request.trustedServers` for servers that should receive credentials
|
|
67
|
+
- [ ] Set up proxy (`esriConfig.request.proxyUrl`) for services that do not support CORS
|
|
68
|
+
- [ ] Configure **allowed referrers** on your API key at developers.arcgis.com
|
|
69
|
+
- [ ] For Enterprise: ensure CORS is enabled on the portal and services
|
|
70
|
+
|
|
71
|
+
### OAuth security
|
|
72
|
+
- [ ] Use HTTPS in production (OAuth redirects require it)
|
|
73
|
+
- [ ] Register only the redirect URIs you actually use
|
|
74
|
+
- [ ] Use `popup: false` (redirect flow) when popup blockers are a concern
|
|
75
|
+
- [ ] Handle token expiration: IdentityManager auto-refreshes, but test edge cases
|
|
76
|
+
|
|
77
|
+
### API Key scoping
|
|
78
|
+
- [ ] Generate API keys with minimum required privileges (only basemaps, only geocoding, etc.)
|
|
79
|
+
- [ ] Set referrer restrictions to limit where the key can be used
|
|
80
|
+
- [ ] Use separate keys for development and production
|
|
81
|
+
- [ ] Rotate keys periodically
|
|
82
|
+
|
|
83
|
+
## Sign-In / Sign-Out Flow Summary
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
App loads
|
|
87
|
+
--> checkSignInStatus()
|
|
88
|
+
--> Signed in? Load Portal, display user info
|
|
89
|
+
--> Not signed in? Show sign-in button
|
|
90
|
+
--> User clicks sign-in
|
|
91
|
+
--> getCredential() triggers OAuth redirect/popup
|
|
92
|
+
--> User authenticates at ArcGIS
|
|
93
|
+
--> Redirect back with token
|
|
94
|
+
--> Load Portal, display user info
|
|
95
|
+
|
|
96
|
+
Sign out:
|
|
97
|
+
--> esriId.destroyCredentials()
|
|
98
|
+
--> window.location.reload()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Common Token Errors
|
|
102
|
+
|
|
103
|
+
| Error | Likely Cause | Fix |
|
|
104
|
+
|-------|-------------|-----|
|
|
105
|
+
| `identity-manager:not-authorized` | Missing or expired token | Trigger re-authentication |
|
|
106
|
+
| `403 Forbidden` | API key lacks required privilege | Add privilege at developers.arcgis.com |
|
|
107
|
+
| `498 Invalid Token` | Token expired or revoked | Refresh or re-authenticate |
|
|
108
|
+
| `499 Token Required` | Secured service, no token provided | Register OAuthInfo or set API key |
|
|
109
|
+
| CORS error on token request | Portal not configured for CORS | Add to `trustedServers` or use proxy |
|
|
@@ -292,11 +292,48 @@ esriConfig.request.proxyRules.push({
|
|
|
292
292
|
|
|
293
293
|
1. **App ID registration**: App ID must be registered with correct redirect URIs
|
|
294
294
|
|
|
295
|
-
2. **Popup blockers**: OAuth
|
|
295
|
+
2. **Popup blockers**: OAuth popup-based sign-in fails on mobile browsers.
|
|
296
|
+
|
|
297
|
+
```javascript
|
|
298
|
+
// Anti-pattern: using popup flow on mobile
|
|
299
|
+
OAuthInfo.create({
|
|
300
|
+
appId: "YOUR_APP_ID",
|
|
301
|
+
popup: true // Blocked by most mobile browsers
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
```javascript
|
|
306
|
+
// Correct: use redirect flow for mobile, popup for desktop
|
|
307
|
+
const isMobile = /iPhone|iPad|Android/i.test(navigator.userAgent);
|
|
308
|
+
OAuthInfo.create({
|
|
309
|
+
appId: "YOUR_APP_ID",
|
|
310
|
+
popup: !isMobile // Redirect flow on mobile, popup on desktop
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Impact:** On mobile browsers (Safari, Chrome for iOS/Android), the OAuth popup is silently blocked. The user sees no sign-in dialog and cannot authenticate, with no error message explaining why.
|
|
296
315
|
|
|
297
316
|
3. **Token expiration**: Tokens expire - handle refresh or re-authentication
|
|
298
317
|
|
|
299
318
|
4. **CORS errors**: Configure trusted servers or use proxy for cross-origin
|
|
300
319
|
|
|
301
|
-
5. **Portal URL
|
|
320
|
+
5. **Portal URL trailing slash**: A trailing slash in the portal URL causes token validation to fail.
|
|
321
|
+
|
|
322
|
+
```javascript
|
|
323
|
+
// Anti-pattern: portal URL with trailing slash
|
|
324
|
+
const portal = new Portal({
|
|
325
|
+
url: "https://myorg.maps.arcgis.com/" // Trailing slash
|
|
326
|
+
});
|
|
327
|
+
await portal.load(); // Token validation may fail
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
```javascript
|
|
331
|
+
// Correct: portal URL without trailing slash
|
|
332
|
+
const portal = new Portal({
|
|
333
|
+
url: "https://myorg.maps.arcgis.com" // No trailing slash
|
|
334
|
+
});
|
|
335
|
+
await portal.load();
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Impact:** The portal URL with a trailing slash does not match the token's server URL during validation. Authentication silently fails, or the user is prompted to sign in repeatedly because the token is never accepted.
|
|
302
339
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Agent Guide: arcgis-core-maps
|
|
2
|
+
|
|
3
|
+
Quick-reference decisions, checklists, and tables for creating 2D/3D maps.
|
|
4
|
+
|
|
5
|
+
## Map Components vs Core API Decision Tree
|
|
6
|
+
|
|
7
|
+
1. **Are you starting a new project?**
|
|
8
|
+
- Yes --> Use Map Components (`<arcgis-map>` / `<arcgis-scene>`)
|
|
9
|
+
- No, maintaining existing Core API code --> Stay with Core API
|
|
10
|
+
|
|
11
|
+
2. **Do you need programmatic control over view construction timing?**
|
|
12
|
+
- Yes (e.g., dynamic container, conditional rendering) --> Core API (`new MapView()`)
|
|
13
|
+
- No --> Map Components
|
|
14
|
+
|
|
15
|
+
3. **Are you using a framework?**
|
|
16
|
+
- React 19 / Angular / Vue --> Map Components work as custom elements
|
|
17
|
+
- Older React (<19) --> Core API (custom element event handling is limited)
|
|
18
|
+
|
|
19
|
+
4. **Do you need to embed the map inside a Calcite shell layout?**
|
|
20
|
+
- Yes --> Map Components with `reference-element` attribute for external widgets
|
|
21
|
+
- No --> Either approach works
|
|
22
|
+
|
|
23
|
+
**Default:** Map Components unless you have a specific reason for Core API.
|
|
24
|
+
|
|
25
|
+
## Setup Checklist
|
|
26
|
+
|
|
27
|
+
### Map Components (npm)
|
|
28
|
+
- [ ] `npm install @arcgis/map-components @esri/calcite-components`
|
|
29
|
+
- [ ] Import components: `import "@arcgis/map-components/dist/components/arcgis-map"`
|
|
30
|
+
- [ ] Set Calcite asset path: `setAssetPath("https://js.arcgis.com/calcite-components/3.3.3/assets")`
|
|
31
|
+
- [ ] Set API key: `esriConfig.apiKey = "YOUR_KEY"`
|
|
32
|
+
- [ ] Ensure `html, body { height: 100%; margin: 0; }` in CSS
|
|
33
|
+
- [ ] Set `"moduleResolution": "bundler"` in tsconfig.json
|
|
34
|
+
- [ ] Use `arcgisViewReadyChange` event before accessing `view`
|
|
35
|
+
|
|
36
|
+
### Core API (npm)
|
|
37
|
+
- [ ] `npm install @arcgis/core`
|
|
38
|
+
- [ ] Import CSS: `@import "@arcgis/core/assets/esri/themes/light/main.css"`
|
|
39
|
+
- [ ] Create container div with full height: `#viewDiv { height: 100%; }`
|
|
40
|
+
- [ ] Import classes: `import Map from "@arcgis/core/Map.js"`
|
|
41
|
+
- [ ] Set API key: `esriConfig.apiKey = "YOUR_KEY"`
|
|
42
|
+
- [ ] Await `view.when()` before accessing view properties
|
|
43
|
+
|
|
44
|
+
### Map Components (CDN)
|
|
45
|
+
- [ ] Add Calcite script: `<script type="module" src="https://js.arcgis.com/calcite-components/3.3.3/calcite.esm.js"></script>`
|
|
46
|
+
- [ ] Add SDK script: `<script src="https://js.arcgis.com/4.34/"></script>`
|
|
47
|
+
- [ ] Add components script: `<script type="module" src="https://js.arcgis.com/4.34/map-components/"></script>`
|
|
48
|
+
- [ ] Use `<arcgis-map>` or `<arcgis-scene>` in HTML
|
|
49
|
+
- [ ] Use `$arcgis.import()` for dynamic imports inside `<script type="module">`
|
|
50
|
+
|
|
51
|
+
### Core API (CDN)
|
|
52
|
+
- [ ] Add CSS: `<link rel="stylesheet" href="https://js.arcgis.com/4.34/esri/themes/light/main.css" />`
|
|
53
|
+
- [ ] Add SDK script: `<script src="https://js.arcgis.com/4.34/"></script>`
|
|
54
|
+
- [ ] Create `#viewDiv` with full height
|
|
55
|
+
- [ ] Use `<script type="module">` for async/await support
|
|
56
|
+
|
|
57
|
+
## CDN vs npm Comparison
|
|
58
|
+
|
|
59
|
+
| Aspect | CDN | npm + Build Tool |
|
|
60
|
+
|--------|-----|-----------------|
|
|
61
|
+
| Setup speed | Fastest (3 script tags) | Requires project scaffolding |
|
|
62
|
+
| Tree-shaking | No (loads full SDK) | Yes (smaller bundles) |
|
|
63
|
+
| TypeScript | No type checking | Full type support |
|
|
64
|
+
| Offline dev | No | Yes |
|
|
65
|
+
| Production use | Prototypes and demos | Recommended |
|
|
66
|
+
| Import style | `$arcgis.import()` | `import X from "@arcgis/core/..."` |
|
|
67
|
+
| CSS handling | Manual link tag (Core API) | `@import` in CSS (Core API), automatic (Components) |
|
|
68
|
+
| Versioning | URL-pinned (`/4.34/`) | `package.json` pinned |
|
|
69
|
+
|
|
70
|
+
## Quick Reference: Coordinate Order
|
|
71
|
+
|
|
72
|
+
ArcGIS uses **[longitude, latitude]**, not [latitude, longitude].
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
center: [-118.24, 34.05] // Los Angeles: longitude first, latitude second
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Quick Reference: View Containers
|
|
79
|
+
|
|
80
|
+
| Container | Dimension | Class |
|
|
81
|
+
|-----------|-----------|-------|
|
|
82
|
+
| `<arcgis-map>` | 2D | Web component (MapView internally) |
|
|
83
|
+
| `<arcgis-scene>` | 3D | Web component (SceneView internally) |
|
|
84
|
+
| `<arcgis-video>` | Video | Web component (VideoView internally) |
|
|
85
|
+
| `new MapView()` | 2D | Core API |
|
|
86
|
+
| `new SceneView()` | 3D | Core API |
|
|
@@ -733,18 +733,67 @@ reactiveUtils.when(
|
|
|
733
733
|
|
|
734
734
|
## Common Pitfalls
|
|
735
735
|
|
|
736
|
-
1. **Missing CSS for Core API**: The Core API requires `main.css
|
|
736
|
+
1. **Missing CSS for Core API**: The Core API requires `main.css` for widgets and popups to render correctly.
|
|
737
|
+
|
|
738
|
+
```html
|
|
739
|
+
<!-- Anti-pattern: no CSS import -->
|
|
740
|
+
<script type="module">
|
|
741
|
+
import MapView from "@arcgis/core/views/MapView";
|
|
742
|
+
import Map from "@arcgis/core/Map";
|
|
743
|
+
const view = new MapView({ container: "viewDiv", map: new Map({ basemap: "topo-vector" }) });
|
|
744
|
+
</script>
|
|
745
|
+
```
|
|
746
|
+
|
|
737
747
|
```html
|
|
748
|
+
<!-- Correct: include the CSS -->
|
|
738
749
|
<link rel="stylesheet" href="https://js.arcgis.com/4.34/esri/themes/light/main.css" />
|
|
750
|
+
<script type="module">
|
|
751
|
+
import MapView from "@arcgis/core/views/MapView";
|
|
752
|
+
import Map from "@arcgis/core/Map";
|
|
753
|
+
const view = new MapView({ container: "viewDiv", map: new Map({ basemap: "topo-vector" }) });
|
|
754
|
+
</script>
|
|
739
755
|
```
|
|
740
756
|
|
|
741
|
-
|
|
757
|
+
**Impact:** The map itself renders, but widgets (Zoom, Legend, Search) and popups appear unstyled or completely broken. Layouts collapse and controls become unusable.
|
|
758
|
+
|
|
759
|
+
2. **Not awaiting viewOnReady()**: View properties are not available until the view is ready.
|
|
760
|
+
|
|
742
761
|
```javascript
|
|
762
|
+
// Anti-pattern: accessing view before it is ready
|
|
763
|
+
const mapElement = document.querySelector("arcgis-map");
|
|
764
|
+
const view = mapElement.view; // undefined - view is not ready yet
|
|
765
|
+
view.goTo({ center: [-118, 34] }); // TypeError: Cannot read properties of undefined
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
```javascript
|
|
769
|
+
// Correct: wait for the view to be ready
|
|
770
|
+
const mapElement = document.querySelector("arcgis-map");
|
|
743
771
|
await mapElement.viewOnReady();
|
|
744
|
-
const view = mapElement.view; //
|
|
772
|
+
const view = mapElement.view; // MapView instance, safe to use
|
|
773
|
+
view.goTo({ center: [-118, 34] });
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
**Impact:** `view` is `null` or `undefined` before the component initializes, causing runtime errors on any property access or method call.
|
|
777
|
+
|
|
778
|
+
3. **Coordinate order**: ArcGIS uses `[longitude, latitude]`, not `[latitude, longitude]`.
|
|
779
|
+
|
|
780
|
+
```javascript
|
|
781
|
+
// Anti-pattern: lat/lng order (Google Maps convention)
|
|
782
|
+
const view = new MapView({
|
|
783
|
+
center: [34.05, -118.24], // lat first, lng second - WRONG
|
|
784
|
+
zoom: 12
|
|
785
|
+
});
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
```javascript
|
|
789
|
+
// Correct: lng/lat order (ArcGIS convention)
|
|
790
|
+
const view = new MapView({
|
|
791
|
+
center: [-118.24, 34.05], // lng first, lat second
|
|
792
|
+
zoom: 12
|
|
793
|
+
});
|
|
745
794
|
```
|
|
746
795
|
|
|
747
|
-
|
|
796
|
+
**Impact:** The map centers on the wrong location, often in the middle of the ocean or on a different continent, with no error message to indicate the mistake.
|
|
748
797
|
|
|
749
798
|
4. **Missing viewDiv height**: Ensure the container has height:
|
|
750
799
|
```css
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Agent Guide: arcgis-editing
|
|
2
|
+
|
|
3
|
+
Quick-reference decisions, checklists, and tables for feature editing and versioning.
|
|
4
|
+
|
|
5
|
+
## Editor Component vs Core API Decision
|
|
6
|
+
|
|
7
|
+
1. **Do you need a basic editing UI with default behavior?**
|
|
8
|
+
- Yes --> `<arcgis-editor slot="top-right">` (Map Component)
|
|
9
|
+
- No, need custom form layout or logic --> Core API `Editor` widget or `FeatureForm`
|
|
10
|
+
|
|
11
|
+
2. **Do you need a standalone form (not inside the Editor panel)?**
|
|
12
|
+
- Yes --> `FeatureForm` widget or `<arcgis-feature-form>` component
|
|
13
|
+
- No --> Editor handles forms internally
|
|
14
|
+
|
|
15
|
+
3. **Do you need programmatic edits without any UI?**
|
|
16
|
+
- Yes --> `featureLayer.applyEdits()` directly
|
|
17
|
+
|
|
18
|
+
4. **Do you need version management?**
|
|
19
|
+
- Yes --> `VersionManagementService` + `<arcgis-version-management>` component
|
|
20
|
+
|
|
21
|
+
| Approach | UI Provided | Geometry Editing | Form Editing | Flexibility |
|
|
22
|
+
|----------|------------|-----------------|--------------|-------------|
|
|
23
|
+
| `<arcgis-editor>` component | Full | Yes | Yes | Low (defaults) |
|
|
24
|
+
| `Editor` widget (Core API) | Full | Yes | Yes | High (layerInfos config) |
|
|
25
|
+
| `FeatureForm` widget | Form only | No | Yes | High (custom form layout) |
|
|
26
|
+
| `applyEdits()` | None | Via code | Via code | Maximum |
|
|
27
|
+
|
|
28
|
+
## Edit Workflow Steps
|
|
29
|
+
|
|
30
|
+
### Interactive editing (Editor widget)
|
|
31
|
+
1. **Setup** -- Add Editor to view with `layerInfos` config
|
|
32
|
+
2. **Create** -- User selects template, draws geometry, fills form
|
|
33
|
+
3. **Update** -- User clicks feature, modifies geometry or attributes
|
|
34
|
+
4. **Delete** -- User selects feature and deletes
|
|
35
|
+
|
|
36
|
+
### Programmatic editing (applyEdits)
|
|
37
|
+
1. **Create Graphic** -- Build `Graphic` with geometry and attributes
|
|
38
|
+
2. **Call applyEdits** -- `layer.applyEdits({ addFeatures: [graphic] })`
|
|
39
|
+
3. **Handle result** -- Check `result.addFeatureResults` for success/errors
|
|
40
|
+
4. **Refresh** -- Layer auto-refreshes; call `layer.refresh()` if needed
|
|
41
|
+
|
|
42
|
+
### Form-based editing (FeatureForm)
|
|
43
|
+
1. **Create form** -- `new FeatureForm({ layer, formTemplate })`
|
|
44
|
+
2. **Set feature** -- `form.feature = graphic`
|
|
45
|
+
3. **User edits** -- Form fields are populated and editable
|
|
46
|
+
4. **Validate** -- Check `form.valid` before proceeding
|
|
47
|
+
5. **Get values** -- `form.getValues()` returns updated attributes
|
|
48
|
+
6. **Apply** -- `layer.applyEdits({ updateFeatures: [graphic] })`
|
|
49
|
+
|
|
50
|
+
## Version Management Checklist
|
|
51
|
+
|
|
52
|
+
### Setup
|
|
53
|
+
- [ ] Have a branch-versioned feature service
|
|
54
|
+
- [ ] Create `VersionManagementService` with the service URL
|
|
55
|
+
- [ ] Await `vms.load()`
|
|
56
|
+
|
|
57
|
+
### Create and edit in a version
|
|
58
|
+
- [ ] Create version: `vms.createVersion({ versionName, access: "private" })`
|
|
59
|
+
- [ ] Switch layer: `featureLayer.gdbVersion = version.versionName`
|
|
60
|
+
- [ ] Refresh layer: `await featureLayer.refresh()`
|
|
61
|
+
- [ ] Start edit session: `await vms.startEditing({ versionIdentifier })`
|
|
62
|
+
- [ ] Apply edits: `await featureLayer.applyEdits(edits)`
|
|
63
|
+
- [ ] Reconcile: `await vms.reconcile({ versionIdentifier, conflictResolution: "favorEditVersion" })`
|
|
64
|
+
- [ ] Check for conflicts: `reconcileResult.hasConflicts`
|
|
65
|
+
- [ ] Post to parent: `await vms.post({ versionIdentifier })`
|
|
66
|
+
- [ ] Stop editing: `await vms.stopEditing({ versionIdentifier, saveEdits: true })`
|
|
67
|
+
|
|
68
|
+
### Cleanup
|
|
69
|
+
- [ ] Switch layer back: `featureLayer.gdbVersion = vms.defaultVersionIdentifier`
|
|
70
|
+
- [ ] Delete temp version: `await vms.deleteVersion({ versionName })`
|
|
71
|
+
|
|
72
|
+
## Form Element Types
|
|
73
|
+
|
|
74
|
+
| Type | Purpose |
|
|
75
|
+
|------|---------|
|
|
76
|
+
| `field` | Single editable field |
|
|
77
|
+
| `group` | Group of fields under a heading |
|
|
78
|
+
|
|
79
|
+
### Field Element Expression Types
|
|
80
|
+
|
|
81
|
+
| Expression Property | Controls |
|
|
82
|
+
|-------------------|----------|
|
|
83
|
+
| `visibilityExpression` | Whether the field is visible |
|
|
84
|
+
| `editableExpression` | Whether the field is editable |
|
|
85
|
+
| `requiredExpression` | Whether the field is required |
|
|
86
|
+
| `valueExpression` | Computed default value |
|
|
87
|
+
|
|
88
|
+
All expressions use Arcade syntax and must return `true`/`false`.
|
|
89
|
+
|
|
90
|
+
## applyEdits Operations
|
|
91
|
+
|
|
92
|
+
| Operation | Property | Value |
|
|
93
|
+
|-----------|----------|-------|
|
|
94
|
+
| Add features | `addFeatures` | Array of Graphics |
|
|
95
|
+
| Update features | `updateFeatures` | Array of Graphics (with existing OID) |
|
|
96
|
+
| Delete features | `deleteFeatures` | Array of Graphics or `[{ objectId: N }]` |
|
|
97
|
+
|
|
98
|
+
All three can be combined in a single `applyEdits()` call.
|
|
@@ -517,6 +517,42 @@ if (form.valid) {
|
|
|
517
517
|
|
|
518
518
|
## Complete Example
|
|
519
519
|
|
|
520
|
+
### Map Components (Recommended)
|
|
521
|
+
|
|
522
|
+
```html
|
|
523
|
+
<!DOCTYPE html>
|
|
524
|
+
<html>
|
|
525
|
+
<head>
|
|
526
|
+
<script type="module" src="https://js.arcgis.com/calcite-components/3.3.3/calcite.esm.js"></script>
|
|
527
|
+
<script src="https://js.arcgis.com/4.34/"></script>
|
|
528
|
+
<script type="module" src="https://js.arcgis.com/4.34/map-components/"></script>
|
|
529
|
+
<style>
|
|
530
|
+
html, body { height: 100%; margin: 0; }
|
|
531
|
+
</style>
|
|
532
|
+
</head>
|
|
533
|
+
<body>
|
|
534
|
+
<arcgis-map basemap="streets-navigation-vector">
|
|
535
|
+
<arcgis-zoom slot="top-left"></arcgis-zoom>
|
|
536
|
+
<arcgis-editor slot="top-right"></arcgis-editor>
|
|
537
|
+
</arcgis-map>
|
|
538
|
+
|
|
539
|
+
<script type="module">
|
|
540
|
+
import FeatureLayer from "@arcgis/core/layers/FeatureLayer.js";
|
|
541
|
+
|
|
542
|
+
const mapElement = document.querySelector("arcgis-map");
|
|
543
|
+
await mapElement.viewOnReady();
|
|
544
|
+
|
|
545
|
+
const layer = new FeatureLayer({
|
|
546
|
+
url: "https://services.arcgis.com/.../FeatureServer/0"
|
|
547
|
+
});
|
|
548
|
+
mapElement.map.add(layer);
|
|
549
|
+
</script>
|
|
550
|
+
</body>
|
|
551
|
+
</html>
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### Core API
|
|
555
|
+
|
|
520
556
|
```html
|
|
521
557
|
<!DOCTYPE html>
|
|
522
558
|
<html>
|
|
@@ -525,7 +561,6 @@ if (form.valid) {
|
|
|
525
561
|
<script src="https://js.arcgis.com/4.34/"></script>
|
|
526
562
|
<style>
|
|
527
563
|
html, body, #viewDiv { height: 100%; margin: 0; }
|
|
528
|
-
#formPanel { position: absolute; top: 10px; right: 10px; width: 300px; }
|
|
529
564
|
</style>
|
|
530
565
|
<script type="module">
|
|
531
566
|
import Map from "@arcgis/core/Map.js";
|
|
@@ -617,11 +652,63 @@ const editor = new Editor({
|
|
|
617
652
|
|
|
618
653
|
## Common Pitfalls
|
|
619
654
|
|
|
620
|
-
1. **Editing permissions**:
|
|
655
|
+
1. **Editing permissions**: Calling `applyEdits` on a layer without checking edit capabilities causes silent failures.
|
|
656
|
+
|
|
657
|
+
```javascript
|
|
658
|
+
// Anti-pattern: calling applyEdits without checking capabilities
|
|
659
|
+
const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" });
|
|
660
|
+
await layer.applyEdits({
|
|
661
|
+
addFeatures: [newFeature]
|
|
662
|
+
});
|
|
663
|
+
// May fail silently or throw a cryptic server error
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
```javascript
|
|
667
|
+
// Correct: check capabilities before editing
|
|
668
|
+
const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" });
|
|
669
|
+
await layer.load();
|
|
670
|
+
if (layer.editingEnabled && layer.capabilities?.editing?.supportsAddingFeatures) {
|
|
671
|
+
await layer.applyEdits({
|
|
672
|
+
addFeatures: [newFeature]
|
|
673
|
+
});
|
|
674
|
+
} else {
|
|
675
|
+
console.error("Layer does not support adding features");
|
|
676
|
+
}
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
**Impact:** The `applyEdits` call fails with a cryptic server error or silently returns an error result that is easy to miss, leaving the user believing the edit was saved when it was not.
|
|
621
680
|
|
|
622
681
|
2. **Subtype field**: Must match the subtype configuration in the service
|
|
623
682
|
|
|
624
|
-
3. **Version locking**:
|
|
683
|
+
3. **Version locking**: Editing branch-versioned data without proper session management causes version locks.
|
|
684
|
+
|
|
685
|
+
```javascript
|
|
686
|
+
// Anti-pattern: editing without version management session
|
|
687
|
+
const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" });
|
|
688
|
+
layer.gdbVersion = "editor1.design_v1";
|
|
689
|
+
await layer.applyEdits({ updateFeatures: [updatedFeature] });
|
|
690
|
+
// Version remains locked, blocking other users
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
```javascript
|
|
694
|
+
// Correct: start and stop version management session
|
|
695
|
+
const versionManagement = new VersionManagementService({
|
|
696
|
+
url: "https://services.arcgis.com/.../VersionManagementServer"
|
|
697
|
+
});
|
|
698
|
+
const versionName = "editor1.design_v1";
|
|
699
|
+
|
|
700
|
+
// Start editing session
|
|
701
|
+
await versionManagement.startEditing(versionName);
|
|
702
|
+
|
|
703
|
+
const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" });
|
|
704
|
+
layer.gdbVersion = versionName;
|
|
705
|
+
await layer.applyEdits({ updateFeatures: [updatedFeature] });
|
|
706
|
+
|
|
707
|
+
// Stop editing session to release the lock
|
|
708
|
+
await versionManagement.stopEditing(versionName, true); // true = save edits
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
**Impact:** The version stays locked after editing, preventing other users from accessing or editing it. Accumulated locks require administrator intervention to release.
|
|
625
712
|
|
|
626
713
|
4. **Validation expressions**: Must return true/false or error object
|
|
627
714
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Agent Guide: arcgis-interaction
|
|
2
|
+
|
|
3
|
+
Quick-reference decisions, checklists, and tables for user interaction patterns.
|
|
4
|
+
|
|
5
|
+
## Hit Test vs Query vs Popup Decision Matrix
|
|
6
|
+
|
|
7
|
+
| Need | Approach | Method |
|
|
8
|
+
|------|----------|--------|
|
|
9
|
+
| User clicks a feature and sees info | Popup (built-in) | Set `popupTemplate` on layer, it works automatically |
|
|
10
|
+
| User clicks and you run custom logic | Hit test | `view.hitTest(event)` in a click handler |
|
|
11
|
+
| Find all features in an area | Spatial query | `featureLayer.queryFeatures(query)` |
|
|
12
|
+
| Highlight features by attribute | Attribute query + highlight | `queryObjectIds()` then `layerView.highlight(oids)` |
|
|
13
|
+
| Show a cursor change on hover | Hit test on pointer-move | `view.hitTest(event)` in a pointer-move handler |
|
|
14
|
+
| Draw a shape and select features in it | Sketch + spatial query | Sketch widget for drawing, then query with the geometry |
|
|
15
|
+
|
|
16
|
+
### When to use each:
|
|
17
|
+
|
|
18
|
+
- **Popup** -- Simplest. Just configure `popupTemplate` on the layer. No code for the interaction itself.
|
|
19
|
+
- **Hit test** -- Returns the graphic at a screen point. Use when you need the actual feature object for custom behavior (not just a popup).
|
|
20
|
+
- **Server query** -- Returns features matching SQL/spatial criteria from the service. Use for bulk operations or features not visible on screen.
|
|
21
|
+
- **Client query** -- `layerView.queryFeatures()` queries only loaded features. Faster but limited to what is in the view.
|
|
22
|
+
|
|
23
|
+
## Click-to-Select Implementation Checklist
|
|
24
|
+
|
|
25
|
+
- [ ] Get a reference to the target `FeatureLayer`
|
|
26
|
+
- [ ] Get the LayerView: `const layerView = await view.whenLayerView(featureLayer)`
|
|
27
|
+
- [ ] Store a highlight handle variable: `let highlightHandle = null`
|
|
28
|
+
- [ ] Add click handler: `view.on("click", async (event) => { ... })`
|
|
29
|
+
- [ ] Inside handler, run hit test: `const response = await view.hitTest(event, { include: [featureLayer] })`
|
|
30
|
+
- [ ] Remove previous highlight: `highlightHandle?.remove()`
|
|
31
|
+
- [ ] If results found, highlight: `highlightHandle = layerView.highlight(graphic)`
|
|
32
|
+
- [ ] Optionally open popup or update side panel with feature attributes
|
|
33
|
+
|
|
34
|
+
## Cleanup Pattern
|
|
35
|
+
|
|
36
|
+
Always clean up handles and event listeners to prevent memory leaks.
|
|
37
|
+
|
|
38
|
+
### Handle-based cleanup (highlight, watch)
|
|
39
|
+
```
|
|
40
|
+
const handle = layerView.highlight(graphic);
|
|
41
|
+
// Later:
|
|
42
|
+
handle.remove();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Event listener cleanup
|
|
46
|
+
```
|
|
47
|
+
const handle = view.on("click", handler);
|
|
48
|
+
// Later:
|
|
49
|
+
handle.remove();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### reactiveUtils cleanup
|
|
53
|
+
```
|
|
54
|
+
const handle = reactiveUtils.watch(() => view.zoom, callback);
|
|
55
|
+
// Later:
|
|
56
|
+
handle.remove();
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Destroy pattern (widgets)
|
|
60
|
+
```
|
|
61
|
+
widget.destroy(); // Removes from DOM and cleans up
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Checklist for cleanup
|
|
65
|
+
- [ ] Store all `view.on()` return handles
|
|
66
|
+
- [ ] Store all `reactiveUtils.watch()` / `when()` / `once()` return handles
|
|
67
|
+
- [ ] Store all `layerView.highlight()` return handles
|
|
68
|
+
- [ ] Call `.remove()` on handles when the interaction is no longer needed
|
|
69
|
+
- [ ] Call `.destroy()` on widgets when removing them
|
|
70
|
+
- [ ] In frameworks (React/Angular): clean up in unmount/destroy lifecycle hooks
|
|
71
|
+
|
|
72
|
+
## Event Types Quick Reference
|
|
73
|
+
|
|
74
|
+
| Event | Fires when | Common use |
|
|
75
|
+
|-------|-----------|------------|
|
|
76
|
+
| `click` | User clicks the map | Feature selection, custom popups |
|
|
77
|
+
| `double-click` | User double-clicks | Override default zoom behavior |
|
|
78
|
+
| `pointer-move` | Mouse moves over map | Hover highlighting, coordinate display |
|
|
79
|
+
| `pointer-down` / `pointer-up` | Mouse button pressed/released | Custom drag interactions |
|
|
80
|
+
| `drag` | User drags the map | Custom pan behavior |
|
|
81
|
+
| `key-down` / `key-up` | Keyboard input while map focused | Keyboard shortcuts |
|
|
82
|
+
| `hold` | Long press | Mobile context menu |
|
|
83
|
+
|
|
84
|
+
### Preventing Default Behavior
|
|
85
|
+
```
|
|
86
|
+
view.on("double-click", (event) => {
|
|
87
|
+
event.stopPropagation(); // Prevents default zoom-in
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Coordinate Conversion Quick Reference
|
|
92
|
+
|
|
93
|
+
| Direction | Method |
|
|
94
|
+
|-----------|--------|
|
|
95
|
+
| Screen (pixels) to Map (geographic) | `view.toMap({ x, y })` |
|
|
96
|
+
| Map (geographic) to Screen (pixels) | `view.toScreen(mapPoint)` |
|
|
97
|
+
| Click event to Map point | `event.mapPoint` (already available) |
|
|
98
|
+
| Pointer event to Map point | `view.toMap(event)` (must convert) |
|
|
99
|
+
|
|
100
|
+
## Highlight Options
|
|
101
|
+
|
|
102
|
+
| Property | Default | Purpose |
|
|
103
|
+
|----------|---------|---------|
|
|
104
|
+
| `color` | `[0, 255, 255, 1]` (cyan) | Highlight outline/halo color |
|
|
105
|
+
| `haloOpacity` | `1` | Opacity of the halo around features |
|
|
106
|
+
| `fillOpacity` | `0.25` | Opacity of the fill inside features |
|
|
107
|
+
|
|
108
|
+
Set on `layerView.highlightOptions` or at the `MapView` / `SceneView` level.
|