2023年10月にリリースされた ArcGIS Maps SDK for JavaScript のバージョン 4.28 にて、最小限のコードで Web マッピング アプリケーションを構築するための全く新しい Web コンポーネントである、ArcGIS Maps SDK コンポーネントがベータ版にてリリースされました。
本記事では、ArcGIS Maps SDK コンポーネントについて、米国 Esri 社が公開している「Build GIS Web Apps with JavaScript Maps SDK components」の記事を翻訳してご紹介します。
ArcGIS Maps SDK for JavaScript は、開発者が魅力的な Web GIS エクスペリエンスを構築できる強力なマッピングおよび空間解析テクノロジーです。Esri では、この基盤となるSDKを使用して地理空間アプリケーションを開発しています。これらのアプリケーションにより、ユーザーは地図の作成、分析、共同作業、共有を行うことができます。
Web コンポーネントは、W3C によって導入された一連の技術であり、再利用可能でフレームワークにとらわれないカスタム HTML エレメントの構築を可能にします。強力なカプセル化、カスタム機能、HTML や DOM API との完全な互換性を提供します。SDKで使用しているWeb コンポーネントには、主に次の3つの技術があります。
開発者が独自の HTML エレメントをカスタムした動作で定義できるようにします。
JavaScript SDKとCSSをカスタム エレメント内にカプセル化し、DOM の他の部分に影響を与えないようにします。
テンプレートを含むカスタム エレメントのインスタンスで使用できるマークアップ チャンクの定義を許可します。
要約すると、Web コンポーネントは複雑な HTML とそれに関連する CSS や JavaScriptを簡素化するのに適しています。次に、JavaScript Maps SDK と、SDK の新機能である Web コンポーネントについて説明します。
10月にリリースされた ArcGIS Maps SDK for JavaScript(v4.28) では、標準ベースの Webコンポーネントのベータ版が導入されました。JavaScript Maps SDKコンポーネントの初期リリースは、JavaScript Maps SDK の一部を再利用可能なHTML エレメントに拡張する3つのパッケージで構成されています。
<arcgis-map item-id="05e015c5f0314db9a487a9b46cb37eca"/>
Webコンポーネントは、米国 Esri で豊かな歴史を刻んできました。開発プロセスの基本的な構成要素として Web コンポーネントを採用することで、特定のアプリケーション フレームワーク(React、Angular、Vue など)に縛られることなく、再利用可能なHTML エレメントを幅広く作成できるようになりました。また、Calcite Design System のコンポーネントも多用しています。このように Web コンポーネントを採用することで、開発者を統一し、異なる ArcGIS 製品間で一貫したユーザー エクスペリエンスを実現しています。
プログラミング パラダイムという点では、JavaScript Maps SDK のコンポーネントは宣言型ファーストのアプローチを提供し、JavaScript Maps SDK は伝統的に命令型/手続き型アプローチを主に採用してきました。コンポーネントを使用することで、開発者は、定型的なコードの必要性を減らし、代わりに必要なタスクのすべてではないにしても、HTML マークアップを使用してほとんどを達成することに集中し、事前に構築されたArcGIS エクスペリエンスを信頼することができます。これにより、ArcGIS の機能をアプリに統合するプロセスが簡素化されます。一方、JavaScript Maps SDK では、開発者が詳細な命令を記述できるため、アプリケーションの状態や実行の流れを広範囲に制御できます。
命令形アプローチを使った基本的な Web マップの作成方法について理解を深めるために、以下の例をご覧ください。JavaScript は、地図とそのウィジェットを表示する <div id="viewDiv"></div> コンテナ エレメントを操作するために使用されます。
JavaScript Maps SDKで構築された Web マップ
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1, maximum-scale=1,user-scalable=no"
/>
<title>ArcGIS Maps SDK for JavaScript 4.28</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
</style>
<link
rel="stylesheet"
href="https://js.arcgis.com/4.28/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/4.28/"></script>
<script>
require([
"esri/views/MapView",
"esri/widgets/Legend",
"esri/widgets/Search",
"esri/WebMap",
], (MapView, Legend, Search, WebMap) => {
const webmap = new WebMap({
portalItem: {
id: "05e015c5f0314db9a487a9b46cb37eca",
},
});
const view = new MapView({
container: "viewDiv",
map: webmap,
});
const search = new Search({
view,
});
const legend = new Legend({
view,
});
view.ui.add(search, "top-right");
view.ui.add(legend, "bottom-left");
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
この2つのパラダイムの違いを説明するために、先ほどの例を考え、@ArcGIS/map-components パッケージを使ってどのように書き換えるかを示します。
JavaScript Maps SDK コンポーネントで構築された Web マップ
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0"
/>
<title>Map components</title>
<style>
html,
body {
padding: 0;
margin: 0;
width: 100vw;
height: 100vh;
}
</style>
<!-- Load the ArcGIS Maps SDK for JavaScript -->
<link
rel="stylesheet"
href="https://js.arcgis.com/4.28/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/4.28/"></script>
<!-- Load the Map Components -->
<script
type="module"
src="https://js.arcgis.com/map-components/4.28/arcgis-map-components.esm.js"
></script>
</head>
<body>
<arcgis-map item-id="05e015c5f0314db9a487a9b46cb37eca">
<arcgis-search position="top-right" />
<arcgis-legend position="bottom-left" />
</arcgis-map>
</body>
</html>
Mapコンポーネントでお分かりのように、同じ解決策を達成するために JavaScript は不要であり、viewDiv コンテナ エレメントも必要ありません。また、WebMap と MapView を別々に初期化する必要もなくなります。この例は純粋に Web コンポーネントで書かれていますが、基礎となる API はまだ利用可能であり、命令型と宣言型のプログラミング パラダイムのバランスを作り出していることに注意することが重要です。この柔軟性により、両方のアプローチのメリットを受けることができます。
<body>
<arcgis-map item-id="05e015c5f0314db9a487a9b46cb37eca">
<arcgis-search position="top-right" />
<arcgis-legend position="bottom-left" />
</arcgis-map>
<script>
// Access the underlying API to get the thumbnail url of the map
const mapElm = document.querySelector("arcgis-map");
mapElm.addEventListener("viewReady", async (event) => {
const { view } = event.detail;
const { map } = view;
console.log(map.thumbnailUrl);
});
</script>
</body>
Calcite Design System を JavaScript Maps SDK のアプリと統合して、包括的で魅力的な GIS エクスペリエンスを構築できます。命令型アプローチでは、JavaScriptで作成し操作するために、定型的なコードと <div/> と HTML エレメントが必要であることに注意してください。
JavaScript Maps SDKとCalciteで作られた Web マップ
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1, maximum-scale=1, user-scalable=no"
/>
<title>Calcite and map components (imperative)</title>
<script
src="https://js.arcgis.com/calcite-components/1.9.2/calcite.esm.js"
type="module"
></script>
<link
rel="stylesheet"
href="https://js.arcgis.com/calcite-components/1.9.2/calcite.css"
/>
<script src="https://js.arcgis.com/4.28/"></script>
<link
rel="stylesheet"
href="https://js.arcgis.com/4.28/esri/themes/light/main.css"
/>
</head>
<style>
html,
body,
#viewDiv {
height: 100%;
width: 100%;
padding: 0;
margin: 0;
}
</style>
<body>
<calcite-shell>
<calcite-navigation slot="header">
<calcite-navigation-logo slot="logo" />
</calcite-navigation>
<div id="viewDiv"></div>
</calcite-shell>
<script>
require(["esri/WebMap", "esri/widgets/LayerList", "esri/views/MapView"], (
WebMap,
LayerList,
MapView
) => {
const webMap = new WebMap({
portalItem: {
id: "e691172598f04ea8881cd2a4adaa45ba",
},
});
const view = new MapView({
container: "viewDiv",
map: webMap,
zoom: 4,
});
view.when(() => {
const portalItem = view.map.portalItem;
const navigationLogo = document.querySelector(
"calcite-navigation-logo"
);
navigationLogo.heading = portalItem.title;
navigationLogo.description = portalItem.snippet;
navigationLogo.thumbnail = portalItem.thumbnailUrl;
const layer = view.map.layers.find(
(layer) => layer.id === "Accidental_Deaths_8938"
);
layer.popupTemplate.title = "Accidental Deaths";
const layerList = new LayerList({
view,
});
view.ui.add(layerList, "top-right");
layerList.listItemCreatedFunction = (event) => {
const item = event.item;
if (item.layer.type !== "group") {
item.panel = {
content: "legend",
};
}
};
});
});
</script>
</body>
</html>
JavaScript Maps SDK コンポーネントと Calcite で作られた Webマップ
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1, maximum-scale=1, user-scalable=no"
/>
<title>Calcite and map components (declarative)</title>
<script
src="https://js.arcgis.com/calcite-components/1.9.2/calcite.esm.js"
type="module"
></script>
<link
rel="stylesheet"
href="https://js.arcgis.com/calcite-components/1.9.2/calcite.css"
/>
<link
rel="stylesheet"
href="https://js.arcgis.com/4.28/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/4.28/"></script>
<script
type="module"
src="https://js.arcgis.com/map-components/4.28/arcgis-map-components.esm.js"
></script>
</head>
<style>
html,
body,
arcgis-map {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<body>
<calcite-shell>
<calcite-navigation slot="header">
<calcite-navigation-logo slot="logo" />
</calcite-navigation>
<arcgis-map item-id="e691172598f04ea8881cd2a4adaa45ba" zoom="4">
<arcgis-layer-list position="top-right" />
</arcgis-map>
</calcite-shell>
<script type="module">
const mapElement = document.querySelector("arcgis-map");
const layerListElement = document.querySelector("arcgis-layer-list");
layerListElement.addEventListener("widgetReady", (event) => {
const layerList = event.detail.widget;
layerList.listItemCreatedFunction = (event) => {
const item = event.item;
if (item.layer.type !== "group") {
item.panel = {
content: "legend",
};
}
};
});
mapElement.addEventListener("viewReady", async (event) => {
const view = event.detail.view;
const portalItem = view.map.portalItem;
const navigationLogo = document.querySelector(
"calcite-navigation-logo"
);
navigationLogo.heading = portalItem.title;
navigationLogo.description = portalItem.snippet;
navigationLogo.thumbnail = portalItem.thumbnailUrl;
const layer = view.map.layers.find(
(layer) => layer.id === "Accidental_Deaths_8938"
);
layer.popupTemplate.title = "Accidental Deaths";
});
</script>
</body>
</html>
JavaScript Maps SDK コンポーネントは、アプリの可読性とシンプルさをさらに向上させます。例えば、ウィジェットは reference-element(詳細はプロパティ タブを参照)を <arcgis-map/> の id に設定することでマップにアクセスできます。これはウィジェットが<arcgis-map/> コンポーネントの外側にある場合に便利です。
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1, maximum-scale=1, user-scalable=no"
/>
<title>Calcite and map components (declarative)</title>
<script
src="https://js.arcgis.com/calcite-components/1.9.2/calcite.esm.js"
type="module"
></script>
<link
rel="stylesheet"
href="https://js.arcgis.com/calcite-components/1.9.2/calcite.css"
/>
<link
rel="stylesheet"
href="https://js.arcgis.com/4.28/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/4.28/"></script>
<script
type="module"
src="https://js.arcgis.com/map-components/4.28/arcgis-map-components.esm.js"
></script>
</head>
<style>
html,
body,
arcgis-map {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<body>
<calcite-shell content-behind>
<calcite-shell-panel slot="panel-start" display-mode="float">
<calcite-action-bar slot="action-bar">
<calcite-action
data-action-id="legend"
icon="legend"
text="Legend"
></calcite-action>
</calcite-action-bar>
<calcite-panel
heading="Legend"
height-scale="l"
data-panel-id="legend"
hidden
>
<arcgis-legend
id="legend-container"
reference-element="#viewDiv"
></arcgis-legend>
</calcite-panel>
</calcite-shell-panel>
<arcgis-map item-id="05e015c5f0314db9a487a9b46cb37eca" id="viewDiv" />
</calcite-shell>
<script type="module">
// Reference the map component
const mapElement = document.querySelector("arcgis-map");
mapElement.addEventListener("viewReady", ({ detail: { view } }) => {
view.ui.move("zoom", "top-right");
view.padding = { left: 49 };
let activeWidget;
const handleActionBarClick = ({ target }) => {
if (target.tagName !== "CALCITE-ACTION") {
return;
}
const nextWidget = target.dataset.actionId;
if (nextWidget === activeWidget) {
target.active = false;
document.querySelector(
`[data-panel-id=${nextWidget}]`
).hidden = true;
activeWidget = null;
} else {
if (activeWidget) {
document.querySelector(
`[data-action-id=${activeWidget}]`
).active = false;
document.querySelector(
`[data-panel-id=${activeWidget}]`
).hidden = true;
}
target.active = true;
document.querySelector(
`[data-panel-id=${nextWidget}]`
).hidden = false;
activeWidget = nextWidget;
}
};
let actionBarExpanded = false;
document.addEventListener("calciteActionBarToggle", (event) => {
actionBarExpanded = !actionBarExpanded;
view.padding = {
left: actionBarExpanded ? 150 : 49,
};
});
document
.querySelector("calcite-action-bar")
.addEventListener("click", handleActionBarClick);
});
</script>
</body>
</html>
コンポーネントのチュートリアルでは、最小限のコードで Web マッピング アプリケーションを構築するためのコンポーネントを見つけることができます。また、今後リリースされる追加コンポーネントにもご期待ください。
詳細については、コンポーネントのドキュメントを参照してください。問題が発生した場合は、トラブルシューティング ガイドを参照してください。また、他のユーザーとの共同作業、質問、問題の報告については、Esri コミュニティを参照してください。