관련 지식 정리
- 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를 이용한 프로그램 개발
실습 #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)); }
- 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;
}
}
}