GIS App'개발 : MapWindow

TopOfTheHead·2024년 6월 9일

시립대-C# , .NET

목록 보기
8/8

관련 지식 정리

  • OSGeo(Open Source Geospatial Foundation)
    。geospatial 기술과 data의 협업을 통한 개발을 위한 비영리 단체.
    。오픈소스 GIS와 관련된 컨퍼런스 등 관련 프로젝트에 관한 금전적 , 기술적 지원을 제공하며 대표적인 프로젝트로 QGIS와 GIS 개발을 지원하는 SW 라이브러리인 GDAL이 존재.
  • GDAL/OGR (Geospatial Data Abstraction Library)
    。C++로 구축된 raster 및 vector 지리 공간 데이터를 조작 가능한 오픈소스 라이브러리.
  • GeoServer
    。Java로 개발된 오픈소스 GIS Software 서버
    。개방형 표준을 사용하여 다양한 공간 데이터 소스를 서비스
  • PostGIS
    。PostgreSQL의 공간데이터베이스 extension
    。기존 DBMS에서 추가로 geometry , geography , raster , topogeometry와 같은 data type을 제공.
  • Mapbox
    。모바일 및 웹 application 개발을 지원하기 위한 공간정보 플랫폼
    。map에 원하는 데이터를 결합할 수 있도록 mapbox studio 및 API를 지원
  • MapWindow
    。데스크탑 버전의 GIS SW와 C# Library를 포함하는 오픈소스 GIS 개발 프로젝트.

MapWindow

  • 데스크탑 버전의 GIS SW와 C# Library를 포함하는 오픈소스 GIS 개발 프로젝트
  • layer가 추가될 때 자동으로 메인 좌표계(TileMap : 배경맵)에 일치시키는 기능을 지원.

MapWindow를 이용한 프로그램 개발

실습 #1

  • Map Control과 PostgreSQL에 저장된 공간 데이터와 연동하기!
  • 다음과 같은 네임스페이스를 추가해야함.
using AxMapWinGIS;
using MapWinGIS;
  • MapControl :
    。지도와 Vector , Raster data를 가시화하는 Control
    。도구상자 - [일반] 우클릭 후 항목 선택 - COM 구성 요소 - Map Control 추가 - Form에 올리기.

#1 Map Control에 지도를 표현
Projection(좌표계) , TileProvider(배경맵)을 설정.

// 맵 좌표계 설정
axMap1.Projection = tkMapProjection.PROJECTION_GOOGLE_MERCATOR;
// 배경맵 적용(타일맵) 
axMap1.TileProvider = tkTileProvider.OpenStreetMap;
// 맵 초기 공간범위
axMap1.KnownExtents = tkKnownExtents.keSouth_Korea;
// 맵 커서 모드 변경
axMap1.CursorMode = tkCursorMode.cmPan;

#2 DBMS에서 MapControl에 data를 추가하기.

  • Layer 추가 (AddLayerFromDatabase)
    맵컨트롤.AddLayerFromDatabase(ConnectionString , SQL , 가시화 여부)
  • Layer 이름 설정 (set_LayerName)
    맵컨트롤.set_LayerName(레이어 인덱스, 레이어 이름);
// 레이어 인덱스
int layerHandle;
// ConnectionString (연결문자열) : Npgsql과 다르다!
string strConn = "PG:host=localhost port=5432 dbname=geodb2 user=postgres password=wjd747";
 // DBMS에서 데이터를 가져올 tbl을 생성할 SQL 문
string cmd = "select * from seoul_gu";
// layer 추가
layerHandle = axMap1.AddLayerFromDatabase(strConn, cmd, true);
// layer 이름 설정
axMap1.set_LayerName(layerHandle, "gu");
//데이터 추가 예시
cmd = "select * from seoul_dong";
            layerHandle = axMap1.AddLayerFromDatabase(strConn, cmd, true);
            axMap1.set_LayerName(layerHandle, "dong");
cmd = "select * from station";
            layerHandle = axMap1.AddLayerFromDatabase(strConn, cmd, true);
            axMap1.set_LayerName(layerHandle, "station");
cmd = "select * from link";
            layerHandle = axMap1.AddLayerFromDatabase(strConn, cmd, true);
            axMap1.set_LayerName(layerHandle, "road");
  • Npgsql과 Connection String이 다르다.
  • 이때 배경맵 좌표계는 Google인데 import된 data의 좌표계는 UTM-K이므로 좌표계가 맞지 않는다!
    => MapControl의 ProjectionMisMatch 이벤트를
    생성해서 다음 코드를 추가 시 layer의 좌표계가 다르더라도 구글 좌표계로 적절하게 가시화됨.
private void axMap1_ProjectionMismatch(object sender, … e)
 { 
// 맵에 레이어가 추가될 때마다 reprojection을 실시함(메인 좌표계로의 통일) 
 e.reproject = tkMwBoolean.blnTrue;
 } 

#3 획득한 Layer data를 CheckedListBox에 연결하기.
1. CheckedListBox에 Layer item 넣기.

  • 맵컨트롤.get_LayerName(레이어 인덱스) :
    레이어 이름을 도출
  • CheckedListBox.Items.Count :
    CheckedListBox의 항목 수를 return.
  • CheckedListBox.Items.Add(레이어 이름) :
    CheckedListBox에 layer를 item으로 추가.
  • CheckedListBox.SetItemChecked(인덱스, true)
    실행 시 특정 인덱스의 아이템이 체크된 상태로 실행 / true는 가시화 여부.
// CheckedListBox에 항목 import하기.
// 끝 항목부터 불러오기
for(int i = layerHandle; i >= 0; i--)
{
// 레이어 이름으로 CheckedListBox에 항목 추가
// axMap1.get_LayerName(레이어 인덱스)     
chkLayers.Items.Add(axMap1.get_LayerName(i));
}
  1. CheckedListBox에 따른 각 Layer 별 가시화 여부 연동하기.
  • CheckedListBox의 itemCheck 이벤트 활성화
  • ItemCheck 이벤트
    ItemCheckEventArgs e : 현재 선택된 체크박스
    e.NewValue : 이벤트 이후 체크 여부
    。e.CurrentValue : 이벤트 직전(현재) 체크 여부
  • 맵컨트롤.set_LayerVisible(레이어 인덱스 , 가시화여부(T/F)) :
    레이어 가시화 설정.
  • CheckState.Checked : 체크박스 체크가 된 상태.
private void chkLayers_ItemCheck(object sender, ItemCheckEventArgs e)
{
	// position : 가시화순서
	// item index : CheckedListBox에 추가됨 / Layer Index와 반대 ( LIFO )
	// Layer Index ( = LayerHandle) :
	//
	// Layer Index와 Item Index와는 반대.
	int position = layerHandle - e.Index;
	//  ItemCheckEventArgs e : 현재 선택된 체크박스
	// e.NewValue : 이벤트 이후 체크 여부
	// e.CurrentValue : 이벤트 직전(현재) 체크 여부
	if (e.NewValue == CheckState.Checked)
	{
          // 레이어 가시화 설정
          // 맵컨트롤.set_LayerVisible( Layer Index , 가시화 여부(T/F) )
          axMap1.set_LayerVisible(position, true);
 	}
    else
    {
          axMap1.set_LayerVisible(position, false);
    }
}

#4 레이어 가시화 순서 제어 코드 추가하기

  • 2개의 버튼을 추가하여 가시화 순서를 부여하며 이때 , 맵컨트롤에 레이어가 추가될 때 얻은 인덱스와는 별개임을 인지해야함.
  • layerHandle :
    。Map Control에 layer가 추가된 순서
    。높을 수록 Map Control에 늦게 추가된 layer.
    layerPosition :
    。가시화 순서
    。높을수록 가장 늦게 가시화되는 layer.
    。CheckedListBox의 item index와 완전한 반대관계.
    。layerPosition = 전체 레이어 갯수 - 선택된 item index의 갯수
  • 맵컨트롤.get_LayerHandle(가시화순서) :
    가시화순서에 따른 LayerHandle을 return.
  • 맵컨트롤.MoveLayerUp(가시화순서)
    맵컨트롤.MoveLayerDown(가시화순서)
    현재 가시화순서에 위치한 layer를 한칸 올리거나 내림.
    => 가시화 순서를 제어.
private void chkLayers_ItemCheck(object sender, ItemCheckEventArgs e)
{
	...
    // 레이어 가시화 순서 변경
    // 가시화순서에 따른 LayerIndex 획득
    // LayerIndex = 맵컨트롤.get_LayerHandle(가시화순서)
    int layerIndex = axMap1.get_LayerHandle(position);
    if(e.NewValue == CheckState.Unchecked)
    {
          axMap1.set_LayerVisible(layerIndex, false);
    }
    else
    {
          axMap1.set_LayerVisible(layerIndex, true);
    }
}
private void button1_Click(object sender, EventArgs e)
{
     // layerPosition을 이용해 layer를 찾고 layer의 가시화 순서 조정.
     int position = layerHandle - chkLayers.SelectedIndex;
     // 현재 가시화순서에 위치한 layer를 한칸 올리기.
     axMap1.MoveLayerUp(position);
     // CheckedListBox에서 순서 변경
     MoveItemAtChkLayers(-1);
}
private void button2_Click(object sender, EventArgs e)
{
     int position = layerHandle - chkLayers.SelectedIndex;
     // 현재 가시화순서에 위치한 layer를 한칸 내리기.
     axMap1.MoveLayerDown(position);
     // CheckedListBox에서 순서 변경
     MoveItemAtChkLayers(+1);
}
private void MoveItemAtChkLayers(int iteminsert) 
// iteminsert : 인덱스 증감분(한칸 up or down)
// 입력된 증감분을 고려해서 선택된 item을 새로운 위치로 추가하는 역할 수행.
{
    int source = chkLayers.SelectedIndex;
    // 선택된 item의 값
    string sourceValue = chkLayers.SelectedItem.ToString();
    // 선택된 item의 체크상태
    CheckState sourceChkState = chkLayers.GetItemCheckState(source);
	// 선택된 item 삭제
    chkLayers.Items.RemoveAt(source);
	// 인덱스 증감분 적용하여 새로운 위치에 아이템 추가.
    chkLayers.Items.Insert(source + iteminsert, sourceValue);
    // 선택 상태 유지
    chkLayers.SetSelected(source + iteminsert, true);
    // 체크 상태 유지
    chkLayers.SetItemCheckState(source + iteminsert,sourceChkState);
}

#5 layer의 feature를 선택 후 선택된 feature들의 attribute table을 확인하는 기능 구현.

  • ComboBox -> SelectedIndexChanged 이벤트 활성화 및 기존 for문에서 다음 구문 입력.
// 콤보박스에 아이템 추가
 cbSelectLayer.Items.Add(axMap1.get_LayerName(i));
  • 맵컨트롤.SendSelectBoxFinal = true :
    맵 컨트롤의 선택모드 활성화.
  • 맵컨트롤.CursorMode = tkCursorMode.cmSelection :
    커서모드를 선택모드로 변경.
  • MapControl -> SelectBoxFinal 이벤트 활성화

Npgsql로 import 한 data를 Map Control과 연동

// MapWindow에서 필요한 데이터.
        string strConn2 = "PG:host=localhost port=5432 dbname=geodb2 user=postgres password=wjd747";
        string sql;
        int layerCurrent;
        Shapefile sf;
        Utils utils = new Utils();
        ShapeDrawingOptions opt;
        string guid, selquary;
// 안 좋은 방법 ! 데이터 가져올때마다 db를 연결해야하므로.
private void dgv_gu_SelectionChanged(object sender, EventArgs e)
{
      if (sf != null)  // dgv에서 선택될 때 작동.
      {
          MessageBox.Show(" 선택 중 ");
          guid = dgv_gu.CurrentRow.Cells["gu_id"].Value.ToString();
          axMap1.RemoveAllLayers();
          sql = $"select sigungu_cd as gu_id , sigungu_nm as gu_name , st_transform(geom,4326) geom from gu where sigungu_cd = '{guid}'";
          layerCurrent = axMap1.AddLayerFromDatabase(strConn2, sql, true);
          axMap1.set_LayerName(layerCurrent, "gu");
          axMap1.ZoomToLayer(layerCurrent);
       } //DB에서 동 데이터 가져오고 선택된 동만 가져와서 표현하기        
 }
private void dgv_gu_DoubleClick(object sender, EventArgs e)
{
       sf.SelectNone();
       string err = "";
       object result = "";
       guid = dgv_gu.CurrentRow.Cells["gu_id"].Value.ToString(); // 선택한 행에서 구 id를 가져오는 역할
       selquery = $"[gu_id] =\"{guid}\""; // 열(gu_id)에서 gu id를 얻어오는 역할.
       if (sf.Table.Query(selquary , ref result , ref err))
       {
            int[] shapes = result as int[];
            if (shapes != null)
            {
                for(int i = 0; i < shapes.Length; i++)
                {
                    sf.set_ShapeSelected(shapes[i],true);
                }
                axMap1.ZoomToSelected(layerCurrent);
            }
        }
   }
  • DataTable로 Binding된 ComboBox에서 선택한 항목에 속하는 특정 Column의 정보 가져오기.
var gu_row = cbgu.SelectedItem as DataRowView;
string gu_id = gu_row["gu_id"].ToString();
  • Shape file 생성
    layer의 모든 field를 포함하여 생성됨.
Shapefile shp = 맵컨트롤.get_Shapefile(layerindex);
  • shapeFile.Labels.Generate("[열이름]",라벨위치 , 가시화여부) :
  • layer에 라벨 설정.
shapefile.Labels.Generate("[sigungu_nm]", tkLabelPositioning.lpCentroid,true);
  • layer의 특정 field를 선택하기
shapefile.set_ShapeSelected(index,true);
  • 선택된 layer에 대해 Zoom
맵컨트롤.ZoomToLayer(layerIndex)
  • 선택된 layer의 field에 속하는 polygon에 대해 Zoom
맵컨트롤.ZoomToSelected(layerIndex)

。먼저 shapefile.set_ShapeSelected(index,true);를 통해 선택이 되어야한다.

  • attribute table로 특정 조건을 만족하는 field의 index 가져오기.
string err = "";
object result = "";
string query = $"[gu_id]=\"{guid}\"";
if (sf.Table.Query(query, ref result , ref err))
{
	int[] shapes = result as int[]; 
    // query에 만족하는 shapefile의 특정 field의 index를 가져옴.
    if (shapes != null)
    {
           for(int i=0; i< shapes.Length; i++)
           {
                 sf.set_ShapeSelected(shapes[i], true);
                 // shapefile에서 특정 field의 polygon 선택.
           }
           axMap2.ZoomToSelected(layerHandle_gu);
    }
}

Control 과 BindingSource(DataSource) 연결하기

  • ComboBox
comboBox.DataSource = bs;
comboBox.DisplayMember = "Column 이름"
  • TextBox
textBox.DataBindings.Add("참조될 프로퍼티명: ex)Text",BindingSource,"참조될 tbl Column명")

숫자포맷 지정 시 다음과 같이 추가한다.
"N2" : 소수점 2자리까지 표현.
"C1" : Money(Currency)단위로 소수점 1자리.

textBox.DataBindings.Add("참조될 프로퍼티명: ex)Text",BindingSource,"참조될 tbl Column명",true,DataSourceUpdateMode.Never , "" , "N2")
  • DataGridView
dgv.DataSource = bs;
  • layer 시각화 하기
    맵컨트롤.set_LayerVisible(레이어 인덱스 , 가시화여부(T/F)) :
axMap1.set_LayerVisible(layerHandle_gu, true);
  • 특정 layer로 확대하기
    MapControl.ZoomToLayer(layerIndex);
private void btnFullExtent_Click(object sender, EventArgs e)
{
      axMap1.ZoomToLayer(layerCurrent);
}
  • 특정 layer의 field로 확대하기
shp_gu.set_ShapeSelected(shapes[0], true);
axMap1.ZoomToSelected(layerHandle_gu);

Utils , ShapeDrawingOptions : MapControl에 표현되는 Shapefile의 layer 꾸미기

  • ShapeDrawingOptions opt =shapeFile.DefaultDrawingOptions
    。opt.LineWidth : 선 굵기
    。opt.FillColor : 색 설정
    。opt.FillTransparency : 투명도 설정
  • shapeFile.Labels.Generate("[열이름]",라벨위치 , 가시화여부) :
  • layer에 라벨 설정.
Utils utils = new Utils();
ShapeDrawingOptions opt;
opt = sf.DefaultDrawingOptions;
opt.LineWidth = 2;
opt.FillColor = utils.ColorByName(tkMapColor.Aqua);
opt.FillTransparency = 50;
// Show [gu_name]
sf.Labels.Generate("[column_name]",tkLabelPositioning.lpCentroid , true);

MapControl의 layer에 표현되는 Field 선택해서 AttributeTable에 표현하기.

  • 선택할 버튼을 만들고 다음 구문을 입력.
    。Mapcontrol.SendSelectBoxFinal = true
    행동이 MapControl의 SelectBoㅌ에게 반영되도록 하는 기능.
  • 마우스 커서를 선택,해제 모드로 설정하기.
private void btnSelect_Click(object sender, EventArgs e)
        {
            // button을 눌렀을 때 MapControl의 Selectbox에 반영이 되도록 하는 기능
            axMap1.SendSelectBoxFinal = true;

            // 커서모드를 Selection으로 설정.
            axMap1.CursorMode = tkCursorMode.cmSelection;
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            // 반영 해제하기.
            axMap1.SendSelectBoxFinal = false;
            axMap1.CursorMode = tkCursorMode.cmPan;
            sf.SelectNone();
            dgv_Select.DataSource = null;
        }
  • 마우스로 드래그할 공간범위 설정 및 객체 선택 기능 구현
		private void axMap1_SelectBoxFinal(object sender, _DMapEvents_SelectBoxFinalEvent e)
        {
            Shapefile sf=new Shapefile();
            switch (layerCurrent)
            {
                case 0: // gu
                    sf = shp_gu;
                    break;
                case 1: // gu
                    sf = shp_dong;
                    break;
                case 2: // gu
                    sf = shp_oa;
                    break;
            }
            if (sf != null)
            {
                object result = null;

                // 마우스 선택 범위 초기화
                double left = 0.0, top = 0.0, bottom = 0.0, right = 0.0;

                // e로 전달된 값(pixel)을 좌표계 변환 후 기존 left , top 변수에 할당
                axMap1.PixelToProj(e.left, e.top, ref left, ref top);
                // e로 전달된 값(pixel)을 좌표계 변환 후 기존 left , top 변수에 할당
                axMap1.PixelToProj(e.right, e.bottom, ref right, ref bottom);

                // 공간 범위 객체 생성
                Extents ext = new Extents();

                // 공간 범위 설정 ( x min , y min , z min , x max , y max , z max ) : 2차원이므로 z값은 0.0 할당
                ext.SetBounds(left, bottom, 0.0, right, top, 0.0);
                sf.SelectNone();

                // 선택된 공간범위 내에서 intersection에 해당하는 shape의 layer의 field에 대해 result로 반환.
                if(sf.SelectShapes(ext, 0.0 , SelectMode.INTERSECTION , ref result))
                {
                    int[] shapes = result as int[];
                    if(shapes == null) { return; }
                    for (int i = 0; i < shapes.Length; i++)
                    {
                        sf.set_ShapeSelected(shapes[i], true);
                    }
                    // 선택된 feature들을 dgv에 표현하기.
                    DisplaySelection();
                }
                axMap1.Redraw();
            }
        }
  • shapefile에서 선택된 객체에 대해 DataGridView로 표현하기.

    DataTable로 표현하여 DataColumn과 DataRow를 생성 후 DataTable로 넣어서 이를 Dgv로 전달.

    private void DisplaySelection(int[] shapes)
           {
               // dgv에 선택된 feature들의 정보를 표시하기 위한 메소드
    
               // 선택된 feature를 담을 빈 테이블 생성
               DataTable dt = new DataTable();
    
               // Column 생성 후 dt에 추가
               for (int i = 0; i < sf.NumFields; i++) // shapefile에서 공간범위로 선택된 fields의 수를 가져오기.
               {
                   DataColumn dc = new DataColumn();
                   dc.ColumnName = sf.Table.Field[i].Name; // 열의 이름을 선택된 field(column)의 이름으로 설정.
                   dt.Columns.Add(dc); // 기존에 구축한 DataTable에 열 추가.
               }
    
               // row 생성 후 dt에 추가
               for (int i = 0; i < shapes.Length; i++)
               {
                   DataRow dr = dt.NewRow(); // dt로 새 row 생성
                   for (int j = 0; j < sf.NumFields; j++)
                   {
                       string val = sf.get_CellValue(j, shapes[i]).ToString();
                       dr[j] = val; // 하나의 row의 각 column의 value값을 DataRow로 문자열로 순서대로 넣기.
                   }
                   dt.Rows.Add(dr); // roof 하면서 하나의 row를 계속 각각 input . row가 끝날때 까지 반복.
               }
               // DataGridView에 DataTable 표현
               dgv_Select.DataSource = dt;
           }

    전체적인 완성본

    요구사항 :

  • 해당행의 자식이 하위에 보여진다. (BindingSource 이용)

  • 하위의 행의 속성값이 집계되어 표시된다. (예: 구를 선택하면, 해당 구의 총 면적, 총 인구, 총 동의 갯수 등, 동을 선택하면 해당 동의 총 면적, 총 인구수, 총 집계구 갯수 등)

  • 해당 행이 axMap 캔버스에 보여진다. (예: 동대문구를 선택하면 해당 구가 선택되거나 해당 extent로 확대)

  • 현재 맵에서 특정 폴리곤(구, 동, 집계구)을 선택하면 다른 dgv로 표현됨.




    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using AxMapWinGIS;
    using MapWinGIS;
    using Npgsql;
    namespace EX01
    {
      public partial class Form1 : Form
      {
          string strConn1 = "Host=localhost; port=5432; Username=postgres;" +
                  "Password=wjd747; Database = seouldb";
          string strConn2 = "PG:host=localhost port=5432 dbname=geodb2 user=postgres password=wjd747";
          string cmd2;
          string strSQL;
    
          NpgsqlConnection conn;
          NpgsqlCommand comm;
          NpgsqlDataAdapter adp;
          DataSet ds;
          DataTable tblgu;
          DataTable tbldong;
          DataTable tbloa;
          DataRelation gu_dong;
          DataRelation dong_oa;
          BindingSource bsgu;
          BindingSource bsdong;
          BindingSource bsoa;
    
          // layer index
          int layerHandle_gu;
          int layerHandle_dong;
          int layerHandle_oa;
          int layerCurrent;
    
          Shapefile shp_gu;
          Shapefile shp_dong;
          Shapefile shp_oa;
          Shapefile sf;
    
          // 기타 변수
          string err;
          object result;
    
          public Form1()
          {
              InitializeComponent();
          }
    
          private void Form1_Load(object sender, EventArgs e)
          {
              // 맵 좌표계 설정
              axMap1.Projection = tkMapProjection.PROJECTION_GOOGLE_MERCATOR;
              // 배경맵 적용(타일맵) 
              axMap1.TileProvider = tkTileProvider.OpenStreetMap;
              // 맵 초기 공간범위
              axMap1.KnownExtents = tkKnownExtents.keSouth_Korea;
              // 맵 커서 모드 변경
              axMap1.CursorMode = tkCursorMode.cmPan;
              TableLoad();
              MapLoad();
          }
    
          private void TableLoad()
          {
              // 연결개체
              conn = new NpgsqlConnection(strConn1);
              strSQL = "select gu_id , sigungu_nm from seoul_gu";
              comm = conn.CreateCommand();
              comm.CommandText = strSQL;
              adp = new NpgsqlDataAdapter();
              ds = new DataSet();
              
              // DataSet에서 table로 data 넣기.
              adp.SelectCommand = comm;
              adp.Fill(ds, "gu");
              tblgu = ds.Tables["gu"];
              comm.CommandText = "select dong_id , gu_id , adm_nm from seoul_dong";
              adp.Fill(ds, "dong");
              tbldong = ds.Tables["dong"];
              comm.CommandText = "select id, dong_id , tot_reg_cd , pop , area from seoul_oa";
              adp.Fill(ds, "oa");
              tbloa = ds.Tables["oa"];
              
              // DataRelation
              gu_dong = new DataRelation("gu_dong", tblgu.Columns["gu_id"], tbldong.Columns["gu_id"]);
              ds.Relations.Add(gu_dong);
              dong_oa = new DataRelation("dong_oa", tbldong.Columns["dong_id"], tbloa.Columns["dong_id"]);
              ds.Relations.Add(dong_oa);
              
              // 연산에 필요한 열 삽입하기.
              addColumns();
              
              // BindingSource
              bsgu = new BindingSource();
              bsdong = new BindingSource();
              bsoa = new BindingSource();
              bsgu.DataSource = tblgu;
              bsdong.DataSource = bsgu;
              bsdong.DataMember = "gu_dong";
              bsoa.DataSource = bsdong;
              bsoa.DataMember = "dong_oa";
              
              // Control과 BindingSource 연결
              cb_gu.DataSource = bsgu;
              // ComboBox에서 표시할 field 설정.
              cb_gu.DisplayMember = "sigungu_nm";
              dgv_dong.DataSource = bsdong;
              dgv_oa.DataSource = bsoa;
              tbCountOa.DataBindings.Add("Text", bsdong, "oaCount"); 
              tbDongPop.DataBindings.Add("Text", bsdong, "dongPop");
              tbDongArea.DataBindings.Add("Text", bsdong, "dongArea", true, DataSourceUpdateMode.Never, "", "N2"); // 숫자포맷
              tbCountDong.DataBindings.Add("Text", bsgu, "dongCount");
              tbGuPop.DataBindings.Add("Text", bsgu, "guPop");
              tbGuArea.DataBindings.Add("Text", bsgu, "guArea",true , DataSourceUpdateMode.Never,"","N2"); // 숫자포맷
          }
    
          private void addColumns()
          {
              DataColumn oaCount = new DataColumn();
              oaCount.ColumnName = "oaCount";
              oaCount.DataType = typeof(int);
              oaCount.Expression = "Count(Child(dong_oa).id)";
              tbldong.Columns.Add(oaCount);
    
              DataColumn dongPop = new DataColumn();
              dongPop.ColumnName = "dongPop";
              dongPop.DataType = typeof(int);
              dongPop.Expression = "Sum(Child(dong_oa).pop)";
              tbldong.Columns.Add(dongPop);
    
              DataColumn dongArea = new DataColumn();
              dongArea.ColumnName = "dongArea";
              dongArea.DataType = typeof(double);
              dongArea.Expression = "Sum(Child(dong_oa).area)";
              tbldong.Columns.Add(dongArea);
    
              DataColumn dongCount = new DataColumn();
              dongCount.ColumnName = "dongCount";
              dongCount.DataType = typeof(int);
              dongCount.Expression = "Count(Child(gu_dong).dong_id)";
              tblgu.Columns.Add(dongCount);
    
              DataColumn guPop = new DataColumn();
              guPop.ColumnName = "guPop";
              guPop.DataType = typeof(int);
              guPop.Expression = "Sum(Child(gu_dong).dongPop)";
              tblgu.Columns.Add(guPop);
    
              DataColumn guArea = new DataColumn();
              guArea.ColumnName = "guArea";
              guArea.DataType = typeof(double);
              guArea.Expression = "Sum(Child(gu_dong).dongArea)";
              tblgu.Columns.Add(guArea);
          }
    
          private void MapLoad()
          {
              cmd2 = "select gu_id , sigungu_nm , st_transform(geom,4326) as geom from seoul_gu";
              layerHandle_gu = axMap1.AddLayerFromDatabase(strConn2, cmd2, false);
              axMap1.set_LayerName(layerHandle_gu, "gu");
    
              cmd2 = "select cast(adm_dr_cd as int) as dong_id ,left(adm_dr_cd , 5) as gu_id , adm_dr_nm, st_transform(geom,4326) as geom from dong";
              layerHandle_dong = axMap1.AddLayerFromDatabase(strConn2, cmd2, false);
              axMap1.set_LayerName(layerHandle_dong, "dong");
    
              cmd2 = "select gid as id ,adm_cd as dong_id,st_transform(geom,4326) as geom from oa";
              layerHandle_oa = axMap1.AddLayerFromDatabase(strConn2, cmd2, false);
              axMap1.set_LayerName(layerHandle_oa, "oa");
    
              // Shapefile 생성
              shp_gu = axMap1.get_Shapefile(layerHandle_gu);
              shp_dong = axMap1.get_Shapefile(layerHandle_dong);
              shp_oa = axMap1.get_Shapefile(layerHandle_oa);
    
              // Shapefile 라벨 설정
              shp_gu.Labels.Generate("[sigungu_nm]", tkLabelPositioning.lpCentroid,true);
              shp_dong.Labels.Generate("[adm_dr_nm]", tkLabelPositioning.lpCentroid, true);
              shp_oa.Labels.Generate("[id]", tkLabelPositioning.lpCentroid, true);
    
              // Shapefile Drawing Option 추가하기
              shpDrawing(ref shp_gu,"gu");
              shpDrawing(ref shp_dong, "dong");
              shpDrawing(ref shp_oa, "oa");
          }
    
          private void shpDrawing(ref Shapefile shp, string v)
          {
              ShapeDrawingOptions opt = shp.DefaultDrawingOptions;
              Utils util = new Utils();
              switch (v)
              {
                  case "gu":
                      opt.LineWidth = 2;
                      opt.FillColor = util.ColorByName(tkMapColor.Aqua);
                      opt.FillTransparency = 70;
                      break;
                  case "dong":
                      opt.LineWidth = 2;
                      opt.FillColor = util.ColorByName(tkMapColor.FloralWhite);
                      opt.FillTransparency = 70;
                      break;
                  case "oa":
                      opt.LineWidth = 2;
                      opt.FillColor = util.ColorByName(tkMapColor.RosyBrown);
                      opt.FillTransparency = 70;
                      break;
              }
          }
    
          private void cb_gu_SelectedIndexChanged(object sender, EventArgs e)
          {
              // 필요한 변수 초기화 및 layer 시각화.
              init_visibletLayer("gu");
    
              // 초기화 내용
              // layerCurrent = layerHandle_gu;
              // string err = "";
              // object result = "";
              // shp_gu.SelectNone();
    
              // DataTable로 Binding된 ComboBox에서 선택한 인덱스의 Column의 정보 가져오기. DataRowView임!
              var gu_row = cb_gu.SelectedItem as DataRowView;
              // 특정 layer 시각화하기
              string gu_id = gu_row["gu_id"].ToString();
              // 선택된 shp 파일의 Layer의 특정 Field와 기존 DataTable의 특정 Field를 조회하는 쿼리문.
    
              // gu_id 의 type은 반드시 integer 여야 한다! 형변환 일치.
              string query = $"[gu_id]={gu_id}";
              fieldSelection(ref shp_gu , query,layerHandle_gu);
          }
    
          private void dgv_dong_DoubleClick(object sender, EventArgs e)
          {
              // 필요한 변수 초기화 및 layer 시각화.
              init_visibletLayer("dong");
    
              string dong_id = dgv_dong.CurrentRow.Cells["dong_id"].Value.ToString();
              // gu_id 의 type은 반드시 integer 여야 한다! 형변환 일치.
              string query = $"[dong_id]={dong_id}";
              fieldSelection(ref shp_dong , query, layerHandle_dong);
          }
    
          private void dgv_oa_DoubleClick(object sender, EventArgs e)
          {
              // 필요한 변수 초기화 및 layer 시각화.
              init_visibletLayer("oa");
              string id = dgv_oa.CurrentRow.Cells["id"].Value.ToString();
              string query = $"[id]={id}";
              fieldSelection(ref shp_oa ,query, layerHandle_oa);
          }
    
          private void init_visibletLayer(string v)
          {
              // 변수 초기화
              err = "";
              result = "";
              
              // 레이어 시각화
              axMap1.set_LayerVisible(layerHandle_gu, false);
              axMap1.set_LayerVisible(layerHandle_dong, false);
              axMap1.set_LayerVisible(layerHandle_oa, false);
              switch (v)
              {
                  case "init":
                      break;
                  case "gu":
                      axMap1.set_LayerVisible(layerHandle_gu, true);
                      // layerCurrent : full extent button 용
                      layerCurrent = layerHandle_gu;
                      shp_gu.SelectNone();
                      break;
                  case "dong":
                      axMap1.set_LayerVisible(layerHandle_dong, true);
                      layerCurrent = layerHandle_dong;
                      shp_dong.SelectNone();
                      break;
                  case "oa":
                      axMap1.set_LayerVisible(layerHandle_oa, true);
                      layerCurrent = layerHandle_oa;
                      shp_oa.SelectNone();
                      break;
              }
          }
    
          private void fieldSelection(ref Shapefile shp ,string query, int layerHandle)
          {
              if (shp.Table.Query(query, ref result, ref err))
              {
                  int[] shapes = result as int[];
                  if (shapes != null)
                  {
                      for (int i = 0; i < shapes.Length; i++)
                      {
                          shp.set_ShapeSelected(shapes[i], true);
                      }
                      axMap1.ZoomToSelected(layerHandle);
                  }
              }
          }
    
          private void axMap1_ProjectionMismatch(object sender, _DMapEvents_ProjectionMismatchEvent e)
          {
              e.reproject = tkMwBoolean.blnTrue;
          }
    
          private void btnExtent_Click(object sender, EventArgs e)
          {
              axMap1.ZoomToLayer(layerCurrent);
          }
    
          private void btnSelect_Click(object sender, EventArgs e)
          {
              // button을 눌렀을 때 MapControl의 Selectbox에 반영이 되도록 하는 기능
              axMap1.SendSelectBoxFinal = true;
    
              // 커서모드를 Selection으로 설정.
              axMap1.CursorMode = tkCursorMode.cmSelection;
          }
    
          private void btnClear_Click(object sender, EventArgs e)
          {
              // 반영 해제하기.
              axMap1.SendSelectBoxFinal = false;
              axMap1.CursorMode = tkCursorMode.cmPan;
              sf.SelectNone();
              dgv_Select.DataSource = null;
              axMap1.ZoomToLayer(layerCurrent);
              init_visibletLayer("init");
          }
    
          private void axMap1_SelectBoxFinal(object sender, _DMapEvents_SelectBoxFinalEvent e)
          {
              sf=new Shapefile();
              switch (layerCurrent)
              {
                  case 0: // gu
                      sf = shp_gu;
                      break;
                  case 1: // gu
                      sf = shp_dong;
                      break;
                  case 2: // gu
                      sf = shp_oa;
                      break;
              }
              if (sf != null)
              {
                  object result = null;
    
                  // 마우스 선택 범위 초기화
                  double left = 0.0, top = 0.0, bottom = 0.0, right = 0.0;
    
                  // e로 전달된 값(pixel)을 좌표계 변환 후 기존 left , top 변수에 할당
                  axMap1.PixelToProj(e.left, e.top, ref left, ref top);
                  // e로 전달된 값(pixel)을 좌표계 변환 후 기존 left , top 변수에 할당
                  axMap1.PixelToProj(e.right, e.bottom, ref right, ref bottom);
    
                  // 공간 범위 객체 생성
                  Extents ext = new Extents();
    
                  // 공간 범위 설정 ( x min , y min , z min , x max , y max , z max ) : 2차원이므로 z값은 0.0 할당
                  ext.SetBounds(left, bottom, 0.0, right, top, 0.0);
                  sf.SelectNone();
    
                  // 선택된 공간범위 내에서 intersection에 해당하는 shape의 layer의 field에 대해 result로 반환.
                  if(sf.SelectShapes(ext, 0.0 , SelectMode.INTERSECTION , ref result))
                  {
                      int[] shapes = result as int[];
                      if(shapes == null) { return; }
                      for (int i = 0; i < shapes.Length; i++)
                      {
                          sf.set_ShapeSelected(shapes[i], true); // shapefile의 layer의 선택된 field 지정하기.
                      }
                      // 선택된 feature들을 dgv에 표현하기.
                      DisplaySelection(shapes);
                  }
                  axMap1.Redraw();
              }
          }
    
          private void DisplaySelection(int[] shapes)
          {
              // dgv에 선택된 feature들의 정보를 표시하기 위한 메소드
    
              // 선택된 feature를 담을 빈 테이블 생성
              DataTable dt = new DataTable();
    
              // Column 생성 후 dt에 추가
              for (int i = 0; i < sf.NumFields; i++) // shapefile에서 공간범위로 선택된 fields의 수를 가져오기.
              {
                  DataColumn dc = new DataColumn();
                  dc.ColumnName = sf.Table.Field[i].Name; // 열의 이름을 선택된 field(column)의 이름으로 설정.
                  dt.Columns.Add(dc); // 기존에 구축한 DataTable에 열 추가.
              }
    
              // row 생성 후 dt에 추가
              for (int i = 0; i < shapes.Length; i++)
              {
                  DataRow dr = dt.NewRow(); // dt로 새 row 생성
                  for (int j = 0; j < sf.NumFields; j++)
                  {
                      string val = sf.get_CellValue(j, shapes[i]).ToString();
                      dr[j] = val; // 하나의 row의 각 column의 value값을 DataRow로 문자열로 순서대로 넣기.
                  }
                  dt.Rows.Add(dr); // roof 하면서 하나의 row를 계속 각각 input . row가 끝날때 까지 반복.
              }
              // DataGridView에 DataTable 표현
              dgv_Select.DataSource = dt;
          }
    
      }
    }
    
profile
공부기록 블로그

0개의 댓글