2011년 9월 20일 화요일

[ArcObjects] Feature를 X, Y Offset만큼 이동하는 VBA 스크립트

▣ 미션
 - ArcGIS를 사용하여 아래 그림과 같이 레이어의 모든 피쳐를 X 축의 방향으로 dX 만큼, Y 축의 방향으로 dY 만큼 이동하고 싶습니다.


▣ ArcObjects ITransform2D.Move Method
Transform2D Move Example

▣ VBA Script
 - Feature의 Geometry를 X, Y Offset만큼 이동
 - Undo/Redo가 가능하도록 Editor를 사용
 - 따라서 정상적으로 편집이 되었을 경우 Editor 툴바에서 저장할 것
Option Explicit

Sub MoveGeometry()
    ' get map
    Dim ipDoc As IMxDocument
    Dim focusMap As IMap
    
    Set ipDoc = ThisDocument
    Set focusMap = ipDoc.focusMap
        
    ' get featurelayer
    Dim featureLayer As IFeatureLayer
    Set featureLayer = focusMap.Layer(0)
    ' start editing
    Dim pID As New esriSystem.UID
    pID.Value = "esriEditor.Editor"
        
    Dim editor As IEditor
    Set editor = Application.FindExtensionByCLSID(pID)
    If (editor.EditState <> esriStateEditing) Then
        Dim dataset As IDataset
        Set dataset = featureLayer
        editor.StartEditing dataset.Workspace
    End If
    
    ' start edit operation
    editor.StartOperation
               
    ' set parameters
    Dim dX As Double, dY As Double    
    dX = 5000 ' map unit
    dY = 5000 ' map unit
     
    Dim totalCnt As Long, step As Long
    Dim featureCursor As IFeatureCursor, feature As IFeature, geometry As IGeometry
    
    totalCnt = featureLayer.featureClass.FeatureCount(Nothing)
    Set featureCursor = featureLayer.Search(Nothing, False)
    Set feature = featureCursor.NextFeature
    Do Until feature Is Nothing
        DoEvents
        step = step + 1
        Application.StatusBar.Message(0) = step & " / " & totalCnt & " processed..."
        
        Set geometry = feature.ShapeCopy
        
        ' moves the geometry dX units along the X-Axis and dY units along the Y-Axis
        Dim transform2D  As ITransform2D
        Set transform2D = geometry
        transform2D.Move dX, dY
        
        ' update geometry
        Set feature.Shape = transform2D
        feature.Store
        
        Set feature = featureCursor.NextFeature
    Loop
    
    ' cleanup
    Set featureCursor = Nothing
    
    ' stop edit operation
    editor.StopOperation featureLayer.Name & " Move Operation"
    
    ' refresh map
    Dim activeView As IActiveView
    Set activeView = focusMap
    activeView.Refresh
    
    MsgBox "지도 확인 후 Editor 툴바에서 저장해야 합니다."
End Sub
▣ 실행결과

2011년 9월 8일 목요일

[ArcObjects] ArcObjects에서 필드의 통계정보 계산하기

ArcObjects에서 필드의 통계정보를 계산하는 IDataStatistics라는 인터페이스가 있습니다.

아래 C# 코드는 다음과 같습니다.
 - IDataStatistics 인터페이스를 이용하여 하나의 필드를 계산
 - ICursor 인터페이스를 이용하여 하나의 필드를 계산
 - ICursor 인터페이스를 이용하여 여러 필드를 계산

▣ ArcObjects  IDataStatistics

▣ Code Snippet

using System;
using System.Diagnostics;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.esriSystem;
using System.Collections.Generic;

○  IDataStatistics 인터페이스를 이용하여 하나의 필드를 계산
public static void DataStatistics() {
    IFeatureClass featureClass = OpenShapefile(shapefilePath);
    string fieldName = "POP2008";

    IQueryFilter queryFilter = new QueryFilterClass();
    queryFilter.SubFields = fieldName;

    ICursor cursor = (ICursor)featureClass.Search(queryFilter, true);

    IDataStatistics dataStatistics = new DataStatisticsClass();
    dataStatistics.Field = fieldName;
    dataStatistics.Cursor = cursor;

    // -1 means pull all records
    dataStatistics.SampleRate = -1; 

    IStatisticsResults sr = dataStatistics.Statistics;

    Debug.WriteLine(string.Format("Count = {0}", sr.Count));
    Debug.WriteLine(string.Format("Minimum = {0}", sr.Minimum));
    Debug.WriteLine(string.Format("Maximum = {0}", sr.Maximum));
    Debug.WriteLine(string.Format("Sum = {0}", sr.Sum));
    Debug.WriteLine(string.Format("Mean = {0}", sr.Mean));
    Debug.WriteLine(string.Format("Variance = {0}", Math.Pow(sr.StandardDeviation, 2)));
    Debug.WriteLine(string.Format("Standard Deviation = {0}", sr.StandardDeviation));

    System.Runtime.InteropServices.Marshal.ReleaseComObject(cursor);
}

○  ICursor 인터페이스를 이용하여 하나의 필드를 계산
public static void DataStatistics2() {
    IFeatureClass featureClass = OpenShapefile(shapefilePath);
    string fieldName = "POP2008";

    int fieldID = featureClass.FindField(fieldName);
    double minVal = double.MaxValue;
    double maxVal = double.MinValue;
    double sumOfVals = 0;
    double sumOfSqrs = 0;

    int rowCount = 0;

    IQueryFilter queryFilter = new QueryFilterClass();
    queryFilter.SubFields = fieldName;

    ICursor cursor = (ICursor)featureClass.Search(queryFilter, true);
    IRow row = cursor.NextRow();
    while (row != null) {
        rowCount++;

        double val = Convert.ToDouble(row.get_Value(fieldID));

        minVal = Math.Min(minVal, val);
        maxVal = Math.Max(maxVal, val);
        sumOfVals += val;
        sumOfSqrs += Math.Pow(val, 2.0);

        row = cursor.NextRow();
    }
    System.Runtime.InteropServices.Marshal.ReleaseComObject(cursor);

    Debug.WriteLine(string.Format("Count = {0}", rowCount));
    Debug.WriteLine(string.Format("Minimum = {0}", minVal));
    Debug.WriteLine(string.Format("Maximum = {0}", maxVal));
    Debug.WriteLine(string.Format("Sum = {0}", sumOfVals));
    Debug.WriteLine(string.Format("Mean = {0}", sumOfVals / rowCount));

    double variance = (sumOfSqrs - Math.Pow(sumOfVals, 2) / rowCount) / (rowCount - 1);
    Debug.WriteLine(string.Format("Variance = {0}", variance));
    Debug.WriteLine(string.Format("Standard Deviation = {0}", Math.Sqrt(variance)));
}

○  ICursor 인터페이스를 이용하여 여러 필드를 계산
public static void DataStatistics3() {
    IFeatureClass featureClass = OpenShapefile(shapefilePath);
    string[] fieldNames = { "POP2008", "POP2009", "POP2010" };

    int fieldCount = fieldNames.Length;

    int[] fieldID = new int[fieldCount];
    double[] minVal = new double[fieldCount];
    double[] maxVal = new double[fieldCount];
    double[] sumOfVals = new double[fieldCount];
    double[] sumOfSqrs = new double[fieldCount];
    
    for (int k = 0; k < fieldCount; k++) {
        fieldID[k] = featureClass.FindField(fieldNames[k]);
        minVal[k] = double.MaxValue;
        maxVal[k] = double.MinValue;
        sumOfVals[k] = 0;
        sumOfSqrs[k] = 0;
    }

    int rowCount = 0;
    IQueryFilter queryFilter = new QueryFilterClass();
    queryFilter.SubFields = string.Join(",", fieldNames);

    ICursor cursor = (ICursor)featureClass.Search(queryFilter, true);
    IRow row = cursor.NextRow();
    while (row != null) {
        rowCount++;
        for (int k = 0; k < fieldCount; k++) {
            double val = Convert.ToDouble(row.get_Value(fieldID[k]));

            minVal[k] = Math.Min(minVal[k], val);
            maxVal[k] = Math.Max(maxVal[k], val);
            sumOfVals[k] += val;
            sumOfSqrs[k] += Math.Pow(val, 2.0);
        }
        row = cursor.NextRow();
    }
    System.Runtime.InteropServices.Marshal.ReleaseComObject(cursor);
    
    for (int k = 0; k < fieldCount; k++) {
        Debug.WriteLine(string.Format("Field Name = {0}", fieldNames[k]));
        Debug.WriteLine(string.Format("Count = {0}", rowCount));
        Debug.WriteLine(string.Format("Minimum = {0}", minVal[k]));
        Debug.WriteLine(string.Format("Maximum = {0}", maxVal[k]));
        Debug.WriteLine(string.Format("Sum = {0}", sumOfVals[k]));
        Debug.WriteLine(string.Format("Mean = {0}", sumOfVals[k] / rowCount)); 

        double variance = (sumOfSqrs[k] - Math.Pow(sumOfVals[k], 2) / rowCount) / (rowCount - 1);
        Debug.WriteLine(string.Format("Variance = {0}", variance));
        Debug.WriteLine(string.Format("Standard Deviation = {0}", Math.Sqrt(variance)));
    }
}

2011년 9월 5일 월요일

다양한 포맷을 지원하는 .NET C#용 GIS Viewer 프로그램 만들기

간단한 GIS Viewer 프로그램을 하나 만들면서 진행한 과정입니다.

▣ 미션
 - WPS 결과물(Vector, Raster, Geometry)을 시각화하는 프로그램
 - Vector = Shaepfile, GML2, GML3 등
 - Raster = GeoTiff, Jpeg2000 등
 - Geometry = GML, WKT, WKB, GeoJSON, KML 등

▣ 요구사항
 - Visual Studio 2008 C# .NET Framework 2/3.5 환경
 - WPS 결과물로 리턴되는 다양한 GIS 포맷을 읽을 수 있어야 함
 - 이미 만들어진 프로그램에 Map Control 2개만 들어가야 함 - Shapefile, Grid, Graphic 등을 사용자가 설정한 스타일에 맞게 렌더링
 - 자체 뷰어를 만들기보다 오픈소스나 무료로 사용가능한 라이브러리 검토

▣ 오픈소스 및 라이브러리 검토
 ○ 검토대상 라이브러리
    - MapWindow6
    - SharpMap V2 
    - 김형준 GIS 연구소 (for Developers) - DuraMap-Xr

 ○ 포맷변환
 - GDAL/OGR CSharp bindings

 ○ 검토결과
 - MapWindow6, SharpMap 등은 .NET Framework4에서만 운영되며기본 포맷 외 확장 포맷 변환은 GDAL/OGR 라이브러리 사용 - 대상에서 제외
 - 포맷 변환은 GDAL/OGR 사용
 - DuraMap-Xr은 COM 기술로 제작되어 .NET Framework 제약을 받지 않음
 - DuraMap-Xr은 Shapefile, Graphic(Geometry), GeoTiff 등을 직접 읽을 수 있음
 - DuraMap-Xr의 GridMap Layer를 이용하여 래스터 픽셀단위의 읽고 쓰기가 가능하며 스타일 설정이 간단함
--> DuraMap-Xr 사용 결정

▣ DuraMap-Xr 적용
 ○ GeoTiff Raster와 GML Point
 - GML Point 레이어를 Raster Kernel Density 알고리즘을 이용하여 분석

 ○ GML Geometry와 GML Polygon
 - Point, Polygon Geometry를 Buffer 후 원본 Point, Polygon, Buffered Polygon 및 버텍스를  화면에 출력