Add and manage data layers in ArcGIS maps. Use for FeatureLayer, TileLayer, SceneLayer, GeoJSONLayer, and all other layer types. Covers layer configuration, queries, and management.
61
72%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Fix and improve this skill with Tessl
tessl review fix ./contexts/4.34/skills/arcgis-layers/SKILL.mdUse this skill when adding, configuring, or querying data layers in ArcGIS maps.
| Layer Type | Use Case | Geometry |
|---|---|---|
| FeatureLayer | Most common - feature services, editable data | Points, lines, polygons |
| GraphicsLayer | Client-side temporary graphics | Any |
| GeoJSONLayer | GeoJSON files/URLs | Points, lines, polygons |
| CSVLayer | CSV files with coordinates | Points |
| TileLayer | Cached map tiles | Raster |
| VectorTileLayer | Vector tiles (Mapbox style) | Vector |
| ImageryTileLayer | Raster imagery | Raster |
| SceneLayer | 3D buildings, meshes | 3D objects |
| IntegratedMeshLayer | Photogrammetry meshes | 3D mesh |
| PointCloudLayer | LiDAR point clouds | Points |
import FeatureLayer from "@arcgis/core/layers/FeatureLayer.js";
const featureLayer = new FeatureLayer({
url: "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/Landscape_Trees/FeatureServer/0",
outFields: ["*"],
popupTemplate: {
title: "{Tree_ID}",
content: "Height: {Crown_Height} ft"
}
});
map.add(featureLayer);const featureLayer = new FeatureLayer({
portalItem: {
id: "51c851fef66143959986b473b345b7ca"
},
outFields: ["*"]
});const featureLayer = new FeatureLayer({
source: graphics, // Array of Graphic objects
fields: [
{ name: "ObjectID", type: "oid" },
{ name: "name", type: "string" },
{ name: "value", type: "double" }
],
objectIdField: "ObjectID",
geometryType: "point",
spatialReference: { wkid: 4326 },
renderer: {
type: "simple",
symbol: { type: "simple-marker", color: "blue" }
}
});const featureLayer = new FeatureLayer({
url: "...",
outFields: ["*"], // Fields to include (* = all)
definitionExpression: "population > 10000", // SQL filter
minScale: 500000, // Hide when zoomed out beyond this
maxScale: 0, // Hide when zoomed in beyond this
visible: true, // Initial visibility
opacity: 0.8, // Transparency (0-1)
title: "Cities", // Display name
orderBy: [{ field: "population" }], // Feature draw order
labelingInfo: [...], // Labels
renderer: {...}, // Symbology
popupTemplate: {...}, // Popup configuration
elevationInfo: { // 3D elevation mode
mode: "relative-to-ground"
}
});import GraphicsLayer from "@arcgis/core/layers/GraphicsLayer.js";
import Graphic from "@arcgis/core/Graphic.js";
const graphicsLayer = new GraphicsLayer();
map.add(graphicsLayer);
// Add a point
const pointGraphic = new Graphic({
geometry: {
type: "point",
longitude: -118.24,
latitude: 34.05
},
symbol: {
type: "simple-marker",
color: "red",
size: 12
},
attributes: { name: "Los Angeles" },
popupTemplate: { title: "{name}" }
});
graphicsLayer.add(pointGraphic);
// Add multiple graphics
graphicsLayer.addMany([graphic1, graphic2, graphic3]);
// Remove graphics
graphicsLayer.remove(pointGraphic);
graphicsLayer.removeAll();import GeoJSONLayer from "@arcgis/core/layers/GeoJSONLayer.js";
const geojsonLayer = new GeoJSONLayer({
url: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson",
copyright: "USGS Earthquakes",
popupTemplate: {
title: "Earthquake Info",
content: "Magnitude {mag} hit {place} on {time}",
fieldInfos: [{
fieldName: "time",
format: { dateFormat: "short-date-short-time" }
}]
},
renderer: {
type: "simple",
symbol: { type: "simple-marker", color: "orange" },
visualVariables: [{
type: "size",
field: "mag",
stops: [
{ value: 2.5, size: "4px" },
{ value: 8, size: "40px" }
]
}]
},
orderBy: [{ field: "mag" }] // Draw smaller on top
});
map.add(geojsonLayer);import CSVLayer from "@arcgis/core/layers/CSVLayer.js";
const csvLayer = new CSVLayer({
url: "https://example.com/data.csv",
latitudeField: "latitude", // Column name for lat
longitudeField: "longitude", // Column name for lon
delimiter: ",",
popupTemplate: {
title: "{name}",
content: "{description}"
}
});import TileLayer from "@arcgis/core/layers/TileLayer.js";
const tileLayer = new TileLayer({
url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"
});
map.add(tileLayer);import VectorTileLayer from "@arcgis/core/layers/VectorTileLayer.js";
// From portal item
const vtl = new VectorTileLayer({
portalItem: { id: "VECTOR_TILE_ITEM_ID" }
});
// From URL
const vtl = new VectorTileLayer({
url: "https://basemaps.arcgis.com/v1/arcgis/rest/services/World_Basemap/VectorTileServer"
});import ImageryTileLayer from "@arcgis/core/layers/ImageryTileLayer.js";
const imageryLayer = new ImageryTileLayer({
url: "https://tiledimageservices.arcgis.com/...",
// Or from COG (Cloud Optimized GeoTIFF)
url: "https://example.com/image.tif"
});import SceneLayer from "@arcgis/core/layers/SceneLayer.js";
const sceneLayer = new SceneLayer({
portalItem: { id: "2e0761b9a4274b8db52c4bf34356911e" },
popupEnabled: false
});
// Add to SceneView map
map.add(sceneLayer);
// Custom 3D symbology
sceneLayer.renderer = {
type: "simple",
symbol: {
type: "mesh-3d",
symbolLayers: [{
type: "fill",
material: { color: [244, 247, 134] }
}]
}
};import IntegratedMeshLayer from "@arcgis/core/layers/IntegratedMeshLayer.js";
const meshLayer = new IntegratedMeshLayer({
url: "https://tiles.arcgis.com/tiles/.../IntegratedMeshServer"
});import PointCloudLayer from "@arcgis/core/layers/PointCloudLayer.js";
const pcLayer = new PointCloudLayer({
url: "https://tiles.arcgis.com/tiles/.../SceneServer"
});For WMS, WFS, KML, MapImageLayer, and CatalogLayer, see arcgis-advanced-layers.
import StreamLayer from "@arcgis/core/layers/StreamLayer.js";
const streamLayer = new StreamLayer({
url: "wss://example.com/stream/service",
purgeOptions: {
displayCount: 10000
}
});import ParquetLayer from "@arcgis/core/layers/ParquetLayer.js";
const parquetLayer = new ParquetLayer({
urls: [
"https://example.com/data.parquet"
],
title: "Parquet Data",
renderer: {
type: "simple",
symbol: { type: "simple-marker", color: "blue" }
},
popupTemplate: {
title: "{name}",
content: [{
type: "fields",
fieldInfos: [
{ fieldName: "field1", label: "Field 1" }
]
}]
}
});
// Query feature count
const count = await parquetLayer.queryFeatureCount();import GeoRSSLayer from "@arcgis/core/layers/GeoRSSLayer.js";
const georssLayer = new GeoRSSLayer({
url: "https://example.com/feed.xml"
});
map.add(georssLayer);
// Zoom to layer extent
await view.whenLayerView(georssLayer);
view.goTo(georssLayer.fullExtent);import WebTileLayer from "@arcgis/core/layers/WebTileLayer.js";
// OpenStreetMap tiles
const osmLayer = new WebTileLayer({
urlTemplate: "https://{subDomain}.tile.openstreetmap.org/{z}/{x}/{y}.png",
subDomains: ["a", "b", "c"],
copyright: "OpenStreetMap contributors"
});
// Custom tile service
const customTileLayer = new WebTileLayer({
urlTemplate: "https://tiles.example.com/{level}/{col}/{row}.png"
});
map.add(osmLayer);import MapNotesLayer from "@arcgis/core/layers/MapNotesLayer.js";
// Layer for sketches and annotations
const notesLayer = new MapNotesLayer();
map.add(notesLayer);
// Access sublayers
const pointLayer = notesLayer.pointLayer;
const polylineLayer = notesLayer.polylineLayer;
const polygonLayer = notesLayer.polygonLayer;
const textLayer = notesLayer.textLayer;
// Add a text annotation
const textGraphic = new Graphic({
geometry: point,
symbol: {
type: "text",
text: "Note here",
color: [255, 255, 255],
haloColor: [0, 0, 0],
haloSize: 2,
font: { family: "Arial", size: 14 }
}
});
textLayer.add(textGraphic);// Add single layer
map.add(layer);
// Add at specific index (0 = bottom)
map.add(layer, 0);
// Add multiple layers
map.addMany([layer1, layer2, layer3]);
// Add to Map Component
const viewElement = document.querySelector("arcgis-map");
viewElement.map.add(layer);map.remove(layer);
map.removeMany([layer1, layer2]);
map.removeAll();// Reorder layer
map.reorder(layer, newIndex);
// Get layer position
const index = map.layers.indexOf(layer);
// Find layer by id
const layer = map.findLayerById("myLayerId");
// Find layer by title
const layer = map.layers.find(l => l.title === "Cities");layer.visible = false;
layer.opacity = 0.5; // 0-1
// Watch visibility changes
import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js";
reactiveUtils.watch(
() => layer.visible,
(newValue) => console.log("Visibility changed:", newValue)
);// Create query from layer settings
const query = featureLayer.createQuery();
query.where = "population > 100000";
query.outFields = ["name", "population"];
query.returnGeometry = true;
const result = await featureLayer.queryFeatures(query);
console.log(result.features); // Array of Graphic objectsconst query = featureLayer.createQuery();
query.start = 0; // Start index
query.num = 20; // Number of features
query.orderByFields = ["population DESC"];
const result = await featureLayer.queryFeatures(query);const query = featureLayer.createQuery();
query.geometry = view.extent; // Or any geometry
query.spatialRelationship = "intersects"; // contains, crosses, etc.
query.returnGeometry = true;
const result = await featureLayer.queryFeatures(query);const count = await featureLayer.queryFeatureCount();
console.log("Total features:", count);const result = await featureLayer.queryExtent();
console.log("Feature count:", result.count);
await view.goTo(result.extent);const query = featureLayer.createQuery();
query.outStatistics = [{
statisticType: "sum",
onStatisticField: "population",
outStatisticFieldName: "totalPop"
}, {
statisticType: "avg",
onStatisticField: "population",
outStatisticFieldName: "avgPop"
}];
const result = await featureLayer.queryFeatures(query);
console.log(result.features[0].attributes.totalPop);const layerView = await view.whenLayerView(featureLayer);
// Query only currently visible features
const query = layerView.createQuery();
query.geometry = view.extent;
const result = await layerView.queryFeatures(query);// Wait for layer to load
await featureLayer.load();
console.log("Fields:", featureLayer.fields);
console.log("Extent:", featureLayer.fullExtent);
// Wait for layer view
const layerView = await view.whenLayerView(featureLayer);
// Check if updating
reactiveUtils.watch(
() => layerView.updating,
(updating) => {
if (!updating) {
console.log("Layer view finished updating");
}
}
);// SQL WHERE clause filter
featureLayer.definitionExpression = "state = 'California'";
// Clear filter
featureLayer.definitionExpression = null;
// Dynamic filter
function filterByYear(year) {
featureLayer.definitionExpression = `year = ${year}`;
}Layer configurations with type properties support autocasting. For TypeScript safety, use as const:
// Use 'as const' for type safety
const featureLayer = new FeatureLayer({
source: graphics,
fields: [
{ name: "ObjectID", type: "oid" },
{ name: "name", type: "string" }
],
geometryType: "point",
renderer: {
type: "simple",
symbol: { type: "simple-marker", color: "blue" }
} as const
});For complex layer configurations, use type annotations:
const renderer: __esri.SimpleRendererProperties = {
type: "simple",
symbol: { type: "simple-marker", color: "blue" }
};
layer.renderer = renderer;Tip: See arcgis-core-maps skill for detailed guidance on autocasting vs explicit classes.
intro-layers - Introduction to working with layerslayers-featurelayer - FeatureLayer configuration and usagelayers-geojson - Loading GeoJSON data as a layerfeaturelayer-query - Querying features from a FeatureLayerget-started-graphics - Working with Graphics and GraphicsLayerCORS errors with GeoJSON: GeoJSON URLs must be CORS-enabled or use a proxy
Missing outFields: Queries return no attribute values unless outFields is specified.
// Anti-pattern: querying without outFields
const results = await featureLayer.queryFeatures({
where: "population > 100000",
returnGeometry: true
});
console.log(results.features[0].attributes.name); // undefined// Correct: specify outFields to include desired attributes
const results = await featureLayer.queryFeatures({
where: "population > 100000",
returnGeometry: true,
outFields: ["name", "population", "state"]
});
console.log(results.features[0].attributes.name); // "Los Angeles"Impact: Feature attributes come back as undefined or null. The query succeeds but the response contains no usable attribute data, leading to silent data bugs.
Not waiting for layer load: Layer metadata (fields, extent, renderer) is unavailable until the layer finishes loading.
// Anti-pattern: accessing properties before load
const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" });
console.log(layer.fields); // null - layer has not loaded yet
console.log(layer.fullExtent); // null// Correct: await layer.load() first
const layer = new FeatureLayer({ url: "https://services.arcgis.com/.../FeatureServer/0" });
await layer.load();
console.log(layer.fields); // Array of Field objects
console.log(layer.fullExtent); // Extent objectImpact: fields is null or an empty array, fullExtent is null, and any code that depends on layer metadata throws errors or silently fails.
Wrong layer for MapView vs SceneView: Some layers only work in 3D (SceneLayer, IntegratedMeshLayer)
Spatial reference mismatch: Adding layers with a different spatial reference than the view without projection support.
// Anti-pattern: adding a layer in a different SR without handling projection
const view = new MapView({
spatialReference: { wkid: 4326 } // WGS84
});
const layer = new FeatureLayer({
url: "https://services.arcgis.com/.../FeatureServer/0" // Data in Web Mercator (3857)
});
view.map.add(layer); // Features may render in wrong location// Correct: match spatial references or load the projection engine
import * as projection from "@arcgis/core/geometry/projection";
await projection.load();
const view = new MapView({
spatialReference: { wkid: 4326 }
});
const layer = new FeatureLayer({
url: "https://services.arcgis.com/.../FeatureServer/0"
});
view.map.add(layer); // Projection engine handles the transformationImpact: Features appear in the wrong location, are offset by thousands of kilometers, or do not display at all when spatial references are incompatible.
d84407b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.