WPF - XDL Tutorial

XDL PlanetView 활용 두번째

(piXoneer XDL Tutorial)

 

 

 

 

 

 

NXPlanetView를 활용하여 2D 3D 환경에서 다양한 측정방법을 구현해 보고,

 사용자 정의 설정한 정점에 대한 거리 및 각도 측정도 구현해 봅니다.

 

2019. 04.

 

 

목차

XDL PlanetView 활용 두번째... 1

1    사용하기... 3

1.1    XDL 엔진... 3

2    NXPlanetView 이용한 예제 프로그램 만들기... 3

2.1    기본 프로그램 작성... 3

2.2    프로그램 디자인... 3

2.3    기능 이벤트 추가... 6

 

 

 

 

1     사용하기

 

1.1    XDL 엔진

설치 프로그램으로 배포되는 XDL 엔진은 Visual Studio 2010 x86 Release 버전으로, Visual Studio 2010 이상의 버전에서 사용 가능하다.

아래의 설명은 Visual Studio 2015을 기준으로 하겠다.

2     NXPlanetView를 이용한 예제 프로그램 만들기

l  본 예제 프로그램은 NXPlanetView의 기본 측정 모드를 설정하여 길이, 면적 등을 측정해 보고, 세 점을 이용한 거리 및 각도를 사용자 코드를 이용하여 도시하고 측정해 보도록 한다.

2.1    기본 프로그램 작성

2.1.1     Visual Studio 2015을 이용하여 예제 “XDL_PlanetView1”기본 프로그램 작성방법을 참고로 기본 프로젝트를 생성한다.

 

2.2    프로그램 디자인

 

 

Number

Name

Control type

menu

Menu

splitContainter1

GridSplitter

nxPlanetView2D

NXPlanetView

nxPlanetView3D

NXPlanetView

nxPlanetLayer2D

NXPlanetLayer

 

2.2.1     MainWindow.xaml 창에서 기본으로 생성된 Grid 레이아웃에 Grid.RowDefinition을 이용하여 Grid를 두 개로 나누어 생성한다. 첫 번째 GridMenu를 배치한다.

2D measurement는 아래와 같은 하부 메뉴를 갖는다. 아래 표를 참고하여 메뉴를 생성하도록 한다.

 

 

Control Type

Header

Name

MenuItem

2D Measurement

 

MenuItem

거리(단선) 측정

Measure2DLineMenuItem

MenuItem

거리(폴리라인) 측정

Measure2DPolyLineMenuItem

MenuItem

면적 측정

Measure2DAreaMenuItem

MenuItem

각도 측정(진북방향)

Measure2DAngleMenuItem

MenuItem

각도 측정(3점 이용)

Measure2DAngle3PtMenuItem

MenuItem

원형 측정

Measure2DCircleMenuItem

3D measurement에서의 메뉴 구성은 아래와 같다. 아래의 표를 참고 하여 메뉴를 구성한다.

 

Control Type

Header

Name

MenuItem

3D Measurement

 

MenuItem

거리(단선) 측정

Measure3DLineMenuItem

MenuItem

거리(폴리라인) 측정

Measure3DPolyLineMenuItem

MenuItem

면적 측정

Measure3DAreaMenuItem

MenuItem

각도 측정(진북방향)

Measure3DAngleMenuItem

MenuItem

각도 측정(3점 이용)

Measure3DAngle3PtMenuItem

MenuItem

원형 측정

Measure3DCircleMenuItem

 

마지막으로 사용자 정의 메뉴를 생성한다.

 

 

Control Type

Header

Name

MenuItem

사용자 정의

 

MenuItem

거리 및 각도 측정

UserDefinedMeasureMenuItem

 

2.2.2     다음으로 두 번째 Row GridGrid.ColumnDefinition을 이용하여 세 개의 Column을 생성한다. 첫 번째 Column에는 NXPlanetViewNXPlanetLayer를 배치하고 두 번째 ColumnGridSpiltter을 놓는다. 세 번째 ColumnNXPlanetView 을 배치한다

2.2.3     nxPlanetView3D 컨트롤을 선택하고 [속성] 창의 “EarthMode”“Planet3D”로 설정한다.

NXPlanetViewEarthMode“Planet2D”이다. Earth모드를 “Planet3D”로 설정하면 PlanetView는  3차원 도시 모드로 변경된다.

 

 

2.2.4     솔루션을 빌드하고 실행한다.

 

 

2.3    기능 및 이벤트 추가

2.3.1     Window 창을 선택하고 [속성] - [이벤트 ] 메뉴를 선택한 뒤 “Loaded” 이벤트를 더블 클릭한다.

“Loaded” 이벤트를 더블 클릭하면, “Window_Loaded” 함수가 자동적으로 추가되고 아래와 같이 추가적인 기능을 구현할 수 있도록 MainWindow.xaml.cs의 코드로 이동하는 것을 확인할 수 있다. 아래와 같이 코드를 추가한다.

using Pixoneer.NXDL;
using Pixoneer.NXDL.NGR;
using Pixoneer.NXDL.NXPlanet;

namespace XDL_PlanetView2
{
    public partial class MainWindow : Window
    {
 public MainWindow()
        {
            InitializeComponent();
            nxPlanetView2D.BackColor = Color.Black;
            nxPlanetView3D.BackColor = Color.Black;
        }

private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            XGeoPoint gpEye = XGeoPoint.FromDegree(127.4, 38.0, 1500000);
            // Planet2D 모드의 camera 위치 설정
            nxPlanetView2D.SetCameraPosition(gpEye, XAngle.FromDegree(0.0));
            // Planet3D 모드의 camera 위치 설정
            nxPlanetView3D.SetCameraPosition(gpEye, XAngle.FromDegree(0.0),
 XAngle.FromDegree(-90.0), XAngle.FromDegree(0.0));

            // PlanetView의 측정 단위 설정
            // 거리 측정 단위
            nxPlanetView3D.ToolboxDistUnit = 
NXPlanetView.eToolboxDistUnit.Mile;
            // 면적 측정 단위
            nxPlanetView3D.ToolboxAreaUnit = 
NXPlanetView.eToolboxAreaUnit.SquareKiloMeter;
         
            nxPlanetView2D.RefreshScreen();
            nxPlanetView3D.RefreshScreen();
        }
    }
}

2.3.2     “2D Measurement/3D Measurement/사용자 정의의 하위 메뉴를 선택한 후 더블 클릭한다.

메뉴에서 마우스로 더블 클릭하면 “Click”에 대한 이벤트 함수가 자동 추가된다.

 

2.3.3     각 메뉴에 따른 nxPlanetView2DnxPlanetView3DToolboxMode를 설정한다.

NXPlanetView의 측정모드로는 2점을 이용한 거리 측정(NXPlanetView.eToolboxMode. DistanceMeasurer), 다중 점을 이용한 거리 측정(eToolboxMode.PathMeasurer), 다중 점을 이용한 면적 측정(eToolboxMode.AreaMeasurer), 점의 거리를 반지름으로 원형 측정(eToolboxMode.CircleMeasurer), 점으로 이루어진 벡터와 진북방향과의 각도 측정(eToolboxMode.AngleMeasurer), 점으로 이루어진 각도 측정(eToolboxMode.AngleMeasurer2) 등이 있다.

측정 결과를 표시하는 데에 측정 단위를 설정하려면, 면적에 대해서는 NXPlanetView ToolboxAreaUnit 속성을 NXPlanetView.eToolboxAreaUnit 값으로 설정하면 되고, 거리에 대해서는 NXPlanetView ToolboxDistUnit 속성을 NXPlanetView.eToolboxDistUnit 값으로 설정한다. 코드는 아래와 같다.

private void Measure2DLineMenuItem_Click(object sender, RoutedEventArgs e)
{
   nxPlanetView2D.ToolboxMode = NXPlanetView.eToolboxMode.DistanceMeasurer;
}

private void Measure2DPolyLineMenuItem_Click(object sender, RoutedEventArgs e)
{
   nxPlanetView2D.ToolboxMode = NXPlanetView.eToolboxMode.PathMeasurer;
}

private void Measure2DAreaMenuItem_Click(object sender, RoutedEventArgs e)
{
   nxPlanetView2D.ToolboxMode = NXPlanetView.eToolboxMode.AreaMeasurer;
}

private void Measure2DAngleMenuItem_Click(object sender, RoutedEventArgs e)
{
   nxPlanetView2D.ToolboxMode = NXPlanetView.eToolboxMode.AngleMeasurer;
}

private void Measure2DAngle3PtMenuItem_Click(object sender, RoutedEventArgs e)
{
   nxPlanetView2D.ToolboxMode = NXPlanetView.eToolboxMode.AngleMeasurer2;
}

private void MMeasure2DCircleMenuItem_Click(object sender, RoutedEventArgs e)
{
   nxPlanetView2D.ToolboxMode = NXPlanetView.eToolboxMode.CircleMeasurer;
}

private void Measure3DLineMenuItem_Click(object sender, RoutedEventArgs e)
{
  nxPlanetView3D.ToolboxMode = NXPlanetView.eToolboxMode.DistanceMeasurer;
}

private void Measure3DPolyLineMenuItem_Click(object sender, RoutedEventArgs e)
{
   nxPlanetView3D.ToolboxMode = NXPlanetView.eToolboxMode.PathMeasurer;
}

private void Measure3DAreaMenuItem_Click(object sender, RoutedEventArgs e)
{
   nxPlanetView3D.ToolboxMode = NXPlanetView.eToolboxMode.AreaMeasurer;
}

private void Measure3DAngleMenuItem_Click(object sender, RoutedEventArgs e)
{
   nxPlanetView3D.ToolboxMode = NXPlanetView.eToolboxMode.AngleMeasurer;
}

private void Measure3DAngle3PtMenuItem_Click(object sender, RoutedEventArgs e)
{
    nxPlanetView3D.ToolboxMode = NXPlanetView.eToolboxMode.AngleMeasurer2;
}

private void Measure3DCircleMenuItem_Click(object sender, RoutedEventArgs e)
{
    nxPlanetView3D.ToolboxMode = NXPlanetView.eToolboxMode.CircleMeasurer;
}

2.3.4     사용자 정의 측정을 위한 변수를 추가하고 UserDefinedMeasureMenuItem 이벤트 함수에서 초기화한다.

사용자 정의 측정 여부를 설정하는 “bool userMeasure”와 각도 측정을 위한 세 변수 “XGeoPoint posMeasure0, posMeasure1, posMeasure2”를 생성하고 초기화한다.

public partial class MainWindow : Window
{
    bool userMeasure = false;
    int countPos = 0;
    XGeoPoint posAngle0, posAngle1, posAngle2;    
// index 0 : start, index 1 : center, index 2 : end

    public MainWindow()
    {
        InitializeComponent();
        nxPlanetView2D.BackColor = System.Drawing.Color.Black;
        nxPlanetView3D.BackColor = System.Drawing.Color.Black;
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        XGeoPoint gpEye = XGeoPoint.FromDegree(127.4, 38.0, 1500000);
        // Planet2D 모드의 camera 위치 설정
        nxPlanetView2D.SetCameraPosition(gpEye, XAngle.FromDegree(0.0));
        // Planet3D 모드의 camera 위치 설정
        nxPlanetView3D.SetCameraPosition(gpEye, XAngle.FromDegree(0.0),
 XAngle.FromDegree(-90.0), XAngle.FromDegree(0.0));

        // PlanetView의 측정 단위 설정
        // 거리 측정 단위
        nxPlanetView3D.ToolboxDistUnit = NXPlanetView.eToolboxDistUnit.Mile;
        // 면적 측정 단위
        nxPlanetView3D.ToolboxAreaUnit = 
NXPlanetView.eToolboxAreaUnit.SquareKiloMeter;
         
        nxPlanetView2D.RefreshScreen();
        nxPlanetView3D.RefreshScreen();

        // 사용자 정의 측정을 위한 변수 초기화
        userMeasure = false;
        countPos = 0;
    }

private void UserDefinedMeasureMenuItem_Click(object sender, RoutedEventArgs e)
{
        userMeasure = !userMeasure;
        countPos = 0;
}

2.3.5     각도 및 거리를 측정하는 함수를 이용하기 위해서 “NXDLcc.dll”을 참조에 추가한다.

WGS84체를 반영한 거리를 측정하는 함수는 Xcc.CalcGeodeticDistance이며, 각도를 측정하는 함수는 Xcc.CalcGeodeticAngle이다. 이를 위해 NXDLcc.dll을 참조에 추가하고, “using Pixoneer.NXDL.NCC;” 구문 또한 추가한다.

2.3.6     nxPlanetLayer2D을 선택하여 “OnWndProc” 이벤트와 “OnRender” 이벤트 함수를 추가하여 코드를 작성한다.

OnWndProc 이벤트는 NXPlanetLayer가 추가되어 있는 View의 윈도우 이벤트를 외부에서 받아서 사용할 수 있도록 한다. OnRenderNXPlanetLayer가 추가되어 있는 ViewRendering이 완료된 후 어플리케이션에서 추가적인 작업을 구현해야 할 필요가 있을 때 사용하면 된다.

왼쪽 버튼이 클릭할 때마다 각도 측정을 위한 점을 차례대로 설정하고 3점이 모두 설정되면 계산결과를 Visual Studio 2015 출력창에 출력하도록 한다. 또한 2점 이상인 경우 화면에 붉은 선으로 도시한다. 코드는 아래와 같다.

private bool nxPlanetLayer2D_OnRender(object sender, NXPlanetDrawArgs e)
{
      if (!userMeasure) return false;

      if (countPos > 1)
      {
          e.Graphics.glDisable(XGraphics.GL_DEPTH_TEST);
          e.Graphics.glEnable(XGraphics.GL_BLEND);
          e.Graphics.glBlendFunc(XGraphics.GL_SRC_ALPHA, XGraphics.GL_ONE_MINUS_SRC_ALPHA);

          e.Graphics.glPushMatrix();

          e.Graphics.glColor3f(1.0f, 0.0f, 0.0f);
          e.Graphics.glLineWidth(3);
          e.Graphics.glBegin(XGraphics.GL_LINE_STRIP);

          XVertex3d posWorld = nxPlanetView2D.GeographicToWorld(posAngle0);
          e.Graphics.glVertex3d(posWorld - e.WOS);

          posWorld = nxPlanetView2D.GeographicToWorld(posAngle1);
          e.Graphics.glVertex3d(posWorld - e.WOS);

          if (countPos >= 3)
          {
            posWorld = nxPlanetView2D.GeographicToWorld(posAngle2);
            e.Graphics.glVertex3d(posWorld - e.WOS);
          }

          e.Graphics.glEnd();
          e.Graphics.glColor3f(1.0f, 1.0f, 1.0f);
          e.Graphics.glPopMatrix();
          e.Graphics.glEnable(XGraphics.GL_DEPTH_TEST);
      }

          return default(bool);
}

private bool nxPlanetLayer2D_OnWndProc(object sender, NXPlanetDrawArgs e, ref System.Windows.Forms.Message m)
{
      if (m.Msg == Pixoneer.NXDL.XWndMsg.XWM_LBUTTONDOWN)
      {
           if (userMeasure)
           {
              double x = Pixoneer.NXDL.XWndMsg.GetLowValue(m.LParam);
              double y = Pixoneer.NXDL.XWndMsg.GetHighValue(m.LParam);

              XGeoPoint gpPos = nxPlanetView2D.ScreenToGeographic(x, y);
              if (countPos == 0)
              {
                  posAngle0 = gpPos;
              }
              else if (countPos == 1)
              {
                  posAngle1 = gpPos;
              }
              else if (countPos == 2)
              {
                  posAngle2 = gpPos;

               // 두 선분의 길이와 세 점으로 이루어지는 각도 계산
                  double distance = 0.0;
                  distance += Xcc.CalcGeodeticDistance(XAngle.FromDegree(posAngle0.lond), XAngle.FromDegree(posAngle0.latd),
                            XAngle.FromDegree(posAngle1.lond), XAngle.FromDegree(posAngle1.latd));
                   distance += Xcc.CalcGeodeticDistance(XAngle.FromDegree(posAngle1.lond), XAngle.FromDegree(posAngle1.latd),
                            XAngle.FromDegree(posAngle2.lond), XAngle.FromDegree(posAngle2.latd));

                    double angle = 0.0;
                    angle = Xcc.CalcGeodeticAngle(XAngle.FromDegree(posAngle1.lond), XAngle.FromDegree(posAngle1.latd),
                            XAngle.FromDegree(posAngle0.lond), XAngle.FromDegree(posAngle0.latd),
                            XAngle.FromDegree(posAngle2.lond), XAngle.FromDegree(posAngle2.latd));

                      System.Diagnostics.Debug.WriteLine("Distance : " + distance.ToString());
                      System.Diagnostics.Debug.WriteLine("Angle    : " + angle.ToString());
                    }
                    countPos++;
                    nxPlanetView2D.RefreshScreen();
           }
      }
            return default(bool);
}

 

2.3.7     솔루션을 빌드하고 실행한다.

2D Measurement거리(폴리라인) 측정메뉴를 선택한 뒤 nxPlanetView2D 위에서 마우스 왼쪽 버튼을 클릭하면서 측정하고자 하는 정점을 설정한다. 아래 그림과 같이 측정 결과가 각 선분 요소에 도시된다.

 

 

3D Measurement각도 측정(진북방향)” 메뉴를 선택한 뒤 nxPlanetView3D 위에서 마우스 왼쪽 버튼을 클릭하고 마우스를 움직이면 진북방향과 이루는 각도가 화면에 도시되며 마우스 왼쪽 버튼을 더블 클릭하여 측정을 종료한다.

 

 

사용자 정의 메뉴의 거리 및 각도 측정을 선택한 뒤 nxPlanetView2D 위에서 마우스 왼쪽 버튼을 클릭하면 2점 이상부터 화면에 붉은 실선이 도시되고, 3점이 선택되면 Visual Studio 2015의 출력창에 측정결과가 나타난다.

 

 

 

 

측정결과의 단위는 거리의 경우 meter, 각도의 경우 ˚(degree)이다.