5-ArcGIS Pro SDK for .NET を使用した機能開発 ~ジオメトリ変換~

Document created by wakana_satoesrij-esridist Employee on Jan 16, 2020Last modified by koki_kuniharaesrij-esridist on Jan 23, 2020
Version 14Show Document
  • View in full screen mode

はじめに

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

 

本シリーズのこれまでの記事では、以下のような機能の実装方法をご紹介しました。

 1 : マップとの対話的な操作により選択したフィーチャの属性表示を行う機能

 2 : フィーチャの強調表示、フィーチャへのズーム機能

 3 : レイヤーのレンダラーの設定

第 4 : アノテーションを操作する機能を実装

 5 弾となる今回は、ポリゴン・ライン ジオメトリをポイントに変換する機能の実装方法をご紹介します。

 

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

ArcGIS Pro は、ポリゴン・ライン・ポイントなどのジオメトリを操作するためのジオプロセシングツールを多数そろえています。しかし、それらのジオプロセシングツールで実現することが難しい処理を行いたい場合、または所有するライセンスにより使用できないツールと類似の機能を実装したい場合には、Pro SDK を使用する必要があります。本記事では一例として以下のようにジオメトリを変換する機能をご紹介します。

 

  • ポリゴン:ポリゴンの重心を取得してポイントを作成する
  • ラインラインの両端にポイントを作成する 

 

普段 ArcGIS Pro でデータを選択するように、対象のデータを選択するための[選択のダイアログボックス]を使用できるようにする方法も解説します。

 

完成イメージ

 

     ※使用データに関しては、末尾の関連リンクをご参照ください

 

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

 

  1. アイテム選択ダイアログから結果を格納するファイルジオデータベースを選択する
  2. アクティブなマップ ビューに存在するポリゴン・ラインのレイヤーのみをコンボ ボックスに格納する
  3. ジオメトリ変換を実装する(ポリゴンは重心を取得し、ラインは両端のポイントを取得する)

※本記事ではドッキング ウィンドウの「ジオメトリ変換」タブの機能開発を行います。このシリーズ記事では、Pro SDK の機能をいくつかのタブに沿って解説しています。他のタブの機能については、「ArcGIS Pro SDK for .NET を使った独力での開発」より各記事をご参照ください。

 

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

 

 本記事では「ジオメトリ」「編集」「ジオプロセシング」を中心に使用します。

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

 

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

 

 

実装手順

1. ダイアログからジオメトリ変換した結果を格納するファイル ジオデータベースを指定する

ファイル ジオデータベースを選択するダイアログ画面を呼び出す処理を実装します。(ジオメトリ変換した結果を格納する場所を指定します。)
[出力先 GDB] の入力のテキストボックスには、選択したファイル ジオデータベースへのパスが表示されます。

手順

1.1 MainDockPane.xaml の設定

「ジオメトリ変換」タブ アイテムの「TextBox」の「Text」属性を「MainDockPaneViewModel.cs」の「GdbPath」プロパティにバインドさせます。

<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding GdbPath, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Margin="5,5,5,5"/>

また、「開く」ボタンの「Command」属性を「MainDockPaneViewModel.cs」の「OpenGdbCmd」プロパティにバインドさせます。

<Button Content="開く" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Stretch" Command="{Binding Path=OpenGdbCmd}" VerticalAlignment="Center" Style="{DynamicResource Esri_Button}"></Button>

 

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

「開く」ボタンを押下すると、アイテム選択ダイアログが開く処理が実行されるように実装します。コンストラクタに次の処理を追加します。

// ジオメトリ変換 [開く]を押すとアイテム選択ダイアログが表示される
_openGdbCmd = new RelayCommand(() => OpenGdbDialog(), () => true);

 

1.3 MainDockPaneViewModel.csへGdbPathプロパティを定義する

選択したファイルジオデータベースのパスを取得し表示する”GdbPath”プロパティを定義します。

private string _gdbPath = string.Empty;
public string GdbPath
{
    get { return _gdbPath; }
    set
    {
        SetProperty(ref _gdbPath, value, () => GdbPath);
    }
}

 

1.4 アイテム選択ダイアログを開くメソッドを実装

下記のコードでは、OpenItemDialog クラスをインスタンス化し、アイテム選択ダイアログでカスタムできる部分を 3 つ実装しています。

  • Title:アイテム選択ダイアログの左上に表示されるタイトル
  • MultiSelect:複数のファイルを選択できるかどうかの設定
  • Filter:特定のデータタイプ(拡張子)のみを表示する設定
private ICommand _openGdbCmd;
public ICommand OpenGdbCmd => _openGdbCmd;
private void OpenGdbDialog()
{
    // 選択ダイアログを作成するクラスをインスタンス化する
    OpenItemDialog searchGdbDialog = new OpenItemDialog
    {
        Title = "ファイルジオデータベースを選択", // 選択ダイアログのタイトル
        MultiSelect = false, // 複数のデータの選択を許可しない
        Filter = ItemFilters.geodatabases //選択するデータの指定(フィルター:*.gdbのデータのみ表示・選択できる)
    };

    var ok = searchGdbDialog.ShowDialog();
    if (ok != true)
        return;

    var selectedItems = searchGdbDialog.Items;
    foreach (var selectedItem in selectedItems)
        GdbPath = selectedItem.Path;  //選択したファイルパスを取得する
}

 

2.アクティブなマップ ビューに存在するポリゴン・ラインレイヤーのみをコンボ ボックスに格納する

ここでは、コンボボックスで対象のレイヤーを選択する機能を実装します。選択対象とするレイヤーは、 1  および  3  の記事と同じように、アクティブなマップ ビューに含まれているレイヤーが対象です。おさらいになりますので、いずれかの記事をご参照ください。今回は前回までの記事に加えて、アクティブなマップ ビューに含まれるレイヤーの中でも、ポリゴンまたはラインのデータのみが対象となるように実装します。

 

 

実際に選択させるレイヤーをコンボボックスに格納する処理は、MainDockPaneViewModel クラスの GetLayers() メソッドに集約されています。このメソッド内では、レイヤーを格納する処理に加えて、特定のジオメトリタイプのみを抽出し格納する処理を実装します。

// ジオメトリ変換タブのレイヤーコンボボックスに、ポリゴンまたはラインのレイヤーを格納
var polygonAndLineLayers = mapView.Map.Layers.OfType<FeatureLayer>().Where(f => f.ShapeType == esriGeometryType.esriGeometryPolygon || f.ShapeType == esriGeometryType.esriGeometryPolyline);
PolygonAndLineLayers.Clear();
foreach (var polygonAndLineLayer in polygonAndLineLayers) PolygonAndLineLayers.Add(polygonAndLineLayer);

 

3.ジオメトリ変換を実装する(ポリゴンは重心を取得し、ラインは両端のポイントを取得する)

選択したレイヤーに含まれるジオメトリのタイプを判別して、それぞれ次の操作を行います。

 

  • ポリゴン:ポリゴンの重心を求めてポイントを作成する
  • ラインラインの両端にポイントを作成する

 

[ポイント作成] ボタンを押下したときに、ジオメトリ変換を行います。

 

 

手順

3.1 MainDockPane.xaml の設定

 

「ジオメトリ変換」タブ アイテムの“フィーチャクラス名”を入力する「TextBox」の「Text」属性を「MainDockPaneViewModel.cs」の「FeatureClassName」プロパティにバインドさせます。

<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding FeatureClassName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Margin="5,5,5,5"/>

また、「ポイント作成」ボタンの「Command」属性を「MainDockPaneViewModel.cs」の「CreatePoint」プロパティにバインドさせます。

<Button Content="ポイント作成" Grid.Row="2" Grid.Column="2" HorizontalAlignment="Stretch" Command="{Binding Path=CreatePoint}" Style="{DynamicResource Esri_Button}"></Button>

 

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

「ポイント作成」ボタンを押下すると、ジオメトリ変換が行われるように実装します。コンストラクタに次の処理を追加します。

// 選択したポリゴンまたはラインにポイントを発生させるジオメトリ処理を行う
_createPoint = new RelayCommand(() => ExecuteCreatePoint(), () => true);

 

3.3 MainDockPaneViewModel.csへFeatureClassNameプロパティを定義する

入力した結果出力用のフィーチャ クラス名称を取得するための”FeatureClassName”プロパティを定義します。

private string _featureClassName;

public string FeatureClassName
{
    get { return _featureClassName; }
    set
    {
        SetProperty(ref _featureClassName, value, () => FeatureClassName);
    }
}

 

3.4 イベントハンドラーの実装

入力されたフィーチャ クラス名称を取得して、ライン・ポリゴンレイヤーに対してジオメトリ変換を行う処理を記述します。

大まかな処理の流れは以下です。

 

  1. 既存のフィーチャ クラスの存在検査
  2. 新規フィーチャ クラス作成(Geoprocessing.ExecuteToolAsync() で作成する)
  3. ポリゴンは重心を取得し、ラインは両端のポイントを取得する処理を実装する

 

ここでは、ポリゴン・ラインそれぞれの実装を解説します。全体コードは Github の MainDockPaneViewModel クラスを参照ください。

 

ポリゴンの重心を取得してポイントを作成する

レイヤーに含まれる各ポリゴンから重心を取得します。重心を取得する処理は、ManipulateGeometry() メソッド内の“polygon”オブジェクトで分岐している条件配下となります。

 

if (manipulatedlayer.GetFeatureClass().GetDefinition().GetShapeType().ToString() == "Polygon")
{
    while (rowCursor.MoveNext())
    {
        using (var row = rowCursor.Current)
        {
            Feature feature = row as Feature;
            Geometry shape = feature.GetShape();

            //
            MapPoint mapPoint = GeometryEngine.Instance.Centroid(shape);

            //レイヤーのフィーチャクラスの Shape フィールドを取得
            string shapeField = featureClass.GetDefinition().GetShapeField();

            var attributes = new Dictionary<string, object>();
            attributes.Add(shapeField, mapPoint);

            //ジオメトリの属性値設定
            foreach (var fld in row.GetFields().Where(fld => fld.FieldType != FieldType.Geometry && fld.FieldType != FieldType.OID && fld.Name != "Shape_Length" && fld.Name != "Shape_Area"))
            {
                attributes.Add(fld.Name, row[fld.Name]);
            }

            //フィーチャの作成と編集実行
            editOperation.Create(featureClass, attributes);
        }

    }

ポリゴンの重心は、GeometryEngine.Instance.Centroid メソッドを使用して取得することができます。取得した重心をポイントとして新規作成する際には、  の記事の「2. 選択したアノテーション フィーチャを回転」でご紹介したように、EditOperation クラスを使用します。今回はフィーチャの新規作成のため Create メソッドを使用します。上記のソースでは、ポイント作成先のフィーチャクラスと、取得したフィーチャ(重心ポイント+属性情報)を渡して新しいフィーチャを作成します。

 

ラインの両端にポイントを作成する

レイヤーに含まれる各ラインから両端のポイントを求める処理は、ManipulateGeometry()メソッド内の“Polyline”オブジェクトで分岐している条件配下となります。

 

else if (manipulatedlayer.GetFeatureClass().GetDefinition().GetShapeType().ToString() == "Polyline")
// ラインの場合の処理
{
    while (rowCursor.MoveNext())
    {
        using (var row = rowCursor.Current)
        {
            Feature feature = row as Feature;
            Polyline polyline = feature.GetShape() as Polyline;
            ReadOnlyPointCollection pts = polyline.Points;

            var mapPointBuilder = new MapPointBuilder(manipulatedlayer.GetSpatialReference());
            mapPointBuilder.X = pts.First().X;
            mapPointBuilder.Y = pts.First().Y;
            MapPoint firstMapPoint = mapPointBuilder.ToGeometry();

            mapPointBuilder.X = pts.Last().X;
            mapPointBuilder.Y = pts.Last().Y;
            MapPoint lastMapPoint = mapPointBuilder.ToGeometry();

            //レイヤーのフィーチャクラスの Shape フィールドを取得
            string shapeField = featureClass.GetDefinition().GetShapeField();

            var firstAttributes = new Dictionary<string, object>();
            firstAttributes.Add(shapeField, firstMapPoint);

            var lastAttributes = new Dictionary<string, object>();
            lastAttributes.Add(shapeField, lastMapPoint);

            //ジオメトリの属性値設定
            foreach (var fld in row.GetFields().Where(fld => fld.FieldType != FieldType.Geometry && fld.FieldType != FieldType.OID && fld.Name != "Shape_Length" && fld.Name != "Shape_Area"))
            {
                firstAttributes.Add(fld.Name, row[fld.Name]);
                lastAttributes.Add(fld.Name, row[fld.Name]);
            }

            editOperation.Create(featureClass, firstAttributes);
            editOperation.Create(featureClass, lastAttributes);

        }

    }
}

ラインの両端のポイントは、Polyline クラスから Polyline.Points のプロパティを取得することで求めることができます。Polyline.Pointsプロパティを使用することによって、ラインの始点と終点を取得することができます(ラインの両端のポイントを取得)。1つラインに対して2つのポイントを作成するため、属性情報もそれぞれ同じものを付加しています。

 

ポリゴンの重心を求めた時と同じように、EditOperation クラスの Create メソッドを使用して、新規でポイントフィーチャを作成します。1つのラインに対して2つのポイントを作成するため、EditOperation  Create メソッドを2回実行します。

 

まとめ

今回はポリゴン・ラインからそれぞれポイントを発生させる処理を解説しました。Pro SDK を使用することによって、ジオメトリ一つ一つのプロパティを取得ができますし、それを利用して今回のようなジオメトリ変換処理を行うことができます。また、今回ご紹介したダイアログ画面の呼び出しなど、ArcGIS Pro 標準の機能を使用することもできます。

 

このシリーズ記事は、ArcGIS Pro SDK for .NET を使った独力での開発 ブログをはじめとして、ArcGIS Pro SDKの実用的な操作や実装について連載してきました。この連載でのご紹介はひとまずこの記事が最後になります。

今後もより業務や研究などの場面で役立つ汎用性の高い機能についてご紹介する予定です。

是非、ご期待ください!

 

 

関連リンク

 

 

使用データ

  • ラインデータ:静岡県 東海自然歩道(静岡県バイパスルート / Esriジャパンオープンデータポータル
  • ポリゴンデータ:E-stat 経済センサス_基礎調査(平成21年)の静岡県のデータを、ファイルジオデータベースに加工し、一部を使用しています。

Attachments

    Outcomes