<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Measurement components (2D + 3D) | Sample | ArcGIS Maps SDK for JavaScript</title>
<!-- Load the ArcGIS Maps SDK for JavaScript from CDN -->
<script type="module" src="https://js.arcgis.com/5.0/"></script>
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 6px;
background: var(--calcite-color-foreground-1);
<arcgis-map id="map2d" center="26.1025, 44.4268" zoom="6" class="visible">
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-distance-measurement-2d id="distance2d" slot="bottom-right" hidden>
</arcgis-distance-measurement-2d>
<arcgis-area-measurement-2d id="area2d" slot="bottom-right" hidden>
</arcgis-area-measurement-2d>
<arcgis-scene id="scene3d" center="26.1025, 44.4268" scale="123456789" camera-tilt="45">
<arcgis-zoom slot="top-left"></arcgis-zoom>
<arcgis-navigation-toggle slot="top-left"></arcgis-navigation-toggle>
<arcgis-compass slot="top-left"></arcgis-compass>
<arcgis-direct-line-measurement-3d id="distance3d" slot="bottom-right" hidden>
</arcgis-direct-line-measurement-3d>
<arcgis-area-measurement-3d id="area3d" slot="bottom-right" hidden>
</arcgis-area-measurement-3d>
<calcite-button id="switchView" appearance="outline-fill" kind="neutral" alignment="start"
<calcite-action-bar id="toolbar" expand-disabled layout="horizontal">
<calcite-action id="distanceAction" text="Distance" icon="measure"></calcite-action>
<calcite-action id="areaAction" text="Area" icon="polygon"></calcite-action>
<calcite-action id="clearAction" text="Clear" icon="trash"></calcite-action>
const [Map, TileLayer, FeatureLayer] = await $arcgis.import([
"@arcgis/core/layers/TileLayer.js",
"@arcgis/core/layers/FeatureLayer.js",
const map2d = document.getElementById("map2d");
const scene3d = document.getElementById("scene3d");
const distance2d = document.getElementById("distance2d");
const area2d = document.getElementById("area2d");
const distance3d = document.getElementById("distance3d");
const area3d = document.getElementById("area3d");
const switchViewButton = document.getElementById("switchView");
const distanceAction = document.getElementById("distanceAction");
const areaAction = document.getElementById("areaAction");
const clearAction = document.getElementById("clearAction");
// World Ocean Base Basemap
const tileLayer = new TileLayer({
url: "https://services.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Base/MapServer",
// Capital cities in Europe FeatureLayer
const featureLayer = new FeatureLayer({
url: "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/europe_country_capitals/FeatureServer/0",
// Create a shared map used by both the 2D and 3D views.
const sharedMap = new Map({
layers: [tileLayer, featureLayer],
await map2d.viewOnReady();
await scene3d.viewOnReady();
// State tracked independently of the Measurement components to manage UI and interactions.
let activeDimension = "2d"; // "2d" | "3d"
let activeTool = null; // null | "distance" | "area"
function setActionStates() {
distanceAction.active = activeTool === "distance";
areaAction.active = activeTool === "area";
function hideAllToolUIs() {
distance2d.hidden = true;
distance3d.hidden = true;
function clearAllMeasurements() {
function startTool(toolName) {
const currentTools = toolElements[activeDimension];
const toolToShow = currentTools[toolName];
const toolToHide = currentTools[toolName === "distance" ? "area" : "distance"];
toolToHide.hidden = true;
toolToShow.hidden = false;
function showAndHide(elementToShow, elementToHide) {
elementToShow.classList.add("visible");
elementToHide.classList.remove("visible");
function getLatitudeFromViewpoint(viewpoint) {
const lat = viewpoint?.targetGeometry?.latitude;
return typeof lat === "number" ? lat : 0;
const switchingFrom2D = activeDimension === "2d";
const activeViewpoint = switchingFrom2D
? map2d.viewpoint.clone()
: scene3d.viewpoint.clone();
// Adjust for Web Mercator distortion in 2D maps (scale increases with latitude).
const latitude = getLatitudeFromViewpoint(activeViewpoint);
const scaleConversionFactor = Math.cos((latitude * Math.PI) / 180);
// Switching between 2D and 3D clears measurements
activeViewpoint.scale *= scaleConversionFactor;
scene3d.viewpoint = activeViewpoint;
showAndHide(scene3d, map2d);
switchViewButton.textContent = "2D";
activeViewpoint.scale /= scaleConversionFactor;
map2d.viewpoint = activeViewpoint;
showAndHide(map2d, scene3d);
switchViewButton.textContent = "3D";
switchViewButton.addEventListener("click", switchView);
distanceAction.addEventListener("click", () => startTool("distance"));
areaAction.addEventListener("click", () => startTool("area"));
clearAction.addEventListener("click", clearAllMeasurements);