表示レイヤーの切り替えウィジェット (ArcGIS Experience Builder)

【Esri Community Blog】

これまで、ArcGIS Experience Builder (Developer Edition) を使用したカスタム ウィジェットの作成方法として、「スターター ウィジェットの作成」と「マップの座標を取得」という記事をご紹介しました。

本記事では、Web マップ上に表示するレイヤーを切り替えるウィジェットを作成したのでご紹介します。

ArcGIS Experience Builder では、組み込みの[マップ レイヤー]ウィジェットを使用して、[マップ]ウィジェットに設定された Web マップ上のレイヤーの表示・非表示を変更できます。マップに複数レイヤーがある場合、その中から1つのレイヤーだけを表示して残りのレイヤーを非表示にしたいという場合もあるかと思いますが、[マップ レイヤー]ウィジェットでは、レイヤーごとに複数回操作して表示・非表示を切り替える必要があります。今回作成したウィジェットは、レイヤーごとに対応したボタンをクリックすると一括で他のレイヤーを非表示に切り替えるウィジェットです。

layer-switching-widget.gif

インストール

ArcGIS Experience Builder のインストール ガイドを参照して、ArcGIS Experience Builder (Developer Edition) のダウンロードやインストール、設定を行います。

ステップ

フォルダーの作成

1. 以下のパスに新規で `layer-switching` という名称のフォルダーを作成します。
ウィジェット フォルダーにスペースを含めることはできません。ウィジェット構築の詳細は、ウィジェットの実装を参照してください。
\client\your-extensions\widgets

manifest ファイルの作成

2. 以下のパスにある `manifest.json` と `icon.svg` をコピーし、`layer-switching`フォルダーの直下に配置します。
\client\your-extensions\widgets\simple

3. コード エディター等で `layer-switching`フォルダーの `manifest.json` を開きます。

4. `manifest.json` の name プロパティを `layer-switching`、label プロパティを `レイヤー切替` に変更します。

・manifest.json の name プロパティとウィジェットのフォルダー名は一致させる必要があります。

{
  //*** UPDATE ***//
  // "name": "simple",
  // "label": "Simple",
  “name”: “layer-switching”,
  “label”: “レイヤー切替”,
  "type": "widget",
  "version": "1.x.0",
  "exbVersion": "1.x.0",

5. manifest.json の version プロパティの後に プロパティ名 dependency 、値  jimu-arcgis を追加します。これを宣言することで、ウィジェット内で ArcGIS API for JavaScript のモジュールを使用できるようになります。

{
  //*** UPDATE ***//
  // "name": "simple",
  // "label": "Simple",
  “name”: “layer-switching”,
  “label”: “レイヤー切替”,  "type": "widget",
  "version": "1.x.0",
  //*** ADD ***//
  "dependency": "jimu-arcgis",

設定パネルの実装

設定パネルを実装すると、エクスペリエンス(アプリ)の作成者がウィジェットをカスタマイズすることができます。設定パネルは、ArcGIS Experience Builder でウィジェットを選択すると、右側のサイドバーに表示されます。パネルを作成するには、React.PureComponent をベース クラスとして使用する React コンポーネントを作成します。

6.  `layer-switching` フォルダーに、空のオブジェクトを含む config.json ファイルを作成します。

・後で、ウィジェットの設定パラメーターをこのオブジェクトに追加してウィジェットの設定値を保存することができます。

{}

7. `layer-switching` フォルダー内に src フォルダーを作成します。

8. src フォルダー内に、setting という名前のフォルダーを作成します。

9. setting フォルダー内に setting.tsx ファイルを作成します。

10. setting/setting.tsx ファイルを開き、以下の import 文を記述します。

/** @jsx jsx */
import { React, jsx } from "jimu-core";
import { AllWidgetSettingProps } from "jimu-for-builder";

11. コンポーネントを実装するためのコードを追加します。

export default class Setting extends React.PureComponent<AllWidgetSettingProps<any>, any> {
  render() {
    return <div className="widget-setting-demo"> </div>;
  }
}

マップビュー データソースの選択を有効にする

ArcGIS Experience Builder では、一つのページ上に複数のマップ ウィジェットを配置できます。このため、カスタム ウィジェットには、作成者が使用するマップ ウィジェットを選択できる設定パネルのセクションが必要です。

12. setting/setting.tsx ファイルに jimu ライブラリの MapViewSelector をインポートします。

import { MapWidgetSelector } from "jimu-ui/advanced/setting-components";

13. render 関数の前に onMapWidgetSelected 関数を定義します。

// *** ADD ***
 onMapWidgetSelected = (useMapWidgetIds: string[]) => {
   this.props.onSettingChange({
     id: this.props.id,
     useMapWidgetIds: useMapWidgetIds
   });
 };
 render() {
   return <div className="widget-setting-demo"> </div>;
 }

14. render 関数の return() 文の中に、MapViewSelector を表すタグを追加します。

render() {
   return <div className="widget-setting-demo">
     <MapWidgetSelector
       useMapWidgetIds={this.props.useMapWidgetIds}
       onSelect={this.onMapWidgetSelected}
     />
   </div>
 }

ウィジェット パネルの実装

ウィジェットのメインロジックは、widget.tsx で実装する必要があります。このファイルは、React.PureComponent クラスを拡張します。

15. src フォルダー内に、runtime という名前のフォルダーを作成します。

16. runtime フォルダーに、widget.tsx という名前のファイルを作成します。以下のコードを追加して、React.PureComponent クラスを拡張します。

/** @jsx jsx */
import { React, AllWidgetProps, jsx } from "jimu-core";

export default class Widget extends React.PureComponent<AllWidgetProps<any>, any> {
   render() {
     return (
       <div className="widget-starter jimu-widget">
       </div>
     );
   }
}

17. ウィジェット パネルのデザインを設定します。

/** @jsx jsx */
// *** UPDATE ***//
import { React, AllWidgetProps, css, jsx } from "jimu-core";

export default class Widget extends React.PureComponent<AllWidgetProps<any>, any> {
    render() {
        // *** ADD ***//
        const widgetStyle = css`
                 background-color: var(--white);
                 padding: 10px;
                 height: 250px;
                 overflow-y: scroll;`
     return (
       // *** UPDATE ***//
       <div className="widget-starter jimu-widget"  css={widgetStyle}>
       </div>
     );
   }
}

18. widget.tsx ファイルに今回のウィジェットで必要となるクラスやコンポーネントをインポートします。

import { JimuMapViewComponent, JimuMapView } from "jimu-arcgis";
import { Button } from "jimu-ui";
import LayerView from "esri/views/layers/LayerView";
import Collection from "esri/core/Collection";

19. render 関数の前にデフォルトの state を設定します。

export default class Widget extends React.PureComponent<AllWidgetProps<any>, any> {
     //*** ADD ***//
    state = {
        jimuMapView: null, /** 対象 Webマップ */
        webmapLayers: [],/** Web マップのレイヤー情報 */
        chkIdx: 0,  /** ボタンとレイヤーを紐づけるインデックス番号情報 */
        selected: "primary", /** 選択時のボタン デザイン */
        default: "secondary" /** 非選択時のボタン デザイン */
    };
render() {

20.JimuMapView のデータ ソースが変わるたびに state を更新する機能を追加します。

state = {
    jimuMapView: null, /** 対象 Webマップ */
    webmapLayers: [],/** Web マップのレイヤー情報 */
    chkIdx: 0,  /** ボタンとレイヤーを紐づけるインデックス番号情報 */
    selected: "primary", /** 選択時のボタン デザイン */
    default: "secondary" /** 非選択時のボタン デザイン */
};
//*** ADD ***//
// 対象の Web マップが変更された際に実行
activeViewChangeHandler = (jmv: JimuMapView) => {
    if (jmv) {
        this.setState({
            jimuMapView: jmv,
            webmapLayers: this.setLayerList(jmv.view.layerViews)
        });
    }
};

21. render 関数で JSX マークアップに JimuMapViewComponent を追加します。

・追加したコードの最初の 3 行 ({this.props.hasOwnProperty…) は JSX での条件式の使い方です。これは「エクスペリエンスの設定パネルで有効なマップ ウィジェットを選択した場合に、JimuMapViewComponent を追加する」というものです。

render() {
   return (
     <div className="widget-starter jimu-widget">
       {/* *** ADD *** */}
       {this.props.hasOwnProperty("useMapWidgetIds") &&
         this.props.useMapWidgetIds &&
         this.props.useMapWidgetIds[0] && (
           <JimuMapViewComponent
             useMapWidgetId={this.props.useMapWidgetIds?.[0]}
             onActiveViewChange={this.activeViewChangeHandler}
           />
         )
       }
     </div>
   );
}

22. Web マップに設定されているレイヤー情報のリストを取得する処理を追加します。

// 対象の Web マップが変更された際に実行
activeViewChangeHandler = (jmv: JimuMapView) => {
    if (jmv) {
        this.setState({
            jimuMapView: jmv,
            webmapLayers: this.setLayerList(jmv.view.layerViews)
        });
    }
};

//*** ADD ***//
// Web マップからレイヤーの情報を全て取得
setLayerList = (layers: Collection<LayerView>) => {
    const list = [];
    for (let idx = layers.length; 0 < idx; idx--) {
        const layer = layers.items[idx - 1];
        list[idx] = { id: layer.layer.id, name: layer.layer.title }
    }
    return list
};

23. レイヤー切り替え用のボタンを生成する処理と初期表示の処理を追加します。

const widgetStyle = css`
         background-color: var(--white);
         padding: 10px;
        height: 250px;
         overflow-y: scroll;`

//*** ADD ***//
// レイヤーリストからボタンを生成
const { webmapLayers, jimuMapView } = this.state;
let layerList = webmapLayers.length > 0
    && webmapLayers.map((item, idx) => {
        return (
            <tr>
                <td>
                     <Button style={{ width: 380 }} type={this.state.chkIdx === (idx - 1) ? this.state.selected : this.state.default}
                          onClick={() => this.chngRadioBtn((idx - 1), item.id)} size="default"> {item.name} </Button>
       </td>
            </tr>
        ) }, this);

// 初期表示処理
if (jimuMapView) {
    for (let idx = 0; idx < jimuMapView.view.map.layers.length; idx++) {
        const layer = this.state.jimuMapView.view.map.layers.getItemAt(idx);
if (idx === this.state.chkIdx || this.state.chkIdx === -1) {
           layer.visible = true;
        } else {
           layer.visible = false;
        }
   }
}

24. 生成したボタンを表示する領域を追記します。

render() {
   return (
     <div className="widget-starter jimu-widget">
       {this.props.hasOwnProperty("useMapWidgetIds") &&
         this.props.useMapWidgetIds &&
         this.props.useMapWidgetIds[0] && (
           <JimuMapViewComponent
             useMapWidgetId={this.props.useMapWidgetIds?.[0]}
             onActiveViewChange={this.activeViewChangeHandler}
           />
         )
       }
       {/* *** ADD *** */}
       <table>
           {layerList}
       </table>
     </div>
   );
}

25. 生成したボタンをクリックした際のイベント処理を追記します。

setLayerList = (layers: Collection<LayerView>) => {
    const list = [];
    for (let idx = layers.length; 0 < idx; idx--) {
        const layer = layers.items[idx - 1];
        list[idx] = { id: layer.layer.id, name: layer.layer.title }
    }
    return list
};

//*** ADD ***//
// ボタン クリック時のイベント処理
chngRadioBtn = (idx: Number, id: String) => {
    this.setState({
        chkIdx: idx
    });
    for (let cnt = 0; cnt < this.state.jimuMapView.view.map.layers.length; cnt++) {
        const layer = this.state.jimuMapView.view.map.layers.getItemAt(cnt);
        if (layer.id === id || id === 'all') {
            layer.visible = true;
       } else {
            layer.visible = false;
        }
    }
};

ウィジェットのテスト

コードの変更が完了したら、ArcGIS Experience Builder を実行してエクスペリエンスを表示することで、ウィジェットをテストできます。

26. npm start で client フォルダー、server フォルダーを実行し、スクリプトを起動(起動している場合は再起動)します。

27. Web ブラウザでArcGIS Experience Builder を表示します。例:https://localhost:3001

28. ArcGIS Experience Builder で [新規作成] をクリックして、新しい エクスペリエンス ページを作成します。

29. [空白の全画面]のテンプレートの [作成] をクリックします。

30. ウィジェット リストから[マップ]ウィジェットをビルダー画面にドラッグし、任意の Web マップを設定パネルから設定します。

31. ウィジェット リストから今回作成した[レイヤー切替]ウィジェットをビルダー画面にドラッグし、設定パネルのドロップダウンリストから [マップ1] を選択します。

32. ArcGIS Experience Builder のツールバーで、[保存] をクリックしてから [プレビュー] をクリックすると、カスタム ウィジェットとマップが表示されたエクスペリエンスが新しいブラウザ タブで開きます。

以上で完了です。

ArcGIS Experience Builder のプレビューで、作成したウィジェットにあるボタンをクリックすることで、Web マップに設定されているレイヤーの表示が切り替わることを確認できます。

まとめ

今回は、ArcGIS Experience Builder(Developer Edition) を使用して Web マップに設定されているレイヤーを切り替えるサンプル ウィジェットを紹介しました。今回紹介したウィジェットは ESRIジャパンの GitHub に公開しています。また、他にも多くのカスタム ウィジェットのサンプル コードが ArcGIS Experience Builder の Sample Code や GitHub に公開されているので、こちらも併せて参照していただければと思います。

関連リンク

ArcGIS Experience Builder(Developer Edition)

開発リソース集(ArcGIS Experience Builder(Developer Edition))

スターター ウィジェットの作成 (ArcGIS Experience Builder)

Create a starter widget

Get map coordinates

Add layers to a map

ArcGIS Experience Builder (Sample Code)

arcgis-experience-builder-sdk-resources (Esri GitHub)