@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.
- package/README.md +163 -203
- package/bin/cli.js +157 -173
- package/contexts/4.34/{claude → skills}/arcgis-3d-advanced/SKILL.md +586 -586
- package/contexts/4.34/{claude → skills}/arcgis-advanced-layers/SKILL.md +431 -431
- package/contexts/4.34/{claude → skills}/arcgis-analysis-services/SKILL.md +607 -607
- package/contexts/4.34/{claude → skills}/arcgis-authentication/SKILL.md +301 -301
- package/contexts/4.34/{claude → skills}/arcgis-cim-symbols/SKILL.md +486 -486
- package/contexts/4.34/{claude → skills}/arcgis-coordinates-projection/SKILL.md +406 -406
- package/contexts/4.34/{claude → skills}/arcgis-core-maps/SKILL.md +739 -739
- package/contexts/4.34/{claude → skills}/arcgis-core-utilities/SKILL.md +732 -732
- package/contexts/4.34/{claude → skills}/arcgis-custom-rendering/SKILL.md +445 -445
- package/contexts/4.34/{claude → skills}/arcgis-editing-advanced/SKILL.md +702 -702
- package/contexts/4.34/{claude → skills}/arcgis-feature-effects/SKILL.md +393 -393
- package/contexts/4.34/{claude → skills}/arcgis-geometry-operations/SKILL.md +489 -489
- package/contexts/4.34/{claude → skills}/arcgis-imagery/SKILL.md +307 -307
- package/contexts/4.34/{claude → skills}/arcgis-interaction/SKILL.md +572 -572
- package/contexts/4.34/{claude → skills}/arcgis-knowledge-graphs/SKILL.md +582 -582
- package/contexts/4.34/{claude → skills}/arcgis-layers/SKILL.md +601 -601
- package/contexts/4.34/{claude → skills}/arcgis-map-tools/SKILL.md +668 -668
- package/contexts/4.34/{claude → skills}/arcgis-media-layers/SKILL.md +290 -290
- package/contexts/4.34/{claude → skills}/arcgis-portal-content/SKILL.md +679 -679
- package/contexts/4.34/{claude → skills}/arcgis-scene-effects/SKILL.md +512 -512
- package/contexts/4.34/{claude → skills}/arcgis-smart-mapping/SKILL.md +686 -686
- package/contexts/4.34/{claude → skills}/arcgis-starter-app-extended/SKILL.md +649 -649
- package/contexts/4.34/{claude → skills}/arcgis-tables-forms/SKILL.md +877 -877
- package/contexts/4.34/{claude → skills}/arcgis-time-animation/SKILL.md +722 -722
- package/contexts/4.34/{claude → skills}/arcgis-utility-networks/SKILL.md +301 -301
- package/contexts/4.34/{claude → skills}/arcgis-visualization/SKILL.md +580 -580
- package/contexts/4.34/{claude → skills}/arcgis-widgets-ui/SKILL.md +574 -574
- package/lib/installer.js +19 -104
- package/package.json +45 -45
- package/contexts/4.34/copilot/arcgis-3d.instructions.md +0 -267
- package/contexts/4.34/copilot/arcgis-analysis.instructions.md +0 -294
- package/contexts/4.34/copilot/arcgis-arcade.instructions.md +0 -234
- package/contexts/4.34/copilot/arcgis-authentication.instructions.md +0 -187
- package/contexts/4.34/copilot/arcgis-cim-symbols.instructions.md +0 -177
- package/contexts/4.34/copilot/arcgis-core-maps.instructions.md +0 -246
- package/contexts/4.34/copilot/arcgis-core-utilities.instructions.md +0 -247
- package/contexts/4.34/copilot/arcgis-editing.instructions.md +0 -262
- package/contexts/4.34/copilot/arcgis-geometry.instructions.md +0 -225
- package/contexts/4.34/copilot/arcgis-layers.instructions.md +0 -278
- package/contexts/4.34/copilot/arcgis-popup-templates.instructions.md +0 -266
- package/contexts/4.34/copilot/arcgis-portal-advanced.instructions.md +0 -275
- package/contexts/4.34/copilot/arcgis-smart-mapping.instructions.md +0 -184
- package/contexts/4.34/copilot/arcgis-starter-app-extended.instructions.md +0 -643
- package/contexts/4.34/copilot/arcgis-starter-app.instructions.md +0 -268
- package/contexts/4.34/copilot/arcgis-time-animation.instructions.md +0 -112
- package/contexts/4.34/copilot/arcgis-visualization.instructions.md +0 -321
- package/contexts/4.34/copilot/arcgis-widgets-ui.instructions.md +0 -277
- /package/contexts/4.34/{claude → skills}/arcgis-arcade/SKILL.md +0 -0
- /package/contexts/4.34/{claude → skills}/arcgis-popup-templates/SKILL.md +0 -0
- /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
|
+
|