Claude Code Plugins

Community-maintained marketplace

Feedback

Interactive map tools including swipe comparison, measurement, identify, and routing. Use for layer comparison, distance/area measurement, and navigation services.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name arcgis-map-tools
description Interactive map tools including swipe comparison, measurement, identify, and routing. Use for layer comparison, distance/area measurement, and navigation services.

ArcGIS Map Tools

Use this skill for swipe comparison, measurement tools, identify operations, and routing services.

Swipe Widget

Swipe Component

<arcgis-map zoom="15" center="-154.88, 19.46">
  <arcgis-zoom slot="top-left"></arcgis-zoom>
  <arcgis-swipe swipe-position="32"></arcgis-swipe>
</arcgis-map>

<script type="module">
  import Map from "@arcgis/core/Map.js";
  import TileLayer from "@arcgis/core/layers/TileLayer.js";

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

  const layer1 = new TileLayer({ url: "..." });
  const layer2 = new TileLayer({ url: "..." });

  viewElement.map = new Map({
    basemap: "satellite",
    layers: [layer1, layer2]
  });

  // Configure swipe when ready
  arcgisSwipe.addEventListener("arcgisPropertyChange", (e) => {
    if (e.detail.name === "state" && arcgisSwipe.state === "ready") {
      arcgisSwipe.leadingLayers.add(layer1);
      arcgisSwipe.trailingLayers.add(layer2);
    }
  });
</script>

Swipe Widget (Core API) - Deprecated

DEPRECATED since 4.32: Use the arcgis-swipe component shown above instead. For information on widget deprecation, see Esri's move to web components.

// DEPRECATED - Use arcgis-swipe component instead
import Swipe from "@arcgis/core/widgets/Swipe.js";

const swipe = new Swipe({
  view: view,
  leadingLayers: [layer1],
  trailingLayers: [layer2],
  position: 50,  // Percentage from left
  direction: "horizontal"  // or "vertical"
});

view.ui.add(swipe);

Measurement

Measurement Components

<!-- 2D Measurement -->
<arcgis-map>
  <arcgis-distance-measurement-2d slot="top-right"></arcgis-distance-measurement-2d>
  <arcgis-area-measurement-2d slot="top-right"></arcgis-area-measurement-2d>
</arcgis-map>

<!-- 3D Measurement -->
<arcgis-scene>
  <arcgis-direct-line-measurement-3d slot="top-right"></arcgis-direct-line-measurement-3d>
  <arcgis-area-measurement-3d slot="top-right"></arcgis-area-measurement-3d>
</arcgis-scene>

Measurement Widget (Core API)

import Measurement from "@arcgis/core/widgets/Measurement.js";

const measurement = new Measurement({
  view: view
});

view.ui.add(measurement, "bottom-right");

// Activate distance measurement
// For 2D: "distance", For 3D: "direct-line"
measurement.activeTool = view.type === "2d" ? "distance" : "direct-line";

// Activate area measurement
measurement.activeTool = "area";

// Clear measurements
measurement.clear();

2D Measurement Widgets (Core API)

import DistanceMeasurement2D from "@arcgis/core/widgets/DistanceMeasurement2D.js";
import AreaMeasurement2D from "@arcgis/core/widgets/AreaMeasurement2D.js";

// Distance measurement
const distanceWidget = new DistanceMeasurement2D({
  view: view,
  unit: "kilometers"
});
view.ui.add(distanceWidget, "top-right");

// Area measurement
const areaWidget = new AreaMeasurement2D({
  view: view,
  unit: "square-kilometers"
});
view.ui.add(areaWidget, "top-right");

3D Measurement Widgets (Core API)

import DirectLineMeasurement3D from "@arcgis/core/widgets/DirectLineMeasurement3D.js";
import AreaMeasurement3D from "@arcgis/core/widgets/AreaMeasurement3D.js";

// Direct line measurement (3D)
const lineWidget = new DirectLineMeasurement3D({
  view: sceneView,
  unit: "meters"
});

// Area measurement (3D)
const areaWidget = new AreaMeasurement3D({
  view: sceneView,
  unit: "square-meters"
});

Identify

Identify on MapImageLayer

import identify from "@arcgis/core/rest/identify.js";
import IdentifyParameters from "@arcgis/core/rest/support/IdentifyParameters.js";

const identifyURL = "https://sampleserver6.arcgisonline.com/arcgis/rest/services/MtBaldy_BaseMap/MapServer";

// Set up parameters
const params = new IdentifyParameters({
  tolerance: 3,
  layerIds: [0, 1, 2, 3, 4],
  layerOption: "top",  // "top", "visible", "all"
  width: view.width,
  height: view.height
});

// Execute on click
view.on("click", async (event) => {
  params.geometry = event.mapPoint;
  params.mapExtent = view.extent;

  const response = await identify.identify(identifyURL, params);

  const features = response.results.map(result => {
    const feature = result.feature;
    feature.attributes.layerName = result.layerName;

    // Set popup template based on layer
    if (result.layerName === "Roads") {
      feature.popupTemplate = {
        title: "Road",
        content: "<b>Length:</b> {Shape_Length}"
      };
    }
    return feature;
  });

  if (features.length > 0) {
    view.openPopup({
      features: features,
      location: event.mapPoint
    });
  }
});

Routing

Basic Route

import route from "@arcgis/core/rest/route.js";
import RouteParameters from "@arcgis/core/rest/support/RouteParameters.js";
import FeatureSet from "@arcgis/core/rest/support/FeatureSet.js";
import Graphic from "@arcgis/core/Graphic.js";
import GraphicsLayer from "@arcgis/core/layers/GraphicsLayer.js";

const routeUrl = "https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World";

const routeLayer = new GraphicsLayer();
map.add(routeLayer);

const routeParams = new RouteParameters({
  apiKey: "YOUR_API_KEY",
  stops: new FeatureSet(),
  outSpatialReference: { wkid: 3857 }
});

// Add stop on click
view.on("click", async (event) => {
  const stop = new Graphic({
    geometry: event.mapPoint,
    symbol: {
      type: "simple-marker",
      style: "cross",
      size: 15
    }
  });
  routeLayer.add(stop);
  routeParams.stops.features.push(stop);

  // Solve route when 2+ stops
  if (routeParams.stops.features.length >= 2) {
    const result = await route.solve(routeUrl, routeParams);
    const routeResult = result.routeResults[0].route;
    routeResult.symbol = {
      type: "simple-line",
      color: [0, 0, 255, 0.5],
      width: 5
    };
    routeLayer.add(routeResult);
  }
});

Route with Options

const routeParams = new RouteParameters({
  apiKey: "YOUR_API_KEY",
  stops: new FeatureSet({ features: stopGraphics }),
  returnDirections: true,
  directionsLanguage: "en",
  returnRoutes: true,
  returnStops: true,
  impedanceAttribute: "TravelTime",  // or "Miles", "Kilometers"
  restrictionAttributes: ["Avoid Toll Roads"],
  outSpatialReference: { wkid: 4326 }
});

const result = await route.solve(routeUrl, routeParams);

// Get directions
const directions = result.routeResults[0].directions;
directions.features.forEach(feature => {
  console.log(feature.attributes.text);
});

Directions Widget

Directions Component

<arcgis-map>
  <arcgis-directions slot="top-right"></arcgis-directions>
</arcgis-map>

Directions Widget (Core API)

import Directions from "@arcgis/core/widgets/Directions.js";

const directions = new Directions({
  view: view,
  apiKey: "YOUR_API_KEY"
});

view.ui.add(directions, "top-right");

Print

Print Component

<arcgis-map item-id="YOUR_WEBMAP_ID">
  <arcgis-expand slot="top-right" expand-tooltip="Print">
    <arcgis-print></arcgis-print>
  </arcgis-expand>
</arcgis-map>

Print Service

import print from "@arcgis/core/rest/print.js";
import PrintTemplate from "@arcgis/core/rest/support/PrintTemplate.js";
import PrintParameters from "@arcgis/core/rest/support/PrintParameters.js";

const printUrl = "https://utility.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task";

const template = new PrintTemplate({
  format: "pdf",
  layout: "a4-landscape",
  layoutOptions: {
    titleText: "My Map",
    authorText: "Author Name"
  }
});

const params = new PrintParameters({
  view: view,
  template: template
});

const result = await print.execute(printUrl, params);
console.log("PDF URL:", result.url);

PrintTemplate Configuration

import PrintTemplate from "@arcgis/core/rest/support/PrintTemplate.js";

const template = new PrintTemplate({
  // Output format
  format: "pdf",  // pdf, png32, png8, jpg, gif, eps, svg, svgz

  // Layout template name (from print service)
  layout: "a4-landscape",  // Or custom layout name

  // Export options for map-only output
  exportOptions: {
    width: 800,
    height: 600,
    dpi: 300
  },

  // Layout customization
  layoutOptions: {
    titleText: "Map Title",
    authorText: "Created by",
    copyrightText: "Copyright 2024",
    scalebarUnit: "Kilometers",  // Kilometers, Miles
    legendLayers: [],  // Layers to include in legend
    customTextElements: [
      { customText: "Custom field value" }
    ]
  },

  // Preserve scale or extent
  outScale: 50000,  // Fixed scale
  scalePreserved: true,  // Maintain current scale

  // Include attribution
  attributionVisible: true
});

Print Formats

Format Description
pdf Adobe PDF
png32 PNG with transparency (32-bit)
png8 PNG 8-bit
jpg JPEG
gif GIF
eps Encapsulated PostScript
svg Scalable Vector Graphics
svgz Compressed SVG

Print Layouts

// Get available layouts from print service
const printInfo = await print.getInfo(printUrl);

console.log("Available layouts:");
printInfo.layouts.forEach(layout => {
  console.log(`- ${layout.name}`);
});

console.log("Available formats:");
printInfo.formats.forEach(format => {
  console.log(`- ${format}`);
});

Map-Only Export (No Layout)

const template = new PrintTemplate({
  format: "png32",
  layout: "map-only",  // No layout template
  exportOptions: {
    width: 1920,
    height: 1080,
    dpi: 96
  }
});

High-Resolution Export

const template = new PrintTemplate({
  format: "pdf",
  layout: "a3-landscape",
  exportOptions: {
    dpi: 300  // High DPI for print quality
  },
  layoutOptions: {
    titleText: "High Resolution Map",
    scalebarUnit: "Miles"
  },
  scalePreserved: true
});

Print with Custom Extent

const template = new PrintTemplate({
  format: "pdf",
  layout: "letter-ansi-a-landscape",
  outScale: 24000  // 1:24000 scale
});

const params = new PrintParameters({
  view: view,
  template: template,
  extraParameters: {
    // Custom extent (optional)
    extent: {
      xmin: -117.2,
      ymin: 34.0,
      xmax: -117.1,
      ymax: 34.1,
      spatialReference: { wkid: 4326 }
    }
  }
});

Print Widget (Core API)

import Print from "@arcgis/core/widgets/Print.js";

const print = new Print({
  view: view,
  printServiceUrl: "https://utility.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task"
});

view.ui.add(print, "top-right");

Print Widget Configuration (Core API)

import Print from "@arcgis/core/widgets/Print.js";

const print = new Print({
  view: view,
  printServiceUrl: printUrl,

  // Allowed formats
  allowedFormats: ["pdf", "png32", "jpg"],

  // Allowed layouts
  allowedLayouts: ["a4-landscape", "a4-portrait", "letter-ansi-a-landscape"],

  // Default template settings
  templateOptions: {
    title: "Default Map Title",
    author: "Default Author",
    copyright: "Copyright 2024",
    scaleEnabled: true,
    attributionEnabled: true
  },

  // Include legend
  includeDefaultTemplates: true
});

Print Widget Events

// Listen for print completion
print.viewModel.on("complete", (event) => {
  console.log("Print complete:", event.url);
  window.open(event.url, "_blank");
});

// Listen for errors
print.viewModel.on("error", (event) => {
  console.error("Print error:", event.error);
});

// Track progress
print.viewModel.watch("state", (state) => {
  console.log("Print state:", state);  // ready, printing, complete, error
});

Custom Print Workflow

async function printMap(view, options = {}) {
  const {
    title = "Map Export",
    format = "pdf",
    layout = "a4-landscape",
    dpi = 150
  } = options;

  const template = new PrintTemplate({
    format,
    layout,
    exportOptions: { dpi },
    layoutOptions: {
      titleText: title,
      authorText: "Generated by Web App",
      copyrightText: `Exported: ${new Date().toLocaleDateString()}`
    }
  });

  const params = new PrintParameters({
    view,
    template
  });

  try {
    const result = await print.execute(printUrl, params);
    return result.url;
  } catch (error) {
    console.error("Print failed:", error);
    throw error;
  }
}

// Usage
const pdfUrl = await printMap(view, {
  title: "Project Area Map",
  format: "pdf",
  dpi: 300
});
window.open(pdfUrl, "_blank");

Print with Legend Customization

const template = new PrintTemplate({
  format: "pdf",
  layout: "a4-landscape",
  layoutOptions: {
    titleText: "Map with Custom Legend",
    // Specify which layers to include in legend
    legendLayers: [
      { layerId: featureLayer.id },
      { layerId: anotherLayer.id, sublayerIds: [0, 2] }  // Specific sublayers
    ]
  }
});

Screenshot as Alternative

For quick exports without a print service:

// Take screenshot of current view
const screenshot = await view.takeScreenshot({
  format: "png",
  width: 1920,
  height: 1080,
  quality: 90
});

// Use the data URL
const img = document.createElement("img");
img.src = screenshot.dataUrl;
document.body.appendChild(img);

// Or download
const link = document.createElement("a");
link.download = "map-screenshot.png";
link.href = screenshot.dataUrl;
link.click();

Find

Find Service

import find from "@arcgis/core/rest/find.js";
import FindParameters from "@arcgis/core/rest/support/FindParameters.js";

const findUrl = "https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer";

const params = new FindParameters({
  searchText: "California",
  layerIds: [0, 1, 2],
  searchFields: ["STATE_NAME", "NAME"],
  returnGeometry: true
});

const result = await find.find(findUrl, params);

result.results.forEach(r => {
  console.log(r.feature.attributes);
});

Complete Example

<!DOCTYPE html>
<html>
<head>
  <script type="module" src="https://js.arcgis.com/calcite-components/3.3.3/calcite.esm.js"></script>
  <script src="https://js.arcgis.com/4.34/"></script>
  <script type="module" src="https://js.arcgis.com/4.34/map-components/"></script>
  <style>
    html, body { height: 100%; margin: 0; }
    #toolbar { position: absolute; top: 10px; right: 10px; }
  </style>
</head>
<body>
  <arcgis-map basemap="streets-navigation-vector" center="-117.195, 34.057" zoom="13">
    <arcgis-zoom slot="top-left"></arcgis-zoom>
  </arcgis-map>
  <div id="toolbar">
    <calcite-button id="measureBtn">Measure</calcite-button>
    <calcite-button id="clearBtn">Clear</calcite-button>
  </div>

  <script type="module">
    import Measurement from "@arcgis/core/widgets/Measurement.js";

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

    const measurement = new Measurement({ view: viewElement });
    viewElement.ui.add(measurement, "bottom-right");

    document.getElementById("measureBtn").onclick = () => {
      measurement.activeTool = "distance";
    };

    document.getElementById("clearBtn").onclick = () => {
      measurement.clear();
    };
  </script>
</body>
</html>

Common Pitfalls

  1. API Key for routing: Route service requires valid API key or authenticated user

  2. Identify tolerance: Set appropriate tolerance based on zoom level

  3. Swipe layer order: Leading/trailing layers must be in the map's layers collection

  4. Measurement units: Set appropriate units for the measurement context

  5. 3D vs 2D measurement: Use different widgets/tools for 2D and 3D views