@saschabrunnerch/arcgis-maps-sdk-js-ai-context 0.0.1 → 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 (50) hide show
  1. package/README.md +163 -201
  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/skills/arcgis-starter-app/SKILL.md +273 -0
  25. package/contexts/4.34/skills/arcgis-starter-app-extended/SKILL.md +649 -0
  26. package/contexts/4.34/{claude → skills}/arcgis-tables-forms/SKILL.md +877 -877
  27. package/contexts/4.34/{claude → skills}/arcgis-time-animation/SKILL.md +722 -722
  28. package/contexts/4.34/{claude → skills}/arcgis-utility-networks/SKILL.md +301 -301
  29. package/contexts/4.34/{claude → skills}/arcgis-visualization/SKILL.md +580 -580
  30. package/contexts/4.34/{claude → skills}/arcgis-widgets-ui/SKILL.md +574 -574
  31. package/lib/installer.js +294 -379
  32. package/package.json +45 -45
  33. package/contexts/4.34/copilot/arcgis-3d.instructions.md +0 -267
  34. package/contexts/4.34/copilot/arcgis-analysis.instructions.md +0 -294
  35. package/contexts/4.34/copilot/arcgis-arcade.instructions.md +0 -234
  36. package/contexts/4.34/copilot/arcgis-authentication.instructions.md +0 -187
  37. package/contexts/4.34/copilot/arcgis-cim-symbols.instructions.md +0 -177
  38. package/contexts/4.34/copilot/arcgis-core-maps.instructions.md +0 -246
  39. package/contexts/4.34/copilot/arcgis-core-utilities.instructions.md +0 -247
  40. package/contexts/4.34/copilot/arcgis-editing.instructions.md +0 -262
  41. package/contexts/4.34/copilot/arcgis-geometry.instructions.md +0 -225
  42. package/contexts/4.34/copilot/arcgis-layers.instructions.md +0 -278
  43. package/contexts/4.34/copilot/arcgis-popup-templates.instructions.md +0 -266
  44. package/contexts/4.34/copilot/arcgis-portal-advanced.instructions.md +0 -275
  45. package/contexts/4.34/copilot/arcgis-smart-mapping.instructions.md +0 -184
  46. package/contexts/4.34/copilot/arcgis-time-animation.instructions.md +0 -112
  47. package/contexts/4.34/copilot/arcgis-visualization.instructions.md +0 -321
  48. package/contexts/4.34/copilot/arcgis-widgets-ui.instructions.md +0 -277
  49. /package/contexts/4.34/{claude → skills}/arcgis-arcade/SKILL.md +0 -0
  50. /package/contexts/4.34/{claude → skills}/arcgis-popup-templates/SKILL.md +0 -0
@@ -1,445 +1,445 @@
1
- ---
2
- name: arcgis-custom-rendering
3
- description: Create custom layer types with WebGL rendering, custom tile layers, and blend layers. Use for advanced visualizations and custom data sources.
4
- ---
5
-
6
- # ArcGIS Custom Rendering
7
-
8
- Use this skill for creating custom layers, WebGL rendering, and advanced visualization techniques.
9
-
10
- ## Custom TileLayer
11
-
12
- ### Basic Custom TileLayer
13
- ```javascript
14
- import BaseTileLayer from "@arcgis/core/layers/BaseTileLayer.js";
15
- import esriRequest from "@arcgis/core/request.js";
16
- import Color from "@arcgis/core/Color.js";
17
-
18
- const TintLayer = BaseTileLayer.createSubclass({
19
- properties: {
20
- urlTemplate: null,
21
- tint: {
22
- value: null,
23
- type: Color
24
- }
25
- },
26
-
27
- // Generate tile URL
28
- getTileUrl(level, row, col) {
29
- return this.urlTemplate
30
- .replace("{z}", level)
31
- .replace("{x}", col)
32
- .replace("{y}", row);
33
- },
34
-
35
- // Fetch and process tiles
36
- fetchTile(level, row, col, options) {
37
- const url = this.getTileUrl(level, row, col);
38
-
39
- return esriRequest(url, {
40
- responseType: "image",
41
- signal: options?.signal
42
- }).then((response) => {
43
- const image = response.data;
44
- const width = this.tileInfo.size[0];
45
- const height = this.tileInfo.size[0];
46
-
47
- // Create canvas for processing
48
- const canvas = document.createElement("canvas");
49
- const context = canvas.getContext("2d");
50
- canvas.width = width;
51
- canvas.height = height;
52
-
53
- // Apply tint
54
- if (this.tint) {
55
- context.fillStyle = this.tint.toCss();
56
- context.fillRect(0, 0, width, height);
57
- context.globalCompositeOperation = "difference";
58
- }
59
-
60
- context.drawImage(image, 0, 0, width, height);
61
- return canvas;
62
- });
63
- }
64
- });
65
-
66
- // Use the custom layer
67
- const customLayer = new TintLayer({
68
- urlTemplate: "https://tile.opentopomap.org/{z}/{x}/{y}.png",
69
- tint: new Color("#132178"),
70
- title: "Custom Tinted Layer"
71
- });
72
-
73
- map.add(customLayer);
74
- ```
75
-
76
- ### Watch Property Changes
77
- ```javascript
78
- const CustomLayer = BaseTileLayer.createSubclass({
79
- properties: {
80
- customProperty: null
81
- },
82
-
83
- initialize() {
84
- reactiveUtils.watch(
85
- () => this.customProperty,
86
- () => {
87
- this.refresh(); // Refresh tiles when property changes
88
- }
89
- );
90
- }
91
- });
92
- ```
93
-
94
- ## Custom DynamicLayer
95
-
96
- ### Dynamic Layer from Canvas
97
- ```javascript
98
- import BaseDynamicLayer from "@arcgis/core/layers/BaseDynamicLayer.js";
99
-
100
- const CustomDynamicLayer = BaseDynamicLayer.createSubclass({
101
- properties: {
102
- data: null
103
- },
104
-
105
- // Called when layer needs to render
106
- getImageUrl(extent, width, height) {
107
- // Create canvas with specified dimensions
108
- const canvas = document.createElement("canvas");
109
- canvas.width = width;
110
- canvas.height = height;
111
- const context = canvas.getContext("2d");
112
-
113
- // Draw your custom visualization
114
- this.drawVisualization(context, extent, width, height);
115
-
116
- return canvas.toDataURL("image/png");
117
- },
118
-
119
- drawVisualization(ctx, extent, width, height) {
120
- // Convert geographic coordinates to pixel coordinates
121
- const xScale = width / (extent.xmax - extent.xmin);
122
- const yScale = height / (extent.ymax - extent.ymin);
123
-
124
- // Draw data points
125
- this.data.forEach(point => {
126
- const x = (point.x - extent.xmin) * xScale;
127
- const y = height - (point.y - extent.ymin) * yScale;
128
-
129
- ctx.beginPath();
130
- ctx.arc(x, y, 5, 0, Math.PI * 2);
131
- ctx.fillStyle = "red";
132
- ctx.fill();
133
- });
134
- }
135
- });
136
- ```
137
-
138
- ## Custom Elevation Layer
139
-
140
- ### Exaggerated Elevation
141
- ```javascript
142
- import BaseElevationLayer from "@arcgis/core/layers/BaseElevationLayer.js";
143
-
144
- const ExaggeratedElevationLayer = BaseElevationLayer.createSubclass({
145
- properties: {
146
- exaggeration: 2
147
- },
148
-
149
- load() {
150
- this._elevation = new ElevationLayer({
151
- url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"
152
- });
153
- this.addResolvingPromise(this._elevation.load());
154
- },
155
-
156
- fetchTile(level, row, col, options) {
157
- return this._elevation.fetchTile(level, row, col, options)
158
- .then((data) => {
159
- const exaggeration = this.exaggeration;
160
- for (let i = 0; i < data.values.length; i++) {
161
- data.values[i] *= exaggeration;
162
- }
163
- return data;
164
- });
165
- }
166
- });
167
- ```
168
-
169
- ## Custom Blend Layer
170
-
171
- ### Blending Multiple Layers
172
- ```javascript
173
- import BaseLayerViewGL2D from "@arcgis/core/views/2d/layers/BaseLayerViewGL2D.js";
174
-
175
- const BlendLayer = Layer.createSubclass({
176
- properties: {
177
- layers: null,
178
- blendMode: "multiply"
179
- },
180
-
181
- createLayerView(view) {
182
- if (view.type === "2d") {
183
- return new BlendLayerView2D({
184
- view: view,
185
- layer: this
186
- });
187
- }
188
- }
189
- });
190
-
191
- const BlendLayerView2D = BaseLayerViewGL2D.createSubclass({
192
- render(renderParameters) {
193
- // Custom WebGL rendering
194
- const gl = renderParameters.context;
195
- // ... WebGL operations
196
- }
197
- });
198
- ```
199
-
200
- ## WebGL Custom Rendering
201
-
202
- ### Custom LayerView with WebGL
203
- ```javascript
204
- import BaseLayerViewGL2D from "@arcgis/core/views/2d/layers/BaseLayerViewGL2D.js";
205
-
206
- const CustomLayerView2D = BaseLayerViewGL2D.createSubclass({
207
- constructor() {
208
- this.bindAttach = this.attach.bind(this);
209
- this.bindDetach = this.detach.bind(this);
210
- this.bindRender = this.render.bind(this);
211
- },
212
-
213
- attach() {
214
- const gl = this.context;
215
-
216
- // Create shaders
217
- const vertexSource = `
218
- attribute vec2 a_position;
219
- uniform mat3 u_transform;
220
- void main() {
221
- gl_Position = vec4((u_transform * vec3(a_position, 1)).xy, 0, 1);
222
- }
223
- `;
224
-
225
- const fragmentSource = `
226
- precision mediump float;
227
- uniform vec4 u_color;
228
- void main() {
229
- gl_FragColor = u_color;
230
- }
231
- `;
232
-
233
- // Compile shaders and create program
234
- this.program = this.createProgram(gl, vertexSource, fragmentSource);
235
- },
236
-
237
- render(renderParameters) {
238
- const gl = renderParameters.context;
239
- const state = renderParameters.state;
240
-
241
- // Set up transformation matrix
242
- const transform = mat3.create();
243
- mat3.translate(transform, transform, [state.size[0] / 2, state.size[1] / 2]);
244
-
245
- gl.useProgram(this.program);
246
- // ... draw operations
247
- },
248
-
249
- detach() {
250
- const gl = this.context;
251
- gl.deleteProgram(this.program);
252
- }
253
- });
254
- ```
255
-
256
- ## LERC Decoding
257
-
258
- ### Custom LERC Layer
259
- ```javascript
260
- import BaseTileLayer from "@arcgis/core/layers/BaseTileLayer.js";
261
- import esriRequest from "@arcgis/core/request.js";
262
-
263
- // Import LERC decoder
264
- import * as Lerc from "https://cdn.jsdelivr.net/npm/lerc@4.0.4/+esm";
265
-
266
- const LercLayer = BaseTileLayer.createSubclass({
267
- properties: {
268
- urlTemplate: null,
269
- minValue: 0,
270
- maxValue: 1000
271
- },
272
-
273
- fetchTile(level, row, col, options) {
274
- const url = this.urlTemplate
275
- .replace("{z}", level)
276
- .replace("{x}", col)
277
- .replace("{y}", row);
278
-
279
- return esriRequest(url, {
280
- responseType: "array-buffer",
281
- signal: options?.signal
282
- }).then((response) => {
283
- // Decode LERC data
284
- const decodedPixels = Lerc.decode(response.data);
285
- const { width, height, pixels } = decodedPixels;
286
-
287
- // Convert to canvas
288
- const canvas = document.createElement("canvas");
289
- canvas.width = width;
290
- canvas.height = height;
291
- const ctx = canvas.getContext("2d");
292
- const imageData = ctx.createImageData(width, height);
293
-
294
- // Map values to colors
295
- for (let i = 0; i < pixels[0].length; i++) {
296
- const value = pixels[0][i];
297
- const normalized = (value - this.minValue) / (this.maxValue - this.minValue);
298
- const color = this.valueToColor(normalized);
299
-
300
- imageData.data[i * 4] = color.r;
301
- imageData.data[i * 4 + 1] = color.g;
302
- imageData.data[i * 4 + 2] = color.b;
303
- imageData.data[i * 4 + 3] = 255;
304
- }
305
-
306
- ctx.putImageData(imageData, 0, 0);
307
- return canvas;
308
- });
309
- },
310
-
311
- valueToColor(value) {
312
- // Simple blue-to-red color ramp
313
- return {
314
- r: Math.round(value * 255),
315
- g: 0,
316
- b: Math.round((1 - value) * 255)
317
- };
318
- }
319
- });
320
- ```
321
-
322
- ## Custom Graphics Rendering
323
-
324
- ### Animated Lines with WebGL
325
- ```javascript
326
- // Animated lines require custom render nodes in 3D
327
- // or custom LayerView in 2D
328
-
329
- const view = new SceneView({
330
- container: "viewDiv",
331
- map: map
332
- });
333
-
334
- // Use external render node for animations
335
- view.when(() => {
336
- // Add custom render node
337
- const renderNode = new CustomRenderNode({ view });
338
- view.environment.lighting = {
339
- type: "virtual"
340
- };
341
- });
342
- ```
343
-
344
- ## Tessellation Helpers
345
-
346
- ### Using Tessellation for Complex Geometries
347
- ```javascript
348
- import tessellate from "@arcgis/core/geometry/support/tessellate.js";
349
-
350
- // Tessellate a polygon for WebGL rendering
351
- const polygon = new Polygon({
352
- rings: [[/* coordinates */]]
353
- });
354
-
355
- const tessellated = tessellate(polygon);
356
- // Use tessellated.vertices and tessellated.indices for WebGL
357
- ```
358
-
359
- ## Complete Example
360
-
361
- ```html
362
- <!DOCTYPE html>
363
- <html>
364
- <head>
365
- <link rel="stylesheet" href="https://js.arcgis.com/4.34/esri/themes/light/main.css" />
366
- <script src="https://js.arcgis.com/4.34/"></script>
367
- <style>
368
- html, body, #viewDiv { height: 100%; margin: 0; }
369
- </style>
370
- <script type="module">
371
- import Map from "@arcgis/core/Map.js";
372
- import SceneView from "@arcgis/core/views/SceneView.js";
373
- import BaseTileLayer from "@arcgis/core/layers/BaseTileLayer.js";
374
- import esriRequest from "@arcgis/core/request.js";
375
- import Color from "@arcgis/core/Color.js";
376
-
377
- // Create custom tinted tile layer
378
- const TintLayer = BaseTileLayer.createSubclass({
379
- properties: {
380
- urlTemplate: null,
381
- tint: { value: null, type: Color }
382
- },
383
-
384
- getTileUrl(level, row, col) {
385
- return this.urlTemplate
386
- .replace("{z}", level)
387
- .replace("{x}", col)
388
- .replace("{y}", row);
389
- },
390
-
391
- fetchTile(level, row, col, options) {
392
- return esriRequest(this.getTileUrl(level, row, col), {
393
- responseType: "image",
394
- signal: options?.signal
395
- }).then((response) => {
396
- const canvas = document.createElement("canvas");
397
- const ctx = canvas.getContext("2d");
398
- canvas.width = canvas.height = this.tileInfo.size[0];
399
-
400
- if (this.tint) {
401
- ctx.fillStyle = this.tint.toCss();
402
- ctx.fillRect(0, 0, canvas.width, canvas.height);
403
- ctx.globalCompositeOperation = "difference";
404
- }
405
-
406
- ctx.drawImage(response.data, 0, 0, canvas.width, canvas.height);
407
- return canvas;
408
- });
409
- }
410
- });
411
-
412
- const customLayer = new TintLayer({
413
- urlTemplate: "https://tile.opentopomap.org/{z}/{x}/{y}.png",
414
- tint: new Color("#4488ff"),
415
- title: "Custom Layer"
416
- });
417
-
418
- const map = new Map({ layers: [customLayer] });
419
-
420
- const view = new SceneView({
421
- container: "viewDiv",
422
- map: map,
423
- center: [8.5, 46],
424
- zoom: 8
425
- });
426
- </script>
427
- </head>
428
- <body>
429
- <div id="viewDiv"></div>
430
- </body>
431
- </html>
432
- ```
433
-
434
- ## Common Pitfalls
435
-
436
- 1. **CORS**: External tile servers must support CORS
437
-
438
- 2. **Canvas size**: Match tile size exactly with tileInfo
439
-
440
- 3. **Memory management**: Clean up WebGL resources in detach()
441
-
442
- 4. **Async operations**: Handle abort signals properly
443
-
444
- 5. **Coordinate systems**: Transform coordinates correctly for rendering
445
-
1
+ ---
2
+ name: arcgis-custom-rendering
3
+ description: Create custom layer types with WebGL rendering, custom tile layers, and blend layers. Use for advanced visualizations and custom data sources.
4
+ ---
5
+
6
+ # ArcGIS Custom Rendering
7
+
8
+ Use this skill for creating custom layers, WebGL rendering, and advanced visualization techniques.
9
+
10
+ ## Custom TileLayer
11
+
12
+ ### Basic Custom TileLayer
13
+ ```javascript
14
+ import BaseTileLayer from "@arcgis/core/layers/BaseTileLayer.js";
15
+ import esriRequest from "@arcgis/core/request.js";
16
+ import Color from "@arcgis/core/Color.js";
17
+
18
+ const TintLayer = BaseTileLayer.createSubclass({
19
+ properties: {
20
+ urlTemplate: null,
21
+ tint: {
22
+ value: null,
23
+ type: Color
24
+ }
25
+ },
26
+
27
+ // Generate tile URL
28
+ getTileUrl(level, row, col) {
29
+ return this.urlTemplate
30
+ .replace("{z}", level)
31
+ .replace("{x}", col)
32
+ .replace("{y}", row);
33
+ },
34
+
35
+ // Fetch and process tiles
36
+ fetchTile(level, row, col, options) {
37
+ const url = this.getTileUrl(level, row, col);
38
+
39
+ return esriRequest(url, {
40
+ responseType: "image",
41
+ signal: options?.signal
42
+ }).then((response) => {
43
+ const image = response.data;
44
+ const width = this.tileInfo.size[0];
45
+ const height = this.tileInfo.size[0];
46
+
47
+ // Create canvas for processing
48
+ const canvas = document.createElement("canvas");
49
+ const context = canvas.getContext("2d");
50
+ canvas.width = width;
51
+ canvas.height = height;
52
+
53
+ // Apply tint
54
+ if (this.tint) {
55
+ context.fillStyle = this.tint.toCss();
56
+ context.fillRect(0, 0, width, height);
57
+ context.globalCompositeOperation = "difference";
58
+ }
59
+
60
+ context.drawImage(image, 0, 0, width, height);
61
+ return canvas;
62
+ });
63
+ }
64
+ });
65
+
66
+ // Use the custom layer
67
+ const customLayer = new TintLayer({
68
+ urlTemplate: "https://tile.opentopomap.org/{z}/{x}/{y}.png",
69
+ tint: new Color("#132178"),
70
+ title: "Custom Tinted Layer"
71
+ });
72
+
73
+ map.add(customLayer);
74
+ ```
75
+
76
+ ### Watch Property Changes
77
+ ```javascript
78
+ const CustomLayer = BaseTileLayer.createSubclass({
79
+ properties: {
80
+ customProperty: null
81
+ },
82
+
83
+ initialize() {
84
+ reactiveUtils.watch(
85
+ () => this.customProperty,
86
+ () => {
87
+ this.refresh(); // Refresh tiles when property changes
88
+ }
89
+ );
90
+ }
91
+ });
92
+ ```
93
+
94
+ ## Custom DynamicLayer
95
+
96
+ ### Dynamic Layer from Canvas
97
+ ```javascript
98
+ import BaseDynamicLayer from "@arcgis/core/layers/BaseDynamicLayer.js";
99
+
100
+ const CustomDynamicLayer = BaseDynamicLayer.createSubclass({
101
+ properties: {
102
+ data: null
103
+ },
104
+
105
+ // Called when layer needs to render
106
+ getImageUrl(extent, width, height) {
107
+ // Create canvas with specified dimensions
108
+ const canvas = document.createElement("canvas");
109
+ canvas.width = width;
110
+ canvas.height = height;
111
+ const context = canvas.getContext("2d");
112
+
113
+ // Draw your custom visualization
114
+ this.drawVisualization(context, extent, width, height);
115
+
116
+ return canvas.toDataURL("image/png");
117
+ },
118
+
119
+ drawVisualization(ctx, extent, width, height) {
120
+ // Convert geographic coordinates to pixel coordinates
121
+ const xScale = width / (extent.xmax - extent.xmin);
122
+ const yScale = height / (extent.ymax - extent.ymin);
123
+
124
+ // Draw data points
125
+ this.data.forEach(point => {
126
+ const x = (point.x - extent.xmin) * xScale;
127
+ const y = height - (point.y - extent.ymin) * yScale;
128
+
129
+ ctx.beginPath();
130
+ ctx.arc(x, y, 5, 0, Math.PI * 2);
131
+ ctx.fillStyle = "red";
132
+ ctx.fill();
133
+ });
134
+ }
135
+ });
136
+ ```
137
+
138
+ ## Custom Elevation Layer
139
+
140
+ ### Exaggerated Elevation
141
+ ```javascript
142
+ import BaseElevationLayer from "@arcgis/core/layers/BaseElevationLayer.js";
143
+
144
+ const ExaggeratedElevationLayer = BaseElevationLayer.createSubclass({
145
+ properties: {
146
+ exaggeration: 2
147
+ },
148
+
149
+ load() {
150
+ this._elevation = new ElevationLayer({
151
+ url: "https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"
152
+ });
153
+ this.addResolvingPromise(this._elevation.load());
154
+ },
155
+
156
+ fetchTile(level, row, col, options) {
157
+ return this._elevation.fetchTile(level, row, col, options)
158
+ .then((data) => {
159
+ const exaggeration = this.exaggeration;
160
+ for (let i = 0; i < data.values.length; i++) {
161
+ data.values[i] *= exaggeration;
162
+ }
163
+ return data;
164
+ });
165
+ }
166
+ });
167
+ ```
168
+
169
+ ## Custom Blend Layer
170
+
171
+ ### Blending Multiple Layers
172
+ ```javascript
173
+ import BaseLayerViewGL2D from "@arcgis/core/views/2d/layers/BaseLayerViewGL2D.js";
174
+
175
+ const BlendLayer = Layer.createSubclass({
176
+ properties: {
177
+ layers: null,
178
+ blendMode: "multiply"
179
+ },
180
+
181
+ createLayerView(view) {
182
+ if (view.type === "2d") {
183
+ return new BlendLayerView2D({
184
+ view: view,
185
+ layer: this
186
+ });
187
+ }
188
+ }
189
+ });
190
+
191
+ const BlendLayerView2D = BaseLayerViewGL2D.createSubclass({
192
+ render(renderParameters) {
193
+ // Custom WebGL rendering
194
+ const gl = renderParameters.context;
195
+ // ... WebGL operations
196
+ }
197
+ });
198
+ ```
199
+
200
+ ## WebGL Custom Rendering
201
+
202
+ ### Custom LayerView with WebGL
203
+ ```javascript
204
+ import BaseLayerViewGL2D from "@arcgis/core/views/2d/layers/BaseLayerViewGL2D.js";
205
+
206
+ const CustomLayerView2D = BaseLayerViewGL2D.createSubclass({
207
+ constructor() {
208
+ this.bindAttach = this.attach.bind(this);
209
+ this.bindDetach = this.detach.bind(this);
210
+ this.bindRender = this.render.bind(this);
211
+ },
212
+
213
+ attach() {
214
+ const gl = this.context;
215
+
216
+ // Create shaders
217
+ const vertexSource = `
218
+ attribute vec2 a_position;
219
+ uniform mat3 u_transform;
220
+ void main() {
221
+ gl_Position = vec4((u_transform * vec3(a_position, 1)).xy, 0, 1);
222
+ }
223
+ `;
224
+
225
+ const fragmentSource = `
226
+ precision mediump float;
227
+ uniform vec4 u_color;
228
+ void main() {
229
+ gl_FragColor = u_color;
230
+ }
231
+ `;
232
+
233
+ // Compile shaders and create program
234
+ this.program = this.createProgram(gl, vertexSource, fragmentSource);
235
+ },
236
+
237
+ render(renderParameters) {
238
+ const gl = renderParameters.context;
239
+ const state = renderParameters.state;
240
+
241
+ // Set up transformation matrix
242
+ const transform = mat3.create();
243
+ mat3.translate(transform, transform, [state.size[0] / 2, state.size[1] / 2]);
244
+
245
+ gl.useProgram(this.program);
246
+ // ... draw operations
247
+ },
248
+
249
+ detach() {
250
+ const gl = this.context;
251
+ gl.deleteProgram(this.program);
252
+ }
253
+ });
254
+ ```
255
+
256
+ ## LERC Decoding
257
+
258
+ ### Custom LERC Layer
259
+ ```javascript
260
+ import BaseTileLayer from "@arcgis/core/layers/BaseTileLayer.js";
261
+ import esriRequest from "@arcgis/core/request.js";
262
+
263
+ // Import LERC decoder
264
+ import * as Lerc from "https://cdn.jsdelivr.net/npm/lerc@4.0.4/+esm";
265
+
266
+ const LercLayer = BaseTileLayer.createSubclass({
267
+ properties: {
268
+ urlTemplate: null,
269
+ minValue: 0,
270
+ maxValue: 1000
271
+ },
272
+
273
+ fetchTile(level, row, col, options) {
274
+ const url = this.urlTemplate
275
+ .replace("{z}", level)
276
+ .replace("{x}", col)
277
+ .replace("{y}", row);
278
+
279
+ return esriRequest(url, {
280
+ responseType: "array-buffer",
281
+ signal: options?.signal
282
+ }).then((response) => {
283
+ // Decode LERC data
284
+ const decodedPixels = Lerc.decode(response.data);
285
+ const { width, height, pixels } = decodedPixels;
286
+
287
+ // Convert to canvas
288
+ const canvas = document.createElement("canvas");
289
+ canvas.width = width;
290
+ canvas.height = height;
291
+ const ctx = canvas.getContext("2d");
292
+ const imageData = ctx.createImageData(width, height);
293
+
294
+ // Map values to colors
295
+ for (let i = 0; i < pixels[0].length; i++) {
296
+ const value = pixels[0][i];
297
+ const normalized = (value - this.minValue) / (this.maxValue - this.minValue);
298
+ const color = this.valueToColor(normalized);
299
+
300
+ imageData.data[i * 4] = color.r;
301
+ imageData.data[i * 4 + 1] = color.g;
302
+ imageData.data[i * 4 + 2] = color.b;
303
+ imageData.data[i * 4 + 3] = 255;
304
+ }
305
+
306
+ ctx.putImageData(imageData, 0, 0);
307
+ return canvas;
308
+ });
309
+ },
310
+
311
+ valueToColor(value) {
312
+ // Simple blue-to-red color ramp
313
+ return {
314
+ r: Math.round(value * 255),
315
+ g: 0,
316
+ b: Math.round((1 - value) * 255)
317
+ };
318
+ }
319
+ });
320
+ ```
321
+
322
+ ## Custom Graphics Rendering
323
+
324
+ ### Animated Lines with WebGL
325
+ ```javascript
326
+ // Animated lines require custom render nodes in 3D
327
+ // or custom LayerView in 2D
328
+
329
+ const view = new SceneView({
330
+ container: "viewDiv",
331
+ map: map
332
+ });
333
+
334
+ // Use external render node for animations
335
+ view.when(() => {
336
+ // Add custom render node
337
+ const renderNode = new CustomRenderNode({ view });
338
+ view.environment.lighting = {
339
+ type: "virtual"
340
+ };
341
+ });
342
+ ```
343
+
344
+ ## Tessellation Helpers
345
+
346
+ ### Using Tessellation for Complex Geometries
347
+ ```javascript
348
+ import tessellate from "@arcgis/core/geometry/support/tessellate.js";
349
+
350
+ // Tessellate a polygon for WebGL rendering
351
+ const polygon = new Polygon({
352
+ rings: [[/* coordinates */]]
353
+ });
354
+
355
+ const tessellated = tessellate(polygon);
356
+ // Use tessellated.vertices and tessellated.indices for WebGL
357
+ ```
358
+
359
+ ## Complete Example
360
+
361
+ ```html
362
+ <!DOCTYPE html>
363
+ <html>
364
+ <head>
365
+ <link rel="stylesheet" href="https://js.arcgis.com/4.34/esri/themes/light/main.css" />
366
+ <script src="https://js.arcgis.com/4.34/"></script>
367
+ <style>
368
+ html, body, #viewDiv { height: 100%; margin: 0; }
369
+ </style>
370
+ <script type="module">
371
+ import Map from "@arcgis/core/Map.js";
372
+ import SceneView from "@arcgis/core/views/SceneView.js";
373
+ import BaseTileLayer from "@arcgis/core/layers/BaseTileLayer.js";
374
+ import esriRequest from "@arcgis/core/request.js";
375
+ import Color from "@arcgis/core/Color.js";
376
+
377
+ // Create custom tinted tile layer
378
+ const TintLayer = BaseTileLayer.createSubclass({
379
+ properties: {
380
+ urlTemplate: null,
381
+ tint: { value: null, type: Color }
382
+ },
383
+
384
+ getTileUrl(level, row, col) {
385
+ return this.urlTemplate
386
+ .replace("{z}", level)
387
+ .replace("{x}", col)
388
+ .replace("{y}", row);
389
+ },
390
+
391
+ fetchTile(level, row, col, options) {
392
+ return esriRequest(this.getTileUrl(level, row, col), {
393
+ responseType: "image",
394
+ signal: options?.signal
395
+ }).then((response) => {
396
+ const canvas = document.createElement("canvas");
397
+ const ctx = canvas.getContext("2d");
398
+ canvas.width = canvas.height = this.tileInfo.size[0];
399
+
400
+ if (this.tint) {
401
+ ctx.fillStyle = this.tint.toCss();
402
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
403
+ ctx.globalCompositeOperation = "difference";
404
+ }
405
+
406
+ ctx.drawImage(response.data, 0, 0, canvas.width, canvas.height);
407
+ return canvas;
408
+ });
409
+ }
410
+ });
411
+
412
+ const customLayer = new TintLayer({
413
+ urlTemplate: "https://tile.opentopomap.org/{z}/{x}/{y}.png",
414
+ tint: new Color("#4488ff"),
415
+ title: "Custom Layer"
416
+ });
417
+
418
+ const map = new Map({ layers: [customLayer] });
419
+
420
+ const view = new SceneView({
421
+ container: "viewDiv",
422
+ map: map,
423
+ center: [8.5, 46],
424
+ zoom: 8
425
+ });
426
+ </script>
427
+ </head>
428
+ <body>
429
+ <div id="viewDiv"></div>
430
+ </body>
431
+ </html>
432
+ ```
433
+
434
+ ## Common Pitfalls
435
+
436
+ 1. **CORS**: External tile servers must support CORS
437
+
438
+ 2. **Canvas size**: Match tile size exactly with tileInfo
439
+
440
+ 3. **Memory management**: Clean up WebGL resources in detach()
441
+
442
+ 4. **Async operations**: Handle abort signals properly
443
+
444
+ 5. **Coordinate systems**: Transform coordinates correctly for rendering
445
+