ArcGIS Pro SDK for .NET を使用した機能開発 ~マップとの対話的な操作その1~

Document created by koki_kuniharaesrij-esridist Employee on Nov 12, 2019Last modified by koki_kuniharaesrij-esridist Employee on Nov 13, 2019
Version 4Show Document
  • View in full screen mode

はじめに

このシリーズでは「ArcGIS Pro SDK for .NET を使用した機能開発」のシリーズ記事として、ArcGIS Pro を拡張するためのアドイン開発時によく使う便利なクラスやメソッド、また、それらを用いた実践的な開発をご紹介します。本シリーズで実装するソースはすべて Github に格納してありますので、ぜひ ArcGIS Pro SDK for .NET(以下 Pro SDK)を使用する開発の参考にしてください。

 

本シリーズ第1回目となる今回は「マップとの対話的な操作により選択したフィーチャの属性表示を行う機能」を実装する方法をご紹介します。

 

■本記事の対象者

Pro SDK を使用してArcGIS Pro のアドイン開発を行っている方

Pro SDK を使用したArcGIS Pro のアドイン開発をこれから行う、もしくは、検討している方

 

※「ArcGIS Pro SDK for .NET を使った独力の開発」でご紹介した Pro SDK ハンズオンで予習しておくと、本シリーズをよりスムーズに理解できると思います。

 

Tips:マップとの対話的な操作とは?

マップとの対話的な操作とは、マップをクリックしたり、ジオメトリをスケッチしたり、マップに対して何らかの操作をしてそこから結果を得ることです。以下動画でマップをクリックした地点の座標を表示させていますが、これはマップをクリックして(マップに対する操作)、そこの座標を取得する(結果を得る)というマップとの対話的な操作の一例です。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Pro SDK でマップとの対話的な操作を行うためのアイテム

「マップ ツール」というアイテムを使用する必要があります。上記動画の「MapTool1」というボタンがそれにあたります。マップ ツールの実装方法については後述します。

 

本記事で実装する機能の概要

マップ ツールで選択したフィーチャの属性をドッキング ウインドウに配置した DataGrid に表示させる機能を実装します(以下「完成イメージ」参照)。ドッキング ウインドウの実装方法については後述します。

 

完成イメージ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

※本記事ではドッキング ウインドウの「対話的操作」タブ内の一部機能の実装を行います(当該タブ内のその他の機能は次回の連載にてご紹介します)。他タブの機能も今後の連載でご紹介します。

 

Tips:なぜマップ ツールをドッキング ウインドウへ配置する必要があるのか

Pro SDK で追加するアイテムはデフォルトで「アドイン」タブに配置されます。確かにそれでも問題ないこともあると思いますが、追加したアイテムをドッキング ウインドウに配置することにより、より操作性の高い機能を開発することができます。マップ ツールをドッキング ウインドウに配置(ドッキング ウインドウ右上の「選択」ボタン)することによって、見た目のバランスや操作の導線を考慮した開発をすることができるようになります。

 

本記事で実装する機能の実装手順

1.マップ ツールの作成

2.マップ ツールをドッキング ウインドウへ配置

3.アクティブなマップ ビューに存在するレイヤーをコンボ ボックスに格納

4.マップ ツールを使用して選択したフィーチャの属性を DataGrid に表示

5.マップ ビューに対するイベント クラスの利用

 

本記事で使用する主な API カテゴリ 

 

  本記事では「マップ ビュー」「ジオデータベース」を中心に使用します。

 

API カテゴリに関しては「ArcGIS Pro SDK for .NET を使った独力での開発」をご参照ください。

 

本記事で使用するソース

本記事で使用するソースは Github に格納しています。ソースに関して実装のポイントとなる部分を抜粋して解説しますので、ダウンロードをお願いします。

 

本記事で使用するファイル 

 

 

1.Config.daml

ボタンなどのアイテムに対して、配置場所などを定義するための設定ファイル。プロジェクト作成時にデフォルトで作成されます。

 

2.IdentifyFeatures.cs

マップ ツール。作成手順に関しては「1.マップツールの作成」をご参照ください。

 

3.MainDockPane.xaml

ドッキング ウインドウ。作成手順に関しては「Pro SDK を使用した ArcGIS Pro の拡張④:アドインの開発」をご参照ください。

 

4.MainDockPaneViewModel.cs

ViewModel。ドッキング ウインドウ(MainDockPane.xaml)作成時に自動的に作成されます。

 

実装手順

1.マップツールの作成

前述した通りですが、マップ ツールはマップに対して対話的な操作を行うためのアイテムです。マップをクリックしたり、ジオメトリをスケッチする際にイベントを発生させることができます。

 

手順

1.1  マップ ツールをプロジェクトに追加

「プロジェクトを右クリック > 追加 > 新しい項目ArcGIS Pro マップ ツール」の手順でマップ ツールをプロジェクトに追加します(本記事ではIdentifyFeatures.cs と名前を付けて保存しています)。

 

 

1.2 IdentifyFeatures.cs(マップ ツール)に処理を追加

今回実装する処理ではフィーチャの選択はポリゴンで行うため、「SketchType」 プロパティに 「SketchGeometryType.Polygon」 を設定します。また、「OnSketchCompleteAsync」メソッド(当該ファイルにデフォルトで作成されているメソッド)の処理を以下のように拡張します。このように実装することで、マップ ツールでスケッチしたポリゴンに交差するフィーチャを選択することができます。

public IdentifyFeatures()
{
    IsSketchTool = true;
    // ポリゴンをスケッチする
    SketchType = SketchGeometryType.Polygon;
    SketchOutputMode = SketchOutputMode.Map;
}

protected override Task<bool> OnSketchCompleteAsync(Geometry geometry)
{
    return QueuedTask.Run(() =>
    {
        // スケッチしたジオメトリに交差するフィーチャを選択する
        var mapView = MapView.Active;
        mapView.SelectFeatures(geometry);
        return true;
    });
}

 

 これで「マップ ツールの作成」は完了です。次の手順で作成したマップ ツールをドッキング ウインドウに配置します。

 

2.マップ ツールをドッキング ウインドウへ配置

「選択」ボタンを押した際にマップ ツールが起動するように実装します。

  

 

手順

2.1 MainDockPane.xaml の設定

「選択」ボタンの 「Command」 属性を 「MainDockPaneViewModel.cs」 の 「SelectionTool」プロパティとバインドします。

<Button Grid.Column="2" Content="選択"
        Command="{Binding Path= SelectionTool}"
        Style
="{DynamicResource Esri_Button}"
>
</Button>

 

2.2 MainDockPaneViewModel.cs でイベントハンドラを登録

「選択」ボタンを押すと 「ExecuteSelectionTool()」メソッドが実行されるように実装します(マップツールが起動します)。当該メソッド内では 「"AddInSamples_IdentifyFeatures"」(マップ ツールのDAMLID)を引数として 「GetPlugInWrapper()」 メソッドが実行されます。このように 「GetPlugInWrapper()」 を使用することでマップ ツール と「選択」ボタンをバインドすることができるようになります。

protected MainDockPaneViewModel()
{
    // 選択ボタンを押すとExecuteSelectionTool()が実行される
    _selectionTool = new RelayCommand(() => ExecuteSelectionTool(), () => true);
}

private RelayCommand _selectionTool;
public ICommand SelectionTool => _selectionTool;
internal static void ExecuteSelectionTool()
{
     // 作成したマップ ツールのDAMLIDを指定
    var cmd = FrameworkApplication.GetPlugInWrapper("AddInSamples_IdentifyFeatures") as ICommand;
    if (cmd.CanExecute(null))
         // マップツール起動
        cmd.Execute(null);
}

 

2.3 Config.daml の設定

「アドイン」 タブにマップ ツールが表示されないように以下のようにマップ ツールの要素をコメントアウトします。

<group id="AddInSamples_Group1" caption="Group 1" appearsOnAddInTab="true">
  <button refID="AddInSamples_MainDockPane_ShowButton" size="large" />
<!—-<tool refID="AddInSamples_IdentifyFeatures" size="large" />-->
</group>

 

これで「マップツールをドッキング ウインドウへ配置」は完了です。

 

3.アクティブなマップ ビューに存在するレイヤーをコンボ ボックスに格納

コンボ ボックスの選択肢がアクティブなマップ ビューに含まれているレイヤーを動的に表示するように実装します。

  

 

 手順

3.1 MainDockPane.xaml の設定

コンボ ボックスの 「Itemsource」 と 「SelectedItem」 属性を 「MainDockPaneViewModel.cs」 の「FeatureLayers」 と 「SelectedFeatureLayer」プロパティとバインドさせます。

<ComboBox Grid.Column="1" ItemsSource="{Binding FeatureLayers}"
          SelectedItem="{Binding SelectedFeatureLayer}"/>

 

3.2 コンボ ボックスとバインドするプロパティを作成

「MainDockPaneViewModel.cs」 に「FeatureLayers」と「SelectedFeatureLayer」プロパティ(「3.1」でバインドさせたプロパティ)を作成します。各々の役割は以下の通りです。

 

・FeatureLayers・・・アクティブなマップ ビューに存在するレイヤーを格納

・SelectedFeatureLayer・・・コンボ ボックスで選択しているレイヤーを格納

 

private ObservableCollection<BasicFeatureLayer> _featureLayers = new ObservableCollection<BasicFeatureLayer>();
private BasicFeatureLayer _selectedFeatureLayer;

public ObservableCollection<BasicFeatureLayer> FeatureLayers
{
    get { return _featureLayers; }
    set
    {
        SetProperty(ref _featureLayers, value, () => FeatureLayers);
    }
}

public BasicFeatureLayer SelectedFeatureLayer
{
    get { return _selectedFeatureLayer; }
    set
    {
        SetProperty(ref _selectedFeatureLayer, value, () => SelectedFeatureLayer);
    }
}

 

3.3 GetLayers() メソッドの実装

「3.2」で作成した「FeatureLayers」プロパティにアクティブなマップ ビューに存在するレイヤーを格納します。

private void GetLayers()
{
    // アクティブなマップビューを取得
    var mapView = MapView.Active;
    if (mapView == null)
        return;

    // コンボ ボックスに格納されているレイヤーをクリア
    FeatureLayers.Clear();

    // コンボ ボックスにレイヤーを格納
    foreach (var featureLayer in mapView.Map.Layers.OfType<BasicFeatureLayer>())
    {
        FeatureLayers.Add(featureLayer);
    }
}

 

これで「アクティブなマップ ビューに存在するレイヤーをコンボ ボックスに格納」は完了です。

 

4.マップ ツールを使用して選択したフィーチャの属性を DataGrid に表示

マップ ツールを使用して選択したフィーチャの属性を表示する DataGrid を実装します。

 

 

手順

4.1 MainDockPane.xaml の設定

DataGrid の「ItemsSourse」 属性と 「MainDockPaneViewModel.cs」 の 「SelectedFeatureDataTable」 プロパティをバインドします。

<DataGrid
   ItemsSource="{Binding Path=SelectedFeatureDataTable, Mode=OneWay}"
   Grid.ColumnSpan="2">

</DataGrid>

 

4.2 DataGrid とバインドするプロパティを作成

「MainDockPaneViewModel.cs」 に「SelectedFeatureDataTable」プロパティを作成します。

private DataTable _selectedFeatureDataTable;
public DataTable SelectedFeatureDataTable
{
    get { return _selectedFeatureDataTable; }
    set
    {
        SetProperty(ref _selectedFeatureDataTable, value, () => SelectedFeatureDataTable);
    }
}

 

これで「マップ ツールを使用して選択したフィーチャの属性を DataGrid に表示」は完了です。

 

5.マップ ビューに対するイベント クラスの利用

マップ ツールの他にマップ ビューに対するイベント クラスを使用すると、より高度なマップとの対話的な操作ができるようになります。今回使用するイベント クラスは以下の四つです。

 

MapSelectionChangedEvent

マップ内の選択状態が変わった場合に発生

 

ActiveMapViewChangedEvent

アクティブなマップを切り替えた場合に発生

 

LayersAddedEvent

マップにレイヤーを追加した場合に発生

 

LayersRemovedEvent

マップからレイヤーを削除した場合に発生

 

5.1 イベントの登録

MainDockPaneViewModel.cs」 のコンストラクタに以下三つのイベントを登録します。

protected MainDockPaneViewModel()
{
    // マップツール
    _selectionTool = new RelayCommand(() => ExecuteSelectionTool(), () => true);

    // イベントの登録
    ActiveMapViewChangedEvent.Subscribe(OnActiveMapViewChanged);
    LayersAddedEvent.Subscribe(OnLayerAdded);
    LayersRemovedEvent.Subscribe(OnLayerRemoved);
}

 

もう一つのイベント「MapSelectionChangedEvent」を「OnShow」メソッド内で登録します。

MainDockPaneViewModel.cs」が継承している「DockPane」抽象クラスの「OnShow」メソッドをオーバーライドすることでドッキング ウインドウが表示/非表示されるタイミングでイベントの登録/解除を行うことができるようになります。今回はドッキング ウインドウが非表示の場合は MapSelectionChangedEvent を解除するように実装します。

private SubscriptionToken _mapSelectionChangedEvent = null;
protected override void OnShow(bool isVisible)
{
    if (isVisible && _mapSelectionChangedEvent == null)
    {
        // イベントの登録
        _mapSelectionChangedEvent = MapSelectionChangedEvent.Subscribe(OnMapSelectionChanged);
    }

    if (!isVisible && _mapSelectionChangedEvent != null)
    {
        // イベントの解除
        MapSelectionChangedEvent.Unsubscribe(_mapSelectionChangedEvent);
        _mapSelectionChangedEvent = null;
    }
}

 

5.2 イベントハンドラの実装①

以下のイベントハンドラを実装します。

・OnLayerAdded()

マップにレイヤーを追加した時に実行されます。追加したレイヤーをコンボ ボックスに格納します。

 

・OnLayerRemoved()

マップからレイヤーを削除した時に実行されます。削除したレイヤーをコンボ ボックスから削除します。また、DataGrid をクリアして選択しているフィーチャを解除します。

 

・OnActiveMapViewChanged()

アクティブなマップビューが変わった時に実行されます。「3.3」で実装した「GetLayers()」メソッドを実行することでコンボ ボックスにアクティブなマップ ビューに存在するレイヤーを格納します。

private void OnLayerAdded(LayerEventsArgs args)
{
    // 追加したレイヤーをコンボボックスに追加
    foreach (var featureLayer in args.Layers.OfType<BasicFeatureLayer>().ToList())
    {
        FeatureLayers.Add(featureLayer);
    }
}

private void OnLayerRemoved(LayerEventsArgs args)
{
    // 削除したレイヤーをコンボボックスから削除
    foreach (var featureLayer in args.Layers.OfType<BasicFeatureLayer>().ToList())
    {
        if (_featureLayers.Contains(featureLayer))
        {
            FeatureLayers.Remove(featureLayer);
        }
    }

    // DataGridをクリアして選択しているフィーチャを解除
    Clear();
}

private void OnActiveMapViewChanged(ActiveMapViewChangedEventArgs args)
{
    // レイヤーを取得
    GetLayers();

    // DataGridをクリアして選択しているフィーチャを解除
    Clear();
}

 

5.3 イベントハンドラの実装②

以下のイベントハンドラを実装します。

・OnMapSelectionChanged()

マップ ツールを使用して選択したフィーチャのフィールド情報と属性を取得し、それらを DataGrid に格納します。

private void OnMapSelectionChanged(MapSelectionChangedEventArgs args)
{
    QueuedTask.Run(() =>
    {
        // レイヤーを選択していない場合
        if (_selectedFeatureLayer == null)
            return;

        var listColumnNames = new List<KeyValuePair<string, string>>();
        var listValues = new List<List<string>>();

        // 選択したフィーチャを処理する
        using (var rowCursor = _selectedFeatureLayer.GetSelection().Search(null))
        {
            bool bDefineColumns = true;
            while (rowCursor.MoveNext())
            {
                var anyRow = rowCursor.Current;
                if (bDefineColumns)
                {
                    // 選択したフィーチャのフィールドを取得
                    foreach (var fld in anyRow.GetFields().Where(fld => fld.FieldType != FieldType.Geometry))
                    {
                        listColumnNames.Add(new KeyValuePair<string, string>(fld.Name, fld.AliasName));
                    }
                }
                // 選択したフィーチャの属性を取得
                var newRow = new List<string>();
                foreach (var fld in anyRow.GetFields().Where(fld => fld.FieldType != FieldType.Geometry))
                {
                    newRow.Add((anyRow[fld.Name] == null) ? string.Empty : anyRow[fld.Name].ToString());
                }
                listValues.Add(newRow);
                bDefineColumns = false;
            }

        }

        // DataGridにカラムを設定
        SelectedFeatureDataTable = new DataTable();
        foreach (var col in listColumnNames)
        {
            SelectedFeatureDataTable.Columns.Add(new DataColumn(col.Key, typeof(string)) { Caption = col.Value });
        }
        // DataGridに選択したフィーチャの属性を格納
        foreach (var row in listValues)
        {
            var newRow = SelectedFeatureDataTable.NewRow();
            newRow.ItemArray = row.ToArray();
            SelectedFeatureDataTable.Rows.Add(newRow);
        }

        if (_selectedFeatureDataTable.Rows.Count > 0)
        {
           // ズーム
           ZoomToSelection();
        }
        NotifyPropertyChanged(() => SelectedFeatureDataTable);
    });
}

 

5.4 SelectedFeatureLayer に処理を追加

3.2」で実装した「SelectedFeatureLayer」のセッターに「OnMapSelectionChanged(null);」 を追加します。これによってフィーチャ選択後(複数のフィーチャクラスのフィーチャを選択)にコンボ ボックスのレイヤーを変更し、変更後のレイヤーのフィーチャが選択されている場合、その属性が DataGrid に表示されるようになります(以下動画参照)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

public BasicFeatureLayer SelectedFeatureLayer
{
    get { return _selectedFeatureLayer; }
    set
    {
        SetProperty(ref _selectedFeatureLayer, value, () => SelectedFeatureLayer);
        OnMapSelectionChanged(null);
    }
}

 

これで「マップ ビューに対するイベント クラスの利用」は完了です。

 

まとめ

このようにマップ ツールとイベント クラスを利用することによって、ArcGIS Pro 単独では実現できない高度なマップとの対話的な操作を実現することができるようになります。マップ ツールにはその他にも様々なメソッドが用意されています。また、その他々なイベント クラスも用意されていますので、Pro SDK を使用して開発を行う際はぜひ使ってみてください。

 

次回

次回は「マップとの対話的な操作その2」です。本記事でご紹介した機能を拡張します。

 

関連リンク

ArcGIS Pro SDK for .NET を使用した ArcGIS Pro の拡張 シリーズ
ArcGIS Pro SDK for .NET
ArcGIS Pro SDK for .NET コンセプト(GitHub
ArcGIS Pro SDK for .NET サンプル集(GitHub
ArcGIS Pro SDK 2.4 for .NET API リファレンス

ArcGIS Pro SDK for .NET ハンズオン

Attachments

    Outcomes