Pro SDK を使用した ArcGIS Pro の拡張④ : アドインの開発

3772
0
09-27-2018 07:02 PM

Pro SDK を使用した ArcGIS Pro の拡張④ : アドインの開発

はじめに

この記事は、「Pro SDK を使用した ArcGIS Pro の拡張」シリーズの第4弾の記事です。

当ブログシリーズでは、ArcGIS Pro SDK (以下、Pro SDK) を使用した ArcGIS Pro の拡張方法をご紹介していきます。

前回の記事では、Pro SDK のアドインプロジェクトの構成についてご紹介しました。

第4弾では、第3弾の構成を使って、実際に Pro SDK での簡単なアドイン開発を行う流れをご紹介します。

 

アドイン開発と開発ステップ

今回この記事の中では ArcGIS Pro にドックパネルを追加し、ドックパネル内で選択したレイヤーにズームする機能をアドインとして開発します。

※完成イメージ

 

 

次のステップに沿って、アドインの開発方法を紹介していきます。

  1. アドインプロジェクト作成
  2. UI 要素の作成
  3. ロジックの実装
  4. デバッグ実行

それではここからアドインの作成を行っていきます。なお、本記事で記載の開発環境は、Visual Studio 2017Pro SDK 2.2 を利用しています。

 

1. アドインプロジェクト作成

まず、Visual Studio を起動して新規プロジェクトを作成します。

 

次に、ArcGIS のグループから「ArcGIS Pro モジュール アドイン」のテンプレートを選択し、OKボタンを押下します。

※本記事では以下の設定によりプロジェクトを作成していますが、任意の設定で作成してください。

名前:ProAppModule1

場所:D:\workspace\

ソリューション名:ProAppModule1

 

下のイメージのようにプロジェクトが作成されればステップ1のアドインプロジェクト作成は完了です。

 

2. UI 要素の作成

次に、Pro SDK が提供するテンプレートを使用して新しいドックパネルを作成します。

ソリューション エクスプローラーで作成したプロジェクトを右クリックし、「新しい項目の追加」を選択します。

 

ArcGIS のグループから「ArcGIS Pro ドッキング_ウィンドウ」を選択し、追加ボタンを押下します。

※本記事では以下の設定により項目を追加していますが、任意の設定で追加してください。

名前:Dockpane1.xaml

 

下のイメージのように項目が追加されているはずです。

 

テンプレートを使用すると Dockpane1.xaml を継承した Dockpane1ViewModel.cs ファイルも自動で作成され、MVVM モデルを考慮した要素も一緒に作成されます。

また、新しい項目を追加したことにより、Config.daml ファイルにもコントロールの定義が自動で追記されます。

 

次に、Dockpane1.xaml ファイルを開きます。

 

Dockpane1.xaml ファイルにレイヤーの一覧を表示するためのコンボボックスとズームボタンを実装します。

 

Dockpane1.xaml

<DockPanel LastChildFill="true">

<Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="100" />

            <RowDefinition Height="*" />

            <RowDefinition Height="100" />

        </Grid.RowDefinitions>

 

        <Grid Grid.Row="0">

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="Auto" />

                <ColumnDefinition Width="*" />

            </Grid.ColumnDefinitions>

            <!-- レイヤー の一覧を表示するコンボボックスを定義 -->

            <TextBlock

                Grid.Column="0"

                VerticalAlignment="Center"

                Text="レイヤー一覧:  " />

            <ComboBox

                Grid.Column="1"

                Height="40"

                VerticalAlignment="Center"

                ItemsSource="{Binding Layers}"

                SelectedItem="{Binding SelectedLayer, Mode=TwoWay}" />

        </Grid>

  <!-- ズームボタンを定義 -->

        <StackPanel Grid.Row="2">

            <Button

                Height="50"

                Command="{Binding ZoomInCmd}"

                Content="レイヤーにズーム" />

        </StackPanel>

    </Grid>

</DockPanel>

ここまでで、ステップ2 UI 要素の作成は完了です。

 

3. ロジックの実装

次に、レイヤーを選択してズームするために必要なロジックを実装していきます。

Dockpane1ViewModel.cs ファイルを開きます。

 

初めに、今回使用する以下の名前空間を追加します。

using System.Collections.ObjectModel;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

using System.Windows.Input;

using ArcGIS.Desktop.Framework;

using ArcGIS.Desktop.Framework.Contracts;

using ArcGIS.Desktop.Framework.Threading.Tasks;

using ArcGIS.Desktop.Mapping;

using ArcGIS.Desktop.Mapping.Events;

 

次に、イニシャライズのメソッドを定義します。

Dockpane1ViewModel クラス内に定義すること

Map _activeMap;

protected override Task InitializeAsync()

{

    if (MapView.Active == null)

        return Task.FromResult(0);

    _activeMap = MapView.Active.Map;

    return UpdateForActiveMap();

}

 

xaml ファイルに定義したコンボボックス、ボタンにバインディングするプロパティを定義します。

Dockpane1ViewModel クラス内に定義すること

#region Bindable Properties

// ズームボタンにバインドするコマンド

public ICommand ZoomInCmd { get; set; }

 

// コンボボックスにバインドするコレクション

private ObservableCollection<BasicFeatureLayer> _layers = new ObservableCollection<BasicFeatureLayer>();

public ObservableCollection<BasicFeatureLayer> Layers

{

    get { return _layers; }

}

 

// コンボボックスで現在選択しているもの

private BasicFeatureLayer _selectedLayer;

public BasicFeatureLayer SelectedLayer

{

    get { return _selectedLayer; }

    set

    {

        SetProperty(ref _selectedLayer, value, () => SelectedLayer);

        if (_selectedLayer == null)

        {

            FrameworkApplication.SetCurrentToolAsync("esri_mapping_exploreTool");

            return;

        }

        _selectedLayerInfos[_activeMap].SelectedLayer = _selectedLayer;

        SelectedLayerChanged();

    }

}

#endregion

 

プログラムで使用するメソッドを定義します。

Dockpane1ViewModel クラス内に定義すること

#region Private Methods

// マップに何かしらの変化が起きた時の処理

private Task UpdateForActiveMap(bool activeMapChanged = true, Dictionary<MapMember, List<long>> mapSelection = null)

{

    return QueuedTask.Run(() =>

    {

        SelectedLayerInfo selectedLayerInfo = null;

        if (!_selectedLayerInfos.ContainsKey(_activeMap))

        {

            selectedLayerInfo = new SelectedLayerInfo();

            _selectedLayerInfos.Add(_activeMap, selectedLayerInfo);

        }

        else

            selectedLayerInfo = _selectedLayerInfos[_activeMap];

 

        if (activeMapChanged)

        {

            RefreshLayerCollection();

             SetProperty(ref _selectedLayer, (selectedLayerInfo.SelectedLayer != null) ? selectedLayerInfo.SelectedLayer : Layers.FirstOrDefault(), () => SelectedLayer);

            if (_selectedLayer == null)

            {

                FrameworkApplication.SetCurrentToolAsync("esri_mapping_exploreTool");

                return;

            }

            selectedLayerInfo.SelectedLayer = SelectedLayer;

        }

 

        if (SelectedLayer != null)

        {

            List<long> oids = new List<long>();

            if (mapSelection != null)

            {

                if (mapSelection.ContainsKey(SelectedLayer))

                    oids.AddRange(mapSelection[SelectedLayer]);

            }

            else

            {

                oids.AddRange(SelectedLayer.GetSelection().GetObjectIDs());

            }

        }

    });

}

 

// コンボボックスのコレクションにレイヤーを追加する

private void RefreshLayerCollection()

{

    Layers.Clear();

    if (_activeMap == null)

        return;

 

    var layers = _activeMap.GetLayersAsFlattenedList().OfType<BasicFeatureLayer>();

    lock (_lock)

    {

        foreach (var layer in layers)

        {

            Layers.Add(layer);

        }

    }

}

 

// コンボボックスで選択しているコレクションが変わったときのイベント

private Task SelectedLayerChanged()

{

    return QueuedTask.Run(() =>

    {

        if (SelectedLayer != null)

        {

            var selection = SelectedLayer.GetSelection();

        }

    });

}

 

// コンボボックスで値が選択されているかチェックする

private bool ComboCheck()

{

    if (SelectedLayer == null)

    {

         return false;

    }

    else

    {

        return true;

    }

}

#endregion

 

マップや、レイヤーに発生するイベントハンドラーを定義します。

Dockpane1ViewModel クラス内に定義すること

#region Event Handlers

// マップにレイヤーが追加された時のイベント

private void OnLayersAdded(LayerEventsArgs args)

{

    foreach (var layer in args.Layers)

    {

        if (layer.Map == _activeMap && layer is BasicFeatureLayer)

        {

            Layers.Add((BasicFeatureLayer)layer);

            if (SelectedLayer == null)

                SelectedLayer = (BasicFeatureLayer)layer;

        }

    }

}

 

// マップからレイヤーが削除された時のイベント

private void OnLayersRemoved(LayerEventsArgs args)

{

    foreach (var layer in args.Layers)

    {

        if (layer.Map == _activeMap)

        {

            if (Layers.Contains(layer))

                Layers.Remove((BasicFeatureLayer)layer);

        }

    }

 

    if (SelectedLayer == null)

    {

        SelectedLayer = Layers.FirstOrDefault();

        SelectedLayerChanged();

    }

}

 

// マップが削除されたときのイベント

private void OnMapRemoved(MapRemovedEventArgs args)

{

    var map = _selectedLayerInfos.Where(kvp => kvp.Key.URI == args.MapPath).FirstOrDefault().Key;

    if (map != null)

        _selectedLayerInfos.Remove(map);

}

 

// マップの内容が変わったときのイベント

private void id(MapSelectidEventArgs args)

{

    if (args.Map != _activeMap)

        return;

 

    UpdateForActiveMap(false, args.Selection);

}

 

// アクティブなマップが変わったときのイベント

private void OnActiveMapViewChanged(ActiveMapViewChangedEventArgs args)

{

    if (args.IncomingView == null)

    {

        SetProperty(ref _selectedLayer, null, () => SelectedLayer);

        _layers.Clear();

        return;

    }

 

    _activeMap = args.IncomingView.Map;

    UpdateForActiveMap();

}

#endregion

 

選択されたレイヤーの情報を保持するクラスを作成します。

internal class SelectedLayerInfo

{

    public SelectedLayerInfo() { }

    public SelectedLayerInfo(BasicFeatureLayer selectedLayer, long? selectedOID)

    {

        SelectedLayer = selectedLayer;

        SelectedOID = selectedOID;

    }

 

    public BasicFeatureLayer SelectedLayer { get; set; }

 

    public long? SelectedOID { get; set; }

}

 

最後に、コンストラクタとデストラクタに定義したイベントを監視して実行する処理を定義します。

Dockpane1ViewModel クラス内に定義すること

protected Dockpane1ViewModel()

{

    System.Windows.Data.BindingOperations.EnableCollectionSynchronization(_layers, _lock);

    LayersAddedEvent.Subscribe(OnLayersAdded);

    LayersRemovedEvent.Subscribe(OnLayersRemoved);

    MapSelectidEvent.Subscribe(id);

    ActiveMapViewChangedEvent.Subscribe(OnActiveMapViewChanged);

    MapRemovedEvent.Subscribe(OnMapRemoved);

    ZoomInCmd = new RelayCommand(() => MapView.Active.ZoomToAsync(SelectedLayer), () => ComboCheck());

}

 

~Dockpane1ViewModel()

{

    LayersAddedEvent.Unsubscribe(OnLayersAdded);

    LayersRemovedEvent.Unsubscribe(OnLayersRemoved);

    MapSelectidEvent.Unsubscribe(id);

    ActiveMapViewChangedEvent.Unsubscribe(OnActiveMapViewChanged);

    MapRemovedEvent.Unsubscribe(OnMapRemoved);

}

 

これでステップ3のロジックの実装は完了です。

 

4. デバッグ実行

最後のステップです。

 

Visual Studio のデバッガでアドイン プロジェクトを実行します。

 

デバッグを開始すると ArcGIS Pro が自動的に起動します。

 

ArcGIS Pro が起動すると作成したアドインが追加されています。

任意のプロジェクトを選択し、画面上段のリボンからアドインタブを選択して作成したドックパネルを操作してみましょう。

※アドインではレイヤーを使用します。レイヤーが含まれるプロジェクト、またはプロジェクト起動後にレイヤーを追加してご確認ください。

 

次回

これで Pro SDK での簡単なアドイン開発を行う流れについての説明は終わりです。

次回は、Pro SDK を使って構成管理の開発をご紹介していきます。

 

関連リンク

  Esri 社 Web サイト:

   ・ArcGIS Pro SDK

   ・ArcGIS Pro SDK コンセプト(GitHub)

   ・ArcGIS Pro SDK サンプル集(GitHub)

   ・ArcGIS Pro API リファレンス

Version history
Last update:
‎11-13-2020 12:44 AM
Updated by: