arcgis-advanced-layers

SKILL.md

ArcGIS Advanced Layers

Use this skill for working with OGC services, MapImageLayer, CatalogLayer, MediaLayer, and dynamic data layers.

WMSLayer (Web Map Service)

Basic WMSLayer

import WMSLayer from "@arcgis/core/layers/WMSLayer.js";

const layer = new WMSLayer({
  url: "https://ows.terrestris.de/osm/service"
});

await layer.load();

// Find and use a specific sublayer
const sublayer = layer.findSublayerByName("OSM-WMS");
if (sublayer) {
  layer.sublayers = [sublayer];
}

map.add(layer);

WMSLayer as Basemap (Map Component)

<arcgis-scene>
  <arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-scene>

<script type="module">
  import WMSLayer from "@arcgis/core/layers/WMSLayer.js";
  const viewElement = document.querySelector("arcgis-scene");

  const layer = new WMSLayer({
    url: "https://ows.terrestris.de/osm/service"
  });

  await layer.load();
  const sublayer = layer.findSublayerByName("OSM-WMS");
  if (sublayer) {
    layer.sublayers = [sublayer];
  }

  viewElement.map = {
    basemap: {
      baseLayers: [layer],
      title: "WMS Layer"
    }
  };
</script>

WFSLayer (Web Feature Service)

Basic WFSLayer

import WFSLayer from "@arcgis/core/layers/WFSLayer.js";

const layer = new WFSLayer({
  url: "https://geobretagne.fr/geoserver/ows",
  name: "fma:bvme_zhp_vs_culture",
  copyright: "GéoBretagne"
});

map.add(layer);

WFS Capabilities

import WFSLayer from "@arcgis/core/layers/WFSLayer.js";
import wfsUtils from "@arcgis/core/layers/ogc/wfsUtils.js";

// Get capabilities from WFS endpoint
const capabilities = await wfsUtils.getCapabilities("https://geobretagne.fr/geoserver/ows");

// List available feature types
capabilities.featureTypes.forEach(featureType => {
  console.log(featureType.title, featureType.name);
});

// Create layer from specific feature type
const layerInfo = await wfsUtils.getWFSLayerInfo(capabilities, "featureTypeName");
const layer = WFSLayer.fromWFSLayerInfo(layerInfo);
map.add(layer);

WMTSLayer (Web Map Tile Service)

Basic WMTSLayer

import WMTSLayer from "@arcgis/core/layers/WMTSLayer.js";

const layer = new WMTSLayer({
  url: "https://www.ign.es/wmts/ign-base",
  activeLayer: {
    id: "IGNBase-gris",
    tileMatrixSetId: "GoogleMapsCompatible"
  },
  serviceMode: "KVP",
  copyright: "Instituto Geográfico Nacional"
});

map.add(layer);

WMTSLayer as Basemap

import Basemap from "@arcgis/core/Basemap.js";
import WMTSLayer from "@arcgis/core/layers/WMTSLayer.js";

const wmtsBasemap = new Basemap({
  baseLayers: [
    new WMTSLayer({
      url: "https://www.ign.es/wmts/ign-base",
      activeLayer: { id: "IGNBase-gris", tileMatrixSetId: "GoogleMapsCompatible" },
      serviceMode: "KVP"
    })
  ],
  thumbnailUrl: "https://example.com/thumbnail.jpg"
});

const map = new Map({
  basemap: wmtsBasemap
});

OGCFeatureLayer

Basic OGCFeatureLayer

import OGCFeatureLayer from "@arcgis/core/layers/OGCFeatureLayer.js";

const layer = new OGCFeatureLayer({
  url: "https://demo.ldproxy.net/vineyards",  // OGC API landing page
  collectionId: "vineyards",                   // Collection ID
  minScale: 5000000,
  renderer: {
    type: "simple",
    symbol: {
      type: "simple-fill",
      color: [76, 129, 64, 0.6]
    }
  },
  popupTemplate: {
    title: "{name}",
    content: "Area: {area_ha} hectares"
  }
});

map.add(layer);

OGCFeatureLayer with Labeling

const layer = new OGCFeatureLayer({
  url: "https://demo.ldproxy.net/vineyards",
  collectionId: "vineyards",
  labelingInfo: [{
    labelExpressionInfo: {
      expression: "$feature.NAME"
    },
    symbol: {
      type: "text",
      color: "#4a6741",
      haloSize: 1,
      haloColor: "white",
      font: {
        family: "Arial",
        style: "italic"
      }
    },
    minScale: 100000
  }]
});

MapImageLayer

Basic MapImageLayer

import MapImageLayer from "@arcgis/core/layers/MapImageLayer.js";

// From portal item
const layer = new MapImageLayer({
  portalItem: {
    id: "d7892b3c13b44391992ecd42bfa92d01"
  }
});

// From URL
const layer2 = new MapImageLayer({
  url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer"
});

map.add(layer);

MapImageLayer with Sublayers

const layer = new MapImageLayer({
  url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer",
  sublayers: [
    { id: 2, visible: true },   // States
    { id: 1, visible: true },   // Highways
    { id: 0, visible: true }    // Cities
  ]
});

// Toggle sublayer visibility
layer.when(() => {
  const sublayer = layer.findSublayerById(1);
  sublayer.visible = !sublayer.visible;
});

MapImageLayer with Definition Expression

const layer = new MapImageLayer({
  url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer",
  sublayers: [{
    id: 0,
    definitionExpression: "pop2000 > 100000"
  }]
});

MapImageLayer with Custom Renderer

const layer = new MapImageLayer({
  url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer",
  sublayers: [{
    id: 2,
    renderer: {
      type: "simple",
      symbol: {
        type: "simple-fill",
        color: [0, 100, 200, 0.5],
        outline: { color: "white", width: 1 }
      }
    }
  }]
});

Dynamic Data Layers

Data Layer from Table

const layer = new MapImageLayer({
  url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer",
  sublayers: [{
    id: 4,
    title: "Railroads",
    renderer: {
      type: "simple",
      symbol: {
        type: "simple-line",
        color: [255, 255, 255, 0.5],
        width: 0.75,
        style: "long-dash-dot-dot"
      }
    },
    source: {
      type: "data-layer",
      dataSource: {
        type: "table",
        workspaceId: "MyDatabaseWorkspaceIDSSR2",
        dataSourceName: "ss6.gdb.Railroads"
      }
    }
  }]
});

Data Layer with Table Join

const layer = new MapImageLayer({
  url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer",
  sublayers: [{
    id: 0,
    opacity: 0.75,
    source: {
      type: "data-layer",
      dataSource: {
        type: "join-table",
        // Left table: map layer with geometries
        leftTableSource: {
          type: "map-layer",
          mapLayerId: 3
        },
        // Right table: data table in workspace
        rightTableSource: {
          type: "data-layer",
          dataSource: {
            type: "table",
            workspaceId: "CensusFileGDBWorkspaceID",
            dataSourceName: "ancestry"
          }
        },
        leftTableKey: "STATE_NAME",
        rightTableKey: "State",
        joinType: "left-outer-join"
      }
    },
    renderer: {
      type: "class-breaks",
      field: "ancestry.Norwegian",
      normalizationField: "states.POP2007",
      classBreakInfos: [
        { minValue: 0, maxValue: 0.01, symbol: createSymbol("#f8e3c2") },
        { minValue: 0.01, maxValue: 0.05, symbol: createSymbol("#d86868") }
      ]
    }
  }]
});

CatalogLayer

Basic CatalogLayer

import CatalogLayer from "@arcgis/core/layers/CatalogLayer.js";

const layer = new CatalogLayer({
  portalItem: {
    id: "487cc66d305145d3b67fed383456af48",
    portal: {
      url: "https://jsapi.maps.arcgis.com/"
    }
  }
});

map.add(layer);

Working with CatalogLayer Footprints

import CatalogLayer from "@arcgis/core/layers/CatalogLayer.js";
import catalogUtils from "@arcgis/core/layers/catalog/catalogUtils.js";

const layer = new CatalogLayer({
  portalItem: { id: "YOUR_CATALOG_ITEM_ID" }
});

map.add(layer);

const layerView = await view.whenLayerView(layer);

// Query all footprints
const result = await layer.queryFeatures({
  where: "1=1",
  returnGeometry: true,
  outFields: ["*"],
  orderByFields: [layer.itemNameField]
});

// Add labels to footprint layer
layer.footprintLayer.labelingInfo = [{
  labelExpressionInfo: {
    expression: "$feature." + layer.itemNameField
  },
  symbol: {
    type: "label-3d",
    symbolLayers: [{
      type: "text",
      material: { color: "white" },
      size: 10
    }]
  }
}];

// Highlight a footprint
const highlight = layerView.footprintLayerView.highlight(feature);

// Create layer from footprint
const footprint = layer.createFootprintFromLayer(selectedLayer);
const newLayer = await layer.createLayerFromFootprint(footprint);
map.add(newLayer);

CatalogLayer with LayerList

const layerList = document.querySelector("arcgis-layer-list");

layerList.catalogOptions = {
  selectionMode: "single"
};

layerList.listItemCreatedFunction = (event) => {
  if (catalogUtils.isLayerFromCatalog(event.item.layer)) {
    event.item.actionsSections = [[{
      title: "Add layer to map",
      icon: "add-layer",
      id: "add-layer"
    }]];
  }
};

MediaLayer

MediaLayer Basics

Create MediaLayer with Images

import MediaLayer from "@arcgis/core/layers/MediaLayer.js";
import ImageElement from "@arcgis/core/layers/support/ImageElement.js";
import ExtentAndRotationGeoreference from "@arcgis/core/layers/support/ExtentAndRotationGeoreference.js";
import Extent from "@arcgis/core/geometry/Extent.js";

const imageElement = new ImageElement({
  image: "https://example.com/historical-map.png",
  georeference: new ExtentAndRotationGeoreference({
    extent: new Extent({
      xmin: -10047456,
      ymin: 3486722,
      xmax: -10006982,
      ymax: 3514468,
      spatialReference: { wkid: 102100 }
    })
  })
});

const mediaLayer = new MediaLayer({
  source: [imageElement],
  title: "Historical Map"
});

map.add(mediaLayer);

Multiple Images

const imageInfos = [
  {
    url: "image1.png",
    extent: { xmin: -100, ymin: 30, xmax: -90, ymax: 40 }
  },
  {
    url: "image2.png",
    extent: { xmin: -95, ymin: 35, xmax: -85, ymax: 45 }
  }
];

const imageElements = imageInfos.map(info => new ImageElement({
  image: info.url,
  georeference: new ExtentAndRotationGeoreference({
    extent: new Extent({
      ...info.extent,
      spatialReference: { wkid: 4326 }
    })
  })
}));

const mediaLayer = new MediaLayer({
  source: imageElements
});

Georeferencing Methods

Extent and Rotation

const georeference = new ExtentAndRotationGeoreference({
  extent: new Extent({
    xmin: -122.5,
    ymin: 37.5,
    xmax: -122.0,
    ymax: 38.0,
    spatialReference: { wkid: 4326 }
  }),
  rotation: 15 // Degrees clockwise
});

Control Points (Corners)

import ControlPointsGeoreference from "@arcgis/core/layers/support/ControlPointsGeoreference.js";

const georeference = new ControlPointsGeoreference({
  controlPoints: [
    {
      sourcePoint: { x: 0, y: 0 },           // Top-left of image (pixels)
      mapPoint: { x: -122.5, y: 38.0 }       // Map coordinates
    },
    {
      sourcePoint: { x: 1000, y: 0 },        // Top-right
      mapPoint: { x: -122.0, y: 38.0 }
    },
    {
      sourcePoint: { x: 1000, y: 800 },      // Bottom-right
      mapPoint: { x: -122.0, y: 37.5 }
    },
    {
      sourcePoint: { x: 0, y: 800 },         // Bottom-left
      mapPoint: { x: -122.5, y: 37.5 }
    }
  ],
  width: 1000,  // Image width in pixels
  height: 800   // Image height in pixels
});

Video Elements

import VideoElement from "@arcgis/core/layers/support/VideoElement.js";

const videoElement = new VideoElement({
  video: "https://example.com/timelapse.mp4",
  georeference: new ExtentAndRotationGeoreference({
    extent: new Extent({
      xmin: -122.5,
      ymin: 37.5,
      xmax: -122.0,
      ymax: 38.0,
      spatialReference: { wkid: 4326 }
    })
  })
});

const mediaLayer = new MediaLayer({
  source: [videoElement]
});

// Control video playback
videoElement.content.play();
videoElement.content.pause();
videoElement.content.currentTime = 30; // Seek to 30 seconds

Animated GIF

// Animated GIFs work like regular images
const gifElement = new ImageElement({
  image: "https://example.com/weather-animation.gif",
  georeference: new ExtentAndRotationGeoreference({
    extent: new Extent({
      xmin: -130,
      ymin: 25,
      xmax: -65,
      ymax: 50,
      spatialReference: { wkid: 4326 }
    })
  })
});

const mediaLayer = new MediaLayer({
  source: [gifElement]
});

Layer Configuration

Opacity and Blend Mode

const mediaLayer = new MediaLayer({
  source: [imageElement],
  opacity: 0.7,
  blendMode: "multiply" // normal, multiply, luminosity, etc.
});

// Change opacity dynamically
mediaLayer.opacity = 0.5;

// Change blend mode
mediaLayer.blendMode = "luminosity";

Element Opacity

// Individual element opacity
imageElement.opacity = 0.8;

// Update dynamically
document.getElementById("opacitySlider").addEventListener("input", (e) => {
  imageElement.opacity = e.target.value / 100;
});

Managing Source Elements

// Access source
const source = mediaLayer.source;

// Add elements
source.elements.push(newImageElement);
source.elements.push(element1, element2);

// Remove elements
source.elements.splice(source.elements.indexOf(imageElement), 1);
source.elements.length = 0; // Remove all

// Iterate elements
source.elements.forEach(element => {
  console.log(element.image || element.video);
});

Interactive Control Points

// Enable interactive editing of georeference control points
const mediaLayerView = await view.whenLayerView(mediaLayer);

// Enable interactive mode to allow control point editing
mediaLayerView.interactive = true;

// Disable interactive mode
mediaLayerView.interactive = false;

Complete Example

<arcgis-map center="-89.93, 29.97" zoom="10">
  <arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>

<script type="module">
  import MediaLayer from "@arcgis/core/layers/MediaLayer.js";
  import ImageElement from "@arcgis/core/layers/support/ImageElement.js";
  import ExtentAndRotationGeoreference from "@arcgis/core/layers/support/ExtentAndRotationGeoreference.js";
  import Extent from "@arcgis/core/geometry/Extent.js";
  import Map from "@arcgis/core/Map.js";

  const viewElement = document.querySelector("arcgis-map");

  // Create image elements for historical maps
  const imageElements = [
    {
      name: "1891 Map",
      url: "https://example.com/map-1891.png",
      extent: { xmin: -10047456, ymin: 3486722, xmax: -10006982, ymax: 3514468 }
    },
    {
      name: "1920 Map",
      url: "https://example.com/map-1920.png",
      extent: { xmin: -10045000, ymin: 3488000, xmax: -10008000, ymax: 3516000 }
    }
  ].map(info => new ImageElement({
    image: info.url,
    georeference: new ExtentAndRotationGeoreference({
      extent: new Extent({
        ...info.extent,
        spatialReference: { wkid: 102100 }
      })
    })
  }));

  const mediaLayer = new MediaLayer({
    source: imageElements,
    title: "Historical Maps",
    blendMode: "normal"
  });

  viewElement.map = new Map({
    basemap: "topo-vector",
    layers: [mediaLayer]
  });
</script>

Layer Comparison

Layer Type Use Case Data Source
WMSLayer Raster imagery from OGC WMS WMS 1.1.1/1.3.0
WFSLayer Vector features from OGC WFS WFS 2.0.0
WMTSLayer Cached tiles from OGC WMTS WMTS 1.0.0
OGCFeatureLayer Vector from OGC API - Features OGC API
MapImageLayer Server-rendered imagery ArcGIS Map Service
CatalogLayer Collection of layers ArcGIS Catalog Service
MediaLayer Georeferenced images, video, GIFs Local/remote media

Reference Samples

  • layers-wms - Adding and configuring WMS layers
  • layers-wfs - Working with WFS layers
  • layers-ogcfeaturelayer - OGC Features API layer usage
  • layers-mapimagelayer - Dynamic MapImageLayer configuration
  • layers-cataloglayer - Using CatalogLayer to browse portal content
  • layers-medialayer-images - Displaying images with MediaLayer
  • layers-medialayer-video - Video playback in MediaLayer
  • layers-medialayer-control-points - Control point placement for media
  • layers-medialayer-interactive - Interactive media layer manipulation

Common Pitfalls

  1. WFS version: WFSLayer requires WFS 2.0.0 with GeoJSON output format

  2. CORS: OGC services need CORS headers or proxy configuration

  3. Sublayer IDs: MapImageLayer sublayer IDs must match service layer IDs

  4. Dynamic data sources: Require registered workspaces on the server

  5. CatalogLayer portal: Must specify portal URL for non-ArcGIS Online items

  6. Field prefixes: In joined tables, prefix field names with table name (e.g., ancestry.Norwegian)

  7. Media CORS: Images and videos from external servers need CORS headers

  8. Video autoplay: Browsers may block autoplay - require user interaction first

Weekly Installs
16
GitHub Stars
5
First Seen
Jan 22, 2026
Installed on
opencode13
codex12
gemini-cli12
cursor12
github-copilot11
claude-code11