@saschabrunnerch/arcgis-maps-sdk-js-ai-context 0.0.2 → 0.1.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.
Files changed (52) hide show
  1. package/README.md +163 -203
  2. package/bin/cli.js +157 -173
  3. package/contexts/4.34/{claude → skills}/arcgis-3d-advanced/SKILL.md +586 -586
  4. package/contexts/4.34/{claude → skills}/arcgis-advanced-layers/SKILL.md +431 -431
  5. package/contexts/4.34/{claude → skills}/arcgis-analysis-services/SKILL.md +607 -607
  6. package/contexts/4.34/{claude → skills}/arcgis-authentication/SKILL.md +301 -301
  7. package/contexts/4.34/{claude → skills}/arcgis-cim-symbols/SKILL.md +486 -486
  8. package/contexts/4.34/{claude → skills}/arcgis-coordinates-projection/SKILL.md +406 -406
  9. package/contexts/4.34/{claude → skills}/arcgis-core-maps/SKILL.md +739 -739
  10. package/contexts/4.34/{claude → skills}/arcgis-core-utilities/SKILL.md +732 -732
  11. package/contexts/4.34/{claude → skills}/arcgis-custom-rendering/SKILL.md +445 -445
  12. package/contexts/4.34/{claude → skills}/arcgis-editing-advanced/SKILL.md +702 -702
  13. package/contexts/4.34/{claude → skills}/arcgis-feature-effects/SKILL.md +393 -393
  14. package/contexts/4.34/{claude → skills}/arcgis-geometry-operations/SKILL.md +489 -489
  15. package/contexts/4.34/{claude → skills}/arcgis-imagery/SKILL.md +307 -307
  16. package/contexts/4.34/{claude → skills}/arcgis-interaction/SKILL.md +572 -572
  17. package/contexts/4.34/{claude → skills}/arcgis-knowledge-graphs/SKILL.md +582 -582
  18. package/contexts/4.34/{claude → skills}/arcgis-layers/SKILL.md +601 -601
  19. package/contexts/4.34/{claude → skills}/arcgis-map-tools/SKILL.md +668 -668
  20. package/contexts/4.34/{claude → skills}/arcgis-media-layers/SKILL.md +290 -290
  21. package/contexts/4.34/{claude → skills}/arcgis-portal-content/SKILL.md +679 -679
  22. package/contexts/4.34/{claude → skills}/arcgis-scene-effects/SKILL.md +512 -512
  23. package/contexts/4.34/{claude → skills}/arcgis-smart-mapping/SKILL.md +686 -686
  24. package/contexts/4.34/{claude → skills}/arcgis-starter-app-extended/SKILL.md +649 -649
  25. package/contexts/4.34/{claude → skills}/arcgis-tables-forms/SKILL.md +877 -877
  26. package/contexts/4.34/{claude → skills}/arcgis-time-animation/SKILL.md +722 -722
  27. package/contexts/4.34/{claude → skills}/arcgis-utility-networks/SKILL.md +301 -301
  28. package/contexts/4.34/{claude → skills}/arcgis-visualization/SKILL.md +580 -580
  29. package/contexts/4.34/{claude → skills}/arcgis-widgets-ui/SKILL.md +574 -574
  30. package/lib/installer.js +19 -104
  31. package/package.json +45 -45
  32. package/contexts/4.34/copilot/arcgis-3d.instructions.md +0 -267
  33. package/contexts/4.34/copilot/arcgis-analysis.instructions.md +0 -294
  34. package/contexts/4.34/copilot/arcgis-arcade.instructions.md +0 -234
  35. package/contexts/4.34/copilot/arcgis-authentication.instructions.md +0 -187
  36. package/contexts/4.34/copilot/arcgis-cim-symbols.instructions.md +0 -177
  37. package/contexts/4.34/copilot/arcgis-core-maps.instructions.md +0 -246
  38. package/contexts/4.34/copilot/arcgis-core-utilities.instructions.md +0 -247
  39. package/contexts/4.34/copilot/arcgis-editing.instructions.md +0 -262
  40. package/contexts/4.34/copilot/arcgis-geometry.instructions.md +0 -225
  41. package/contexts/4.34/copilot/arcgis-layers.instructions.md +0 -278
  42. package/contexts/4.34/copilot/arcgis-popup-templates.instructions.md +0 -266
  43. package/contexts/4.34/copilot/arcgis-portal-advanced.instructions.md +0 -275
  44. package/contexts/4.34/copilot/arcgis-smart-mapping.instructions.md +0 -184
  45. package/contexts/4.34/copilot/arcgis-starter-app-extended.instructions.md +0 -643
  46. package/contexts/4.34/copilot/arcgis-starter-app.instructions.md +0 -268
  47. package/contexts/4.34/copilot/arcgis-time-animation.instructions.md +0 -112
  48. package/contexts/4.34/copilot/arcgis-visualization.instructions.md +0 -321
  49. package/contexts/4.34/copilot/arcgis-widgets-ui.instructions.md +0 -277
  50. /package/contexts/4.34/{claude → skills}/arcgis-arcade/SKILL.md +0 -0
  51. /package/contexts/4.34/{claude → skills}/arcgis-popup-templates/SKILL.md +0 -0
  52. /package/contexts/4.34/{claude → skills}/arcgis-starter-app/SKILL.md +0 -0
@@ -1,722 +1,722 @@
1
- ---
2
- name: arcgis-time-animation
3
- description: Work with temporal data using TimeSlider, TimeExtent, and time-aware layers. Use for animating data over time, filtering by date ranges, and visualizing temporal patterns.
4
- ---
5
-
6
- # ArcGIS Time Animation
7
-
8
- Use this skill for working with temporal data, time-aware layers, and time animation controls.
9
-
10
- ## TimeExtent
11
-
12
- Represents a time range with start and end dates.
13
-
14
- ```javascript
15
- import TimeExtent from "@arcgis/core/TimeExtent.js";
16
-
17
- // Create time extent
18
- const timeExtent = new TimeExtent({
19
- start: new Date("2024-01-01"),
20
- end: new Date("2024-12-31")
21
- });
22
-
23
- // Apply to view
24
- view.timeExtent = timeExtent;
25
- ```
26
-
27
- ### TimeExtent Properties
28
-
29
- ```javascript
30
- const timeExtent = new TimeExtent({
31
- start: new Date("2024-01-01T00:00:00Z"),
32
- end: new Date("2024-06-30T23:59:59Z")
33
- });
34
-
35
- console.log(timeExtent.start); // Date object
36
- console.log(timeExtent.end); // Date object
37
-
38
- // Check if instant (start === end)
39
- if (timeExtent.start.getTime() === timeExtent.end.getTime()) {
40
- console.log("This is a time instant");
41
- }
42
- ```
43
-
44
- ### Null TimeExtent
45
-
46
- ```javascript
47
- // No time filter (show all data)
48
- view.timeExtent = null;
49
-
50
- // Instant in time
51
- const instant = new TimeExtent({
52
- start: new Date("2024-06-15"),
53
- end: new Date("2024-06-15")
54
- });
55
- ```
56
-
57
- ## TimeInterval
58
-
59
- Represents a duration of time.
60
-
61
- ```javascript
62
- import TimeInterval from "@arcgis/core/TimeInterval.js";
63
-
64
- const interval = new TimeInterval({
65
- value: 1,
66
- unit: "months" // milliseconds, seconds, minutes, hours, days, weeks, months, years
67
- });
68
- ```
69
-
70
- ### TimeInterval Units
71
-
72
- - `milliseconds`
73
- - `seconds`
74
- - `minutes`
75
- - `hours`
76
- - `days`
77
- - `weeks`
78
- - `months`
79
- - `years`
80
- - `decades`
81
- - `centuries`
82
-
83
- ## TimeSlider
84
-
85
- Interactive widget for controlling temporal display.
86
-
87
- ### TimeSlider Component
88
-
89
- ```html
90
- <arcgis-map item-id="YOUR_WEBMAP_ID">
91
- <arcgis-time-slider
92
- slot="bottom-left"
93
- mode="time-window"
94
- loop
95
- time-visible>
96
- </arcgis-time-slider>
97
- </arcgis-map>
98
-
99
- <script type="module">
100
- const map = document.querySelector("arcgis-map");
101
- const timeSlider = document.querySelector("arcgis-time-slider");
102
-
103
- await map.viewOnReady();
104
-
105
- // Configure from layer
106
- const layer = map.view.map.layers.find(l => l.timeInfo);
107
- if (layer) {
108
- await layer.load();
109
- timeSlider.fullTimeExtent = layer.timeInfo.fullTimeExtent;
110
- timeSlider.stops = { interval: layer.timeInfo.interval };
111
- }
112
- </script>
113
- ```
114
-
115
- ### TimeSlider Widget (Core API)
116
-
117
- ```javascript
118
- import TimeSlider from "@arcgis/core/widgets/TimeSlider.js";
119
-
120
- const timeSlider = new TimeSlider({
121
- container: "timeSliderDiv",
122
- view: view,
123
- fullTimeExtent: {
124
- start: new Date("2020-01-01"),
125
- end: new Date("2024-12-31")
126
- },
127
- timeExtent: {
128
- start: new Date("2024-01-01"),
129
- end: new Date("2024-03-31")
130
- }
131
- });
132
- ```
133
-
134
- ### TimeSlider with Playback
135
-
136
- ```javascript
137
- const timeSlider = new TimeSlider({
138
- container: "timeSliderDiv",
139
- view: view,
140
- mode: "time-window", // instant, time-window, cumulative-from-start, cumulative-from-end
141
- fullTimeExtent: {
142
- start: new Date("2020-01-01"),
143
- end: new Date("2024-12-31")
144
- },
145
- playRate: 1000, // Milliseconds between steps
146
- loop: true,
147
- stops: {
148
- interval: {
149
- value: 1,
150
- unit: "months"
151
- }
152
- }
153
- });
154
-
155
- // Start/stop playback
156
- timeSlider.play();
157
- timeSlider.stop();
158
- ```
159
-
160
- ### TimeSlider Modes
161
-
162
- ```javascript
163
- // Instant - single point in time
164
- const instantSlider = new TimeSlider({
165
- mode: "instant",
166
- fullTimeExtent: { start, end }
167
- });
168
-
169
- // Time Window - range with start and end
170
- const windowSlider = new TimeSlider({
171
- mode: "time-window",
172
- fullTimeExtent: { start, end }
173
- });
174
-
175
- // Cumulative from Start - everything from start to current
176
- const cumulativeStart = new TimeSlider({
177
- mode: "cumulative-from-start",
178
- fullTimeExtent: { start, end }
179
- });
180
-
181
- // Cumulative from End - everything from current to end
182
- const cumulativeEnd = new TimeSlider({
183
- mode: "cumulative-from-end",
184
- fullTimeExtent: { start, end }
185
- });
186
- ```
187
-
188
- ### Custom Stops
189
-
190
- ```javascript
191
- // Interval-based stops
192
- const timeSlider = new TimeSlider({
193
- stops: {
194
- interval: {
195
- value: 1,
196
- unit: "weeks"
197
- }
198
- }
199
- });
200
-
201
- // Specific dates
202
- const timeSlider = new TimeSlider({
203
- stops: {
204
- dates: [
205
- new Date("2024-01-01"),
206
- new Date("2024-04-01"),
207
- new Date("2024-07-01"),
208
- new Date("2024-10-01")
209
- ]
210
- }
211
- });
212
-
213
- // Number of stops (evenly distributed)
214
- const timeSlider = new TimeSlider({
215
- stops: {
216
- count: 12 // 12 evenly spaced stops
217
- }
218
- });
219
- ```
220
-
221
- ### TimeSlider Events
222
-
223
- ```javascript
224
- // Watch for time extent changes
225
- timeSlider.watch("timeExtent", (timeExtent) => {
226
- console.log("New time extent:", timeExtent.start, "to", timeExtent.end);
227
- updateCharts(timeExtent);
228
- });
229
-
230
- // Watch for playback state
231
- timeSlider.watch("viewModel.state", (state) => {
232
- console.log("State:", state); // ready, playing, disabled
233
- });
234
- ```
235
-
236
- ### TimeSlider Configuration
237
-
238
- ```javascript
239
- const timeSlider = new TimeSlider({
240
- container: "timeSliderDiv",
241
- view: view,
242
- fullTimeExtent: {
243
- start: new Date("2020-01-01"),
244
- end: new Date("2024-12-31")
245
- },
246
-
247
- // Display options
248
- layout: "auto", // auto, compact, wide
249
- tickConfigs: [{
250
- mode: "position",
251
- values: [
252
- new Date("2021-01-01"),
253
- new Date("2022-01-01"),
254
- new Date("2023-01-01"),
255
- new Date("2024-01-01")
256
- ],
257
- labelsVisible: true,
258
- labelFormatFunction: (value) => value.getFullYear().toString()
259
- }],
260
-
261
- // Behavior
262
- playRate: 2000,
263
- loop: true,
264
-
265
- // Labels
266
- labelFormatFunction: (value, type, element, layout) => {
267
- const date = new Date(value);
268
- if (type === "min" || type === "max") {
269
- return date.toLocaleDateString();
270
- }
271
- return date.toLocaleDateString("en-US", { month: "short", year: "numeric" });
272
- }
273
- });
274
- ```
275
-
276
- ## Time-Aware Layers
277
-
278
- ### FeatureLayer with Time
279
-
280
- ```javascript
281
- const featureLayer = new FeatureLayer({
282
- url: "https://services.arcgis.com/.../FeatureServer/0",
283
- timeInfo: {
284
- startField: "event_date", // Field containing date
285
- endField: "end_date", // Optional end date field
286
- interval: {
287
- value: 1,
288
- unit: "days"
289
- }
290
- }
291
- });
292
-
293
- // Check if layer supports time
294
- if (featureLayer.timeInfo) {
295
- console.log("Time field:", featureLayer.timeInfo.startField);
296
- console.log("Full extent:", featureLayer.timeInfo.fullTimeExtent);
297
- }
298
- ```
299
-
300
- ### TimeInfo Properties
301
-
302
- ```javascript
303
- const timeInfo = {
304
- startField: "start_time", // Required: start date field
305
- endField: "end_time", // Optional: end date field (for duration)
306
- fullTimeExtent: { // Data's full time range
307
- start: new Date("2020-01-01"),
308
- end: new Date("2024-12-31")
309
- },
310
- interval: { // Suggested animation interval
311
- value: 1,
312
- unit: "months"
313
- },
314
- useTime: true // Enable time filtering
315
- };
316
- ```
317
-
318
- ### ImageryLayer with Time
319
-
320
- ```javascript
321
- const imageryLayer = new ImageryLayer({
322
- url: "https://services.arcgis.com/.../ImageServer",
323
- timeInfo: {
324
- startField: "acquisition_date"
325
- }
326
- });
327
-
328
- // Multidimensional imagery
329
- const multidimLayer = new ImageryLayer({
330
- url: "https://services.arcgis.com/.../ImageServer",
331
- multidimensionalDefinition: [{
332
- variableName: "temperature",
333
- dimensionName: "StdTime",
334
- values: [1609459200000] // Epoch milliseconds
335
- }]
336
- });
337
- ```
338
-
339
- ### MapImageLayer with Time
340
-
341
- ```javascript
342
- const mapImageLayer = new MapImageLayer({
343
- url: "https://services.arcgis.com/.../MapServer",
344
- timeInfo: {
345
- startField: "date_field"
346
- }
347
- });
348
- ```
349
-
350
- ### StreamLayer with Time
351
-
352
- ```javascript
353
- const streamLayer = new StreamLayer({
354
- url: "wss://services.arcgis.com/.../StreamServer",
355
- timeInfo: {
356
- trackIdField: "vehicle_id",
357
- startField: "timestamp"
358
- },
359
- purgeOptions: {
360
- displayCount: 1000,
361
- age: 5 // Minutes to keep
362
- }
363
- });
364
- ```
365
-
366
- ## Initializing TimeSlider from Layer
367
-
368
- ```javascript
369
- // Auto-configure from time-aware layer
370
- await featureLayer.load();
371
-
372
- const timeSlider = new TimeSlider({
373
- container: "timeSliderDiv",
374
- view: view
375
- });
376
-
377
- // Initialize from layer's time info
378
- TimeSlider.getPropertiesFromWebMap(webMap).then((properties) => {
379
- timeSlider.set(properties);
380
- });
381
-
382
- // Or manually from layer
383
- if (featureLayer.timeInfo) {
384
- timeSlider.fullTimeExtent = featureLayer.timeInfo.fullTimeExtent;
385
- timeSlider.stops = {
386
- interval: featureLayer.timeInfo.interval
387
- };
388
- }
389
- ```
390
-
391
- ## TimeZoneLabel
392
-
393
- Display time zone information.
394
-
395
- ### TimeZoneLabel Component
396
- ```html
397
- <arcgis-map>
398
- <arcgis-time-zone-label slot="bottom-left"></arcgis-time-zone-label>
399
- </arcgis-map>
400
- ```
401
-
402
- ### TimeZoneLabel Widget (Core API) - Deprecated
403
-
404
- > **DEPRECATED since 4.33:** Use the `arcgis-time-zone-label` component shown above instead. For information on widget deprecation, see [Esri's move to web components](https://developers.arcgis.com/javascript/latest/components-transition-plan/).
405
-
406
- ```javascript
407
- // DEPRECATED - Use arcgis-time-zone-label component instead
408
- import TimeZoneLabel from "@arcgis/core/widgets/TimeZoneLabel.js";
409
-
410
- const timeZoneLabel = new TimeZoneLabel({
411
- view: view
412
- });
413
-
414
- view.ui.add(timeZoneLabel, "bottom-left");
415
- ```
416
-
417
- ## Filtering by Time
418
-
419
- ### Client-Side Filter
420
-
421
- ```javascript
422
- // Apply time filter to view
423
- view.timeExtent = new TimeExtent({
424
- start: new Date("2024-01-01"),
425
- end: new Date("2024-06-30")
426
- });
427
-
428
- // Filter specific layer
429
- layerView.filter = {
430
- timeExtent: new TimeExtent({
431
- start: new Date("2024-03-01"),
432
- end: new Date("2024-03-31")
433
- })
434
- };
435
- ```
436
-
437
- ### Query with Time
438
-
439
- ```javascript
440
- const query = featureLayer.createQuery();
441
- query.timeExtent = new TimeExtent({
442
- start: new Date("2024-01-01"),
443
- end: new Date("2024-12-31")
444
- });
445
- query.where = "status = 'active'";
446
- query.returnGeometry = true;
447
-
448
- const results = await featureLayer.queryFeatures(query);
449
- ```
450
-
451
- ## Animation Patterns
452
-
453
- ### Manual Animation Loop
454
-
455
- ```javascript
456
- async function animateOverTime(layer, startDate, endDate, intervalDays) {
457
- const current = new Date(startDate);
458
-
459
- while (current <= endDate) {
460
- const nextDate = new Date(current);
461
- nextDate.setDate(nextDate.getDate() + intervalDays);
462
-
463
- view.timeExtent = new TimeExtent({
464
- start: current,
465
- end: nextDate
466
- });
467
-
468
- await new Promise(resolve => setTimeout(resolve, 500));
469
-
470
- current.setDate(current.getDate() + intervalDays);
471
- }
472
- }
473
-
474
- // Usage
475
- animateOverTime(
476
- featureLayer,
477
- new Date("2024-01-01"),
478
- new Date("2024-12-31"),
479
- 7 // Weekly intervals
480
- );
481
- ```
482
-
483
- ### TimeSlider with Statistics
484
-
485
- ```javascript
486
- // Update statistics panel as time changes
487
- timeSlider.watch("timeExtent", async (timeExtent) => {
488
- const query = featureLayer.createQuery();
489
- query.timeExtent = timeExtent;
490
- query.outStatistics = [{
491
- statisticType: "count",
492
- onStatisticField: "OBJECTID",
493
- outStatisticFieldName: "count"
494
- }, {
495
- statisticType: "sum",
496
- onStatisticField: "value",
497
- outStatisticFieldName: "total"
498
- }];
499
-
500
- const result = await featureLayer.queryFeatures(query);
501
- const stats = result.features[0].attributes;
502
-
503
- document.getElementById("count").textContent = stats.count;
504
- document.getElementById("total").textContent = stats.total;
505
- });
506
- ```
507
-
508
- ### Synchronized TimeSliders
509
-
510
- ```javascript
511
- // Sync multiple time sliders
512
- const mainSlider = new TimeSlider({
513
- container: "mainSliderDiv",
514
- view: mainView
515
- });
516
-
517
- const compareSlider = new TimeSlider({
518
- container: "compareSliderDiv",
519
- view: compareView
520
- });
521
-
522
- // Sync them
523
- mainSlider.watch("timeExtent", (timeExtent) => {
524
- compareSlider.timeExtent = timeExtent;
525
- });
526
- ```
527
-
528
- ## Historical Data Visualization
529
-
530
- ### Track Lines
531
-
532
- ```javascript
533
- // Show movement paths with time
534
- const featureLayer = new FeatureLayer({
535
- url: "https://.../tracks/FeatureServer/0",
536
- timeInfo: {
537
- startField: "timestamp",
538
- trackIdField: "vehicle_id"
539
- },
540
- renderer: {
541
- type: "simple",
542
- symbol: {
543
- type: "simple-line",
544
- color: "blue",
545
- width: 2
546
- }
547
- }
548
- });
549
- ```
550
-
551
- ### Age-Based Styling
552
-
553
- ```javascript
554
- // Style features based on age
555
- const featureLayer = new FeatureLayer({
556
- url: "...",
557
- renderer: {
558
- type: "simple",
559
- symbol: { type: "simple-marker", size: 8 },
560
- visualVariables: [{
561
- type: "color",
562
- field: "timestamp",
563
- stops: [
564
- { value: Date.now() - 86400000, color: "red" }, // 1 day old
565
- { value: Date.now() - 604800000, color: "yellow" }, // 1 week old
566
- { value: Date.now() - 2592000000, color: "gray" } // 30 days old
567
- ]
568
- }]
569
- }
570
- });
571
- ```
572
-
573
- ## Common Patterns
574
-
575
- ### Full TimeSlider Setup
576
-
577
- ```javascript
578
- async function setupTimeSlider(view, layer) {
579
- await layer.load();
580
-
581
- if (!layer.timeInfo) {
582
- console.warn("Layer is not time-aware");
583
- return null;
584
- }
585
-
586
- const timeSlider = new TimeSlider({
587
- container: "timeSliderDiv",
588
- view: view,
589
- fullTimeExtent: layer.timeInfo.fullTimeExtent,
590
- mode: "time-window",
591
- playRate: 1000,
592
- loop: true,
593
- stops: {
594
- interval: layer.timeInfo.interval || { value: 1, unit: "months" }
595
- }
596
- });
597
-
598
- // Update UI on time change
599
- timeSlider.watch("timeExtent", (extent) => {
600
- document.getElementById("currentTime").textContent =
601
- `${extent.start.toLocaleDateString()} - ${extent.end.toLocaleDateString()}`;
602
- });
603
-
604
- return timeSlider;
605
- }
606
- ```
607
-
608
- ### Time-Aware Query
609
-
610
- ```javascript
611
- async function queryByTimeRange(layer, startDate, endDate) {
612
- const query = layer.createQuery();
613
- query.timeExtent = new TimeExtent({
614
- start: startDate,
615
- end: endDate
616
- });
617
- query.outFields = ["*"];
618
- query.returnGeometry = true;
619
-
620
- return await layer.queryFeatures(query);
621
- }
622
-
623
- // Usage
624
- const marchData = await queryByTimeRange(
625
- featureLayer,
626
- new Date("2024-03-01"),
627
- new Date("2024-03-31")
628
- );
629
- ```
630
-
631
- ### Time-Based Highlighting
632
-
633
- ```javascript
634
- // Highlight features from current time period
635
- timeSlider.watch("timeExtent", async (timeExtent) => {
636
- // Clear previous highlights
637
- if (highlightHandle) {
638
- highlightHandle.remove();
639
- }
640
-
641
- // Query features in current time extent
642
- const query = featureLayer.createQuery();
643
- query.timeExtent = timeExtent;
644
-
645
- const layerView = await view.whenLayerView(featureLayer);
646
- const results = await featureLayer.queryFeatures(query);
647
-
648
- highlightHandle = layerView.highlight(results.features);
649
- });
650
- ```
651
-
652
- ## TypeScript Usage
653
-
654
- Time configurations use autocasting. For TypeScript safety, use `as const`:
655
-
656
- ```typescript
657
- // Use 'as const' for time slider configuration
658
- const timeSlider = new TimeSlider({
659
- view: view,
660
- mode: "time-window",
661
- stops: {
662
- interval: {
663
- value: 1,
664
- unit: "hours"
665
- }
666
- } as const
667
- });
668
-
669
- // For layer time settings
670
- layer.timeInfo = {
671
- startField: "start_date",
672
- endField: "end_date",
673
- interval: {
674
- value: 1,
675
- unit: "days"
676
- }
677
- } as const;
678
- ```
679
-
680
- > **Tip:** See [arcgis-core-maps skill](../arcgis-core-maps/SKILL.md) for detailed guidance on autocasting vs explicit classes.
681
-
682
- ## Common Pitfalls
683
-
684
- 1. **Time Zone Issues**: Dates are affected by time zones
685
- ```javascript
686
- // Use UTC dates for consistency
687
- const date = new Date("2024-06-15T00:00:00Z");
688
-
689
- // Or specify timezone
690
- const localDate = new Date("2024-06-15T00:00:00-05:00");
691
- ```
692
-
693
- 2. **Layer Must Be Loaded**: TimeInfo is only available after loading
694
- ```javascript
695
- await layer.load();
696
- if (layer.timeInfo) {
697
- // Now timeInfo is available
698
- }
699
- ```
700
-
701
- 3. **TimeExtent Not Applied**: View's timeExtent must be set
702
- ```javascript
703
- // Set on view to filter all time-aware layers
704
- view.timeExtent = timeExtent;
705
-
706
- // Or on specific layerView for per-layer filtering
707
- layerView.filter = { timeExtent };
708
- ```
709
-
710
- 4. **Null vs Undefined**: Use null to show all data
711
- ```javascript
712
- // Show all data (no time filter)
713
- view.timeExtent = null;
714
- ```
715
-
716
- 5. **Performance**: Large time ranges can be slow
717
- ```javascript
718
- // Consider using time-based queries instead of filtering
719
- const query = layer.createQuery();
720
- query.timeExtent = smallerTimeRange;
721
- ```
722
-
1
+ ---
2
+ name: arcgis-time-animation
3
+ description: Work with temporal data using TimeSlider, TimeExtent, and time-aware layers. Use for animating data over time, filtering by date ranges, and visualizing temporal patterns.
4
+ ---
5
+
6
+ # ArcGIS Time Animation
7
+
8
+ Use this skill for working with temporal data, time-aware layers, and time animation controls.
9
+
10
+ ## TimeExtent
11
+
12
+ Represents a time range with start and end dates.
13
+
14
+ ```javascript
15
+ import TimeExtent from "@arcgis/core/TimeExtent.js";
16
+
17
+ // Create time extent
18
+ const timeExtent = new TimeExtent({
19
+ start: new Date("2024-01-01"),
20
+ end: new Date("2024-12-31")
21
+ });
22
+
23
+ // Apply to view
24
+ view.timeExtent = timeExtent;
25
+ ```
26
+
27
+ ### TimeExtent Properties
28
+
29
+ ```javascript
30
+ const timeExtent = new TimeExtent({
31
+ start: new Date("2024-01-01T00:00:00Z"),
32
+ end: new Date("2024-06-30T23:59:59Z")
33
+ });
34
+
35
+ console.log(timeExtent.start); // Date object
36
+ console.log(timeExtent.end); // Date object
37
+
38
+ // Check if instant (start === end)
39
+ if (timeExtent.start.getTime() === timeExtent.end.getTime()) {
40
+ console.log("This is a time instant");
41
+ }
42
+ ```
43
+
44
+ ### Null TimeExtent
45
+
46
+ ```javascript
47
+ // No time filter (show all data)
48
+ view.timeExtent = null;
49
+
50
+ // Instant in time
51
+ const instant = new TimeExtent({
52
+ start: new Date("2024-06-15"),
53
+ end: new Date("2024-06-15")
54
+ });
55
+ ```
56
+
57
+ ## TimeInterval
58
+
59
+ Represents a duration of time.
60
+
61
+ ```javascript
62
+ import TimeInterval from "@arcgis/core/TimeInterval.js";
63
+
64
+ const interval = new TimeInterval({
65
+ value: 1,
66
+ unit: "months" // milliseconds, seconds, minutes, hours, days, weeks, months, years
67
+ });
68
+ ```
69
+
70
+ ### TimeInterval Units
71
+
72
+ - `milliseconds`
73
+ - `seconds`
74
+ - `minutes`
75
+ - `hours`
76
+ - `days`
77
+ - `weeks`
78
+ - `months`
79
+ - `years`
80
+ - `decades`
81
+ - `centuries`
82
+
83
+ ## TimeSlider
84
+
85
+ Interactive widget for controlling temporal display.
86
+
87
+ ### TimeSlider Component
88
+
89
+ ```html
90
+ <arcgis-map item-id="YOUR_WEBMAP_ID">
91
+ <arcgis-time-slider
92
+ slot="bottom-left"
93
+ mode="time-window"
94
+ loop
95
+ time-visible>
96
+ </arcgis-time-slider>
97
+ </arcgis-map>
98
+
99
+ <script type="module">
100
+ const map = document.querySelector("arcgis-map");
101
+ const timeSlider = document.querySelector("arcgis-time-slider");
102
+
103
+ await map.viewOnReady();
104
+
105
+ // Configure from layer
106
+ const layer = map.view.map.layers.find(l => l.timeInfo);
107
+ if (layer) {
108
+ await layer.load();
109
+ timeSlider.fullTimeExtent = layer.timeInfo.fullTimeExtent;
110
+ timeSlider.stops = { interval: layer.timeInfo.interval };
111
+ }
112
+ </script>
113
+ ```
114
+
115
+ ### TimeSlider Widget (Core API)
116
+
117
+ ```javascript
118
+ import TimeSlider from "@arcgis/core/widgets/TimeSlider.js";
119
+
120
+ const timeSlider = new TimeSlider({
121
+ container: "timeSliderDiv",
122
+ view: view,
123
+ fullTimeExtent: {
124
+ start: new Date("2020-01-01"),
125
+ end: new Date("2024-12-31")
126
+ },
127
+ timeExtent: {
128
+ start: new Date("2024-01-01"),
129
+ end: new Date("2024-03-31")
130
+ }
131
+ });
132
+ ```
133
+
134
+ ### TimeSlider with Playback
135
+
136
+ ```javascript
137
+ const timeSlider = new TimeSlider({
138
+ container: "timeSliderDiv",
139
+ view: view,
140
+ mode: "time-window", // instant, time-window, cumulative-from-start, cumulative-from-end
141
+ fullTimeExtent: {
142
+ start: new Date("2020-01-01"),
143
+ end: new Date("2024-12-31")
144
+ },
145
+ playRate: 1000, // Milliseconds between steps
146
+ loop: true,
147
+ stops: {
148
+ interval: {
149
+ value: 1,
150
+ unit: "months"
151
+ }
152
+ }
153
+ });
154
+
155
+ // Start/stop playback
156
+ timeSlider.play();
157
+ timeSlider.stop();
158
+ ```
159
+
160
+ ### TimeSlider Modes
161
+
162
+ ```javascript
163
+ // Instant - single point in time
164
+ const instantSlider = new TimeSlider({
165
+ mode: "instant",
166
+ fullTimeExtent: { start, end }
167
+ });
168
+
169
+ // Time Window - range with start and end
170
+ const windowSlider = new TimeSlider({
171
+ mode: "time-window",
172
+ fullTimeExtent: { start, end }
173
+ });
174
+
175
+ // Cumulative from Start - everything from start to current
176
+ const cumulativeStart = new TimeSlider({
177
+ mode: "cumulative-from-start",
178
+ fullTimeExtent: { start, end }
179
+ });
180
+
181
+ // Cumulative from End - everything from current to end
182
+ const cumulativeEnd = new TimeSlider({
183
+ mode: "cumulative-from-end",
184
+ fullTimeExtent: { start, end }
185
+ });
186
+ ```
187
+
188
+ ### Custom Stops
189
+
190
+ ```javascript
191
+ // Interval-based stops
192
+ const timeSlider = new TimeSlider({
193
+ stops: {
194
+ interval: {
195
+ value: 1,
196
+ unit: "weeks"
197
+ }
198
+ }
199
+ });
200
+
201
+ // Specific dates
202
+ const timeSlider = new TimeSlider({
203
+ stops: {
204
+ dates: [
205
+ new Date("2024-01-01"),
206
+ new Date("2024-04-01"),
207
+ new Date("2024-07-01"),
208
+ new Date("2024-10-01")
209
+ ]
210
+ }
211
+ });
212
+
213
+ // Number of stops (evenly distributed)
214
+ const timeSlider = new TimeSlider({
215
+ stops: {
216
+ count: 12 // 12 evenly spaced stops
217
+ }
218
+ });
219
+ ```
220
+
221
+ ### TimeSlider Events
222
+
223
+ ```javascript
224
+ // Watch for time extent changes
225
+ timeSlider.watch("timeExtent", (timeExtent) => {
226
+ console.log("New time extent:", timeExtent.start, "to", timeExtent.end);
227
+ updateCharts(timeExtent);
228
+ });
229
+
230
+ // Watch for playback state
231
+ timeSlider.watch("viewModel.state", (state) => {
232
+ console.log("State:", state); // ready, playing, disabled
233
+ });
234
+ ```
235
+
236
+ ### TimeSlider Configuration
237
+
238
+ ```javascript
239
+ const timeSlider = new TimeSlider({
240
+ container: "timeSliderDiv",
241
+ view: view,
242
+ fullTimeExtent: {
243
+ start: new Date("2020-01-01"),
244
+ end: new Date("2024-12-31")
245
+ },
246
+
247
+ // Display options
248
+ layout: "auto", // auto, compact, wide
249
+ tickConfigs: [{
250
+ mode: "position",
251
+ values: [
252
+ new Date("2021-01-01"),
253
+ new Date("2022-01-01"),
254
+ new Date("2023-01-01"),
255
+ new Date("2024-01-01")
256
+ ],
257
+ labelsVisible: true,
258
+ labelFormatFunction: (value) => value.getFullYear().toString()
259
+ }],
260
+
261
+ // Behavior
262
+ playRate: 2000,
263
+ loop: true,
264
+
265
+ // Labels
266
+ labelFormatFunction: (value, type, element, layout) => {
267
+ const date = new Date(value);
268
+ if (type === "min" || type === "max") {
269
+ return date.toLocaleDateString();
270
+ }
271
+ return date.toLocaleDateString("en-US", { month: "short", year: "numeric" });
272
+ }
273
+ });
274
+ ```
275
+
276
+ ## Time-Aware Layers
277
+
278
+ ### FeatureLayer with Time
279
+
280
+ ```javascript
281
+ const featureLayer = new FeatureLayer({
282
+ url: "https://services.arcgis.com/.../FeatureServer/0",
283
+ timeInfo: {
284
+ startField: "event_date", // Field containing date
285
+ endField: "end_date", // Optional end date field
286
+ interval: {
287
+ value: 1,
288
+ unit: "days"
289
+ }
290
+ }
291
+ });
292
+
293
+ // Check if layer supports time
294
+ if (featureLayer.timeInfo) {
295
+ console.log("Time field:", featureLayer.timeInfo.startField);
296
+ console.log("Full extent:", featureLayer.timeInfo.fullTimeExtent);
297
+ }
298
+ ```
299
+
300
+ ### TimeInfo Properties
301
+
302
+ ```javascript
303
+ const timeInfo = {
304
+ startField: "start_time", // Required: start date field
305
+ endField: "end_time", // Optional: end date field (for duration)
306
+ fullTimeExtent: { // Data's full time range
307
+ start: new Date("2020-01-01"),
308
+ end: new Date("2024-12-31")
309
+ },
310
+ interval: { // Suggested animation interval
311
+ value: 1,
312
+ unit: "months"
313
+ },
314
+ useTime: true // Enable time filtering
315
+ };
316
+ ```
317
+
318
+ ### ImageryLayer with Time
319
+
320
+ ```javascript
321
+ const imageryLayer = new ImageryLayer({
322
+ url: "https://services.arcgis.com/.../ImageServer",
323
+ timeInfo: {
324
+ startField: "acquisition_date"
325
+ }
326
+ });
327
+
328
+ // Multidimensional imagery
329
+ const multidimLayer = new ImageryLayer({
330
+ url: "https://services.arcgis.com/.../ImageServer",
331
+ multidimensionalDefinition: [{
332
+ variableName: "temperature",
333
+ dimensionName: "StdTime",
334
+ values: [1609459200000] // Epoch milliseconds
335
+ }]
336
+ });
337
+ ```
338
+
339
+ ### MapImageLayer with Time
340
+
341
+ ```javascript
342
+ const mapImageLayer = new MapImageLayer({
343
+ url: "https://services.arcgis.com/.../MapServer",
344
+ timeInfo: {
345
+ startField: "date_field"
346
+ }
347
+ });
348
+ ```
349
+
350
+ ### StreamLayer with Time
351
+
352
+ ```javascript
353
+ const streamLayer = new StreamLayer({
354
+ url: "wss://services.arcgis.com/.../StreamServer",
355
+ timeInfo: {
356
+ trackIdField: "vehicle_id",
357
+ startField: "timestamp"
358
+ },
359
+ purgeOptions: {
360
+ displayCount: 1000,
361
+ age: 5 // Minutes to keep
362
+ }
363
+ });
364
+ ```
365
+
366
+ ## Initializing TimeSlider from Layer
367
+
368
+ ```javascript
369
+ // Auto-configure from time-aware layer
370
+ await featureLayer.load();
371
+
372
+ const timeSlider = new TimeSlider({
373
+ container: "timeSliderDiv",
374
+ view: view
375
+ });
376
+
377
+ // Initialize from layer's time info
378
+ TimeSlider.getPropertiesFromWebMap(webMap).then((properties) => {
379
+ timeSlider.set(properties);
380
+ });
381
+
382
+ // Or manually from layer
383
+ if (featureLayer.timeInfo) {
384
+ timeSlider.fullTimeExtent = featureLayer.timeInfo.fullTimeExtent;
385
+ timeSlider.stops = {
386
+ interval: featureLayer.timeInfo.interval
387
+ };
388
+ }
389
+ ```
390
+
391
+ ## TimeZoneLabel
392
+
393
+ Display time zone information.
394
+
395
+ ### TimeZoneLabel Component
396
+ ```html
397
+ <arcgis-map>
398
+ <arcgis-time-zone-label slot="bottom-left"></arcgis-time-zone-label>
399
+ </arcgis-map>
400
+ ```
401
+
402
+ ### TimeZoneLabel Widget (Core API) - Deprecated
403
+
404
+ > **DEPRECATED since 4.33:** Use the `arcgis-time-zone-label` component shown above instead. For information on widget deprecation, see [Esri's move to web components](https://developers.arcgis.com/javascript/latest/components-transition-plan/).
405
+
406
+ ```javascript
407
+ // DEPRECATED - Use arcgis-time-zone-label component instead
408
+ import TimeZoneLabel from "@arcgis/core/widgets/TimeZoneLabel.js";
409
+
410
+ const timeZoneLabel = new TimeZoneLabel({
411
+ view: view
412
+ });
413
+
414
+ view.ui.add(timeZoneLabel, "bottom-left");
415
+ ```
416
+
417
+ ## Filtering by Time
418
+
419
+ ### Client-Side Filter
420
+
421
+ ```javascript
422
+ // Apply time filter to view
423
+ view.timeExtent = new TimeExtent({
424
+ start: new Date("2024-01-01"),
425
+ end: new Date("2024-06-30")
426
+ });
427
+
428
+ // Filter specific layer
429
+ layerView.filter = {
430
+ timeExtent: new TimeExtent({
431
+ start: new Date("2024-03-01"),
432
+ end: new Date("2024-03-31")
433
+ })
434
+ };
435
+ ```
436
+
437
+ ### Query with Time
438
+
439
+ ```javascript
440
+ const query = featureLayer.createQuery();
441
+ query.timeExtent = new TimeExtent({
442
+ start: new Date("2024-01-01"),
443
+ end: new Date("2024-12-31")
444
+ });
445
+ query.where = "status = 'active'";
446
+ query.returnGeometry = true;
447
+
448
+ const results = await featureLayer.queryFeatures(query);
449
+ ```
450
+
451
+ ## Animation Patterns
452
+
453
+ ### Manual Animation Loop
454
+
455
+ ```javascript
456
+ async function animateOverTime(layer, startDate, endDate, intervalDays) {
457
+ const current = new Date(startDate);
458
+
459
+ while (current <= endDate) {
460
+ const nextDate = new Date(current);
461
+ nextDate.setDate(nextDate.getDate() + intervalDays);
462
+
463
+ view.timeExtent = new TimeExtent({
464
+ start: current,
465
+ end: nextDate
466
+ });
467
+
468
+ await new Promise(resolve => setTimeout(resolve, 500));
469
+
470
+ current.setDate(current.getDate() + intervalDays);
471
+ }
472
+ }
473
+
474
+ // Usage
475
+ animateOverTime(
476
+ featureLayer,
477
+ new Date("2024-01-01"),
478
+ new Date("2024-12-31"),
479
+ 7 // Weekly intervals
480
+ );
481
+ ```
482
+
483
+ ### TimeSlider with Statistics
484
+
485
+ ```javascript
486
+ // Update statistics panel as time changes
487
+ timeSlider.watch("timeExtent", async (timeExtent) => {
488
+ const query = featureLayer.createQuery();
489
+ query.timeExtent = timeExtent;
490
+ query.outStatistics = [{
491
+ statisticType: "count",
492
+ onStatisticField: "OBJECTID",
493
+ outStatisticFieldName: "count"
494
+ }, {
495
+ statisticType: "sum",
496
+ onStatisticField: "value",
497
+ outStatisticFieldName: "total"
498
+ }];
499
+
500
+ const result = await featureLayer.queryFeatures(query);
501
+ const stats = result.features[0].attributes;
502
+
503
+ document.getElementById("count").textContent = stats.count;
504
+ document.getElementById("total").textContent = stats.total;
505
+ });
506
+ ```
507
+
508
+ ### Synchronized TimeSliders
509
+
510
+ ```javascript
511
+ // Sync multiple time sliders
512
+ const mainSlider = new TimeSlider({
513
+ container: "mainSliderDiv",
514
+ view: mainView
515
+ });
516
+
517
+ const compareSlider = new TimeSlider({
518
+ container: "compareSliderDiv",
519
+ view: compareView
520
+ });
521
+
522
+ // Sync them
523
+ mainSlider.watch("timeExtent", (timeExtent) => {
524
+ compareSlider.timeExtent = timeExtent;
525
+ });
526
+ ```
527
+
528
+ ## Historical Data Visualization
529
+
530
+ ### Track Lines
531
+
532
+ ```javascript
533
+ // Show movement paths with time
534
+ const featureLayer = new FeatureLayer({
535
+ url: "https://.../tracks/FeatureServer/0",
536
+ timeInfo: {
537
+ startField: "timestamp",
538
+ trackIdField: "vehicle_id"
539
+ },
540
+ renderer: {
541
+ type: "simple",
542
+ symbol: {
543
+ type: "simple-line",
544
+ color: "blue",
545
+ width: 2
546
+ }
547
+ }
548
+ });
549
+ ```
550
+
551
+ ### Age-Based Styling
552
+
553
+ ```javascript
554
+ // Style features based on age
555
+ const featureLayer = new FeatureLayer({
556
+ url: "...",
557
+ renderer: {
558
+ type: "simple",
559
+ symbol: { type: "simple-marker", size: 8 },
560
+ visualVariables: [{
561
+ type: "color",
562
+ field: "timestamp",
563
+ stops: [
564
+ { value: Date.now() - 86400000, color: "red" }, // 1 day old
565
+ { value: Date.now() - 604800000, color: "yellow" }, // 1 week old
566
+ { value: Date.now() - 2592000000, color: "gray" } // 30 days old
567
+ ]
568
+ }]
569
+ }
570
+ });
571
+ ```
572
+
573
+ ## Common Patterns
574
+
575
+ ### Full TimeSlider Setup
576
+
577
+ ```javascript
578
+ async function setupTimeSlider(view, layer) {
579
+ await layer.load();
580
+
581
+ if (!layer.timeInfo) {
582
+ console.warn("Layer is not time-aware");
583
+ return null;
584
+ }
585
+
586
+ const timeSlider = new TimeSlider({
587
+ container: "timeSliderDiv",
588
+ view: view,
589
+ fullTimeExtent: layer.timeInfo.fullTimeExtent,
590
+ mode: "time-window",
591
+ playRate: 1000,
592
+ loop: true,
593
+ stops: {
594
+ interval: layer.timeInfo.interval || { value: 1, unit: "months" }
595
+ }
596
+ });
597
+
598
+ // Update UI on time change
599
+ timeSlider.watch("timeExtent", (extent) => {
600
+ document.getElementById("currentTime").textContent =
601
+ `${extent.start.toLocaleDateString()} - ${extent.end.toLocaleDateString()}`;
602
+ });
603
+
604
+ return timeSlider;
605
+ }
606
+ ```
607
+
608
+ ### Time-Aware Query
609
+
610
+ ```javascript
611
+ async function queryByTimeRange(layer, startDate, endDate) {
612
+ const query = layer.createQuery();
613
+ query.timeExtent = new TimeExtent({
614
+ start: startDate,
615
+ end: endDate
616
+ });
617
+ query.outFields = ["*"];
618
+ query.returnGeometry = true;
619
+
620
+ return await layer.queryFeatures(query);
621
+ }
622
+
623
+ // Usage
624
+ const marchData = await queryByTimeRange(
625
+ featureLayer,
626
+ new Date("2024-03-01"),
627
+ new Date("2024-03-31")
628
+ );
629
+ ```
630
+
631
+ ### Time-Based Highlighting
632
+
633
+ ```javascript
634
+ // Highlight features from current time period
635
+ timeSlider.watch("timeExtent", async (timeExtent) => {
636
+ // Clear previous highlights
637
+ if (highlightHandle) {
638
+ highlightHandle.remove();
639
+ }
640
+
641
+ // Query features in current time extent
642
+ const query = featureLayer.createQuery();
643
+ query.timeExtent = timeExtent;
644
+
645
+ const layerView = await view.whenLayerView(featureLayer);
646
+ const results = await featureLayer.queryFeatures(query);
647
+
648
+ highlightHandle = layerView.highlight(results.features);
649
+ });
650
+ ```
651
+
652
+ ## TypeScript Usage
653
+
654
+ Time configurations use autocasting. For TypeScript safety, use `as const`:
655
+
656
+ ```typescript
657
+ // Use 'as const' for time slider configuration
658
+ const timeSlider = new TimeSlider({
659
+ view: view,
660
+ mode: "time-window",
661
+ stops: {
662
+ interval: {
663
+ value: 1,
664
+ unit: "hours"
665
+ }
666
+ } as const
667
+ });
668
+
669
+ // For layer time settings
670
+ layer.timeInfo = {
671
+ startField: "start_date",
672
+ endField: "end_date",
673
+ interval: {
674
+ value: 1,
675
+ unit: "days"
676
+ }
677
+ } as const;
678
+ ```
679
+
680
+ > **Tip:** See [arcgis-core-maps skill](../arcgis-core-maps/SKILL.md) for detailed guidance on autocasting vs explicit classes.
681
+
682
+ ## Common Pitfalls
683
+
684
+ 1. **Time Zone Issues**: Dates are affected by time zones
685
+ ```javascript
686
+ // Use UTC dates for consistency
687
+ const date = new Date("2024-06-15T00:00:00Z");
688
+
689
+ // Or specify timezone
690
+ const localDate = new Date("2024-06-15T00:00:00-05:00");
691
+ ```
692
+
693
+ 2. **Layer Must Be Loaded**: TimeInfo is only available after loading
694
+ ```javascript
695
+ await layer.load();
696
+ if (layer.timeInfo) {
697
+ // Now timeInfo is available
698
+ }
699
+ ```
700
+
701
+ 3. **TimeExtent Not Applied**: View's timeExtent must be set
702
+ ```javascript
703
+ // Set on view to filter all time-aware layers
704
+ view.timeExtent = timeExtent;
705
+
706
+ // Or on specific layerView for per-layer filtering
707
+ layerView.filter = { timeExtent };
708
+ ```
709
+
710
+ 4. **Null vs Undefined**: Use null to show all data
711
+ ```javascript
712
+ // Show all data (no time filter)
713
+ view.timeExtent = null;
714
+ ```
715
+
716
+ 5. **Performance**: Large time ranges can be slow
717
+ ```javascript
718
+ // Consider using time-based queries instead of filtering
719
+ const query = layer.createQuery();
720
+ query.timeExtent = smallerTimeRange;
721
+ ```
722
+