[GIS] 자바 API를 이용한 ArcSDE 9.2 연동

환경은 ArcSDE 9.2이고..  ArcSDE 서비스 팩은 설치하지 않았으며 오라클은 10g 버전입니다. 그리고 ArcSDE에 대한 자바 API는 ArcSDE 9.2 서비스팩6에 해당합니다. 참고로 자바 API를 구성하는 jar 파일은 다음과 같으며 모두 반드시.. 필요합니다.

  • jsde92_sdk.jar
  • jpe92_sdk.jar
  • icu4j_3_2.jar

네.. 이제 ArcSDE와 연동하기 위한 자바 API에 대해 살펴보겠습니다.. 먼저 연결에 대한 코드입니다..

import java.util.*;
import com.esri.sde.sdk.client.*;

public class MainEntry {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        SeConnection conn = null;
        String server = "192.168.0.18";
        int service = 5151;
        String database = "";
        String user = "na_gis";
        String password = "na_gis";

        try {
            conn = new SeConnection(server, service, database, user, password);
            Vector layerList = conn.getLayers();
            System.out.println(layerList.size() + " layers existed... ");

            for(int i=0; i

위의 코드는 연결뿐만 아니라 연결된 ArcSDE 서버가 관리하고 있는 레이어 개수와 이름을 화면에 출력합니다.

다음은 레이어를 구성하는 필드 정보를 얻어 오는 코드입니다.

import java.util.*;
import com.esri.sde.sdk.client.*;

public class MainEntry {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        SeConnection conn = null;
        String server = "192.168.0.18";
        int service = 5151;
        String database = "";
        String user = "na_gis";
        String password = "na_gis";

        try {
            conn = new SeConnection(server, service, database, user, password);

            Vector tableList = conn.getTables(SeDefs.SE_SELECT_PRIVILEGE);
            System.out.println(tableList.size() + " table existed... ");
   
            for(int i=0; i

필드 정보는 레이어 개념이 아닌 테이블 개념으로 가져와야 합니다. 레이어는 테이블 + 도형 데이터 쯤으로.. 생각하면 됩니다.

다음은 테이블의 특정 필드(들)의 값을 얻어오는 코드입니다.

import java.util.*;
import com.esri.sde.sdk.client.*;

public class MainEntry {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        SeConnection conn = null;
        String server = "192.168.0.18";
        int service = 5151;
        String database = "";
        String user = "na_gis";
        String password = "na_gis";

        try {
            conn = new SeConnection(server, service, database, user, password);
            Vector tableList = conn.getTables(SeDefs.SE_SELECT_PRIVILEGE);

            SeTable tbl = (SeTable)tableList.get(89);

            SeSqlConstruct sqlConstruct = 
                new SeSqlConstruct(tbl.getQualifiedName());

            String[] cols = new String[1];
            cols[0] = "IST_YMD";

            SeQuery query = new SeQuery(conn, cols, sqlConstruct);
            query.prepareQuery();
            query.execute();

            SeRow row;
            while((row = query.fetch()) != null)
            {
                String value = row.getString(0);
                System.out.println(value);
            }
        } catch(SeException e) {
            e.printStackTrace();
        }
    }
}

얻어온 테이블 리스트(Vector tableList)에서 인덱스 번호 89에 해당하는 테이블의 IST_YMD 필드에 대한 값을 뽑아 내 화면에 표시합니다. 만약 SQL의 Where 구문을 지정하고자 한다면 SeSqlConstruct sqlConstruct 객체의 매서드 setWhere에 구문을 지정하면 됩니다. 예를 들면.. 만약 필드 ADM_NAM에 대해서 '목'으로 시작하는 조건의 경우는 다음과 같습니다.

    ....

    sqlConstruct.setWhere("ADM_NAM LIKE '목%'");

    ....

다음은 레이어에서 도형에 대한 좌표를 표시하는 코드입니다.

import java.util.*;
import com.esri.sde.sdk.client.*;

public class MainEntry {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        SeConnection conn = null;
        String server = "192.168.0.18";
        int service = 5151;
        String database = "";
        String user = "na_gis";
        String password = "na_gis";

        try {
            conn = new SeConnection(server, service, database, user, password);
            Vector layerList = conn.getLayers();

            SeLayer lyr = (SeLayer)layerList.get(4);
            SeSqlConstruct sqlConstruct =
                new SeSqlConstruct(lyr.getQualifiedName());

            String[] cols = new String[3];
            cols[0] = "BD_NM";
            cols[1] = "BD_MGT_SN";
            cols[2] = "SHAPE";

            SeQuery query = new SeQuery(conn, cols, sqlConstruct);
            query.prepareQuery();
            query.execute();

            SeRow row;
            while((row = query.fetch()) != null)
            {
                SeShape shp = row.getShape(2);

                double coordinates[][][] = shp.getAllCoords();
                System.out.println("coordinates.length = " + coordinates.length);
                System.out.println("coordinates[0].length = " + coordinates[0].length);
                System.out.println("coordinates[0][0].length = " + coordinates[0][0].length);

                double partCoords[] = coordinates[0][0];
                for(int i=0; i

얻어온 레이어 리스트(Vector layerList)에서 인덱스 4번에 해당하는 레이어에 대해서 필드 BD_NM, BD_MGT_SN 그리고 좌표에 대한 값이 담긴 SHPAE 필드를 지정해 쿼리를 실행합니다. 위의 코드는 지정된 레이어에 대한 모든 레코드를 읽어 오게 됩니다. 여기에 제한을 두어 지정된 MBR에 포함되거나 교차하는 레코드만을 얻어오는 코드는 다음과 같습니다.

import java.util.*;
import com.esri.sde.sdk.client.*; public class MainEntry {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        SeConnection conn = null;
        String server = "192.168.0.18";
        int service = 5151;
        String database = "";
        String user = "na_gis";
        String password = "na_gis";

        try {
            conn = new SeConnection(server, service, database, user, password);
            Vector layerList = conn.getLayers();

            SeLayer lyr = (SeLayer)layerList.get(4);
            SeSqlConstruct sqlConstruct = 
                new SeSqlConstruct(lyr.getQualifiedName());

            String[] cols = new String[3];
            cols[0] = "BD_NM";
            cols[1] = "BD_MGT_SN";
            cols[2] = "SHAPE";

            SeQuery query = new SeQuery(conn, cols, sqlConstruct);
            query.prepareQuery();

            // Add New Codes
            SeShape filterShape = new SeShape(lyr.getCoordRef());
            SeExtent ext = new SeExtent(148574, 146403, 148587, 146412);
            filterShape.generateRectangle(ext); 

            SeShapeFilter filter = new SeShapeFilter(lyr.getQualifiedName(), 
                lyr.getSpatialColumn(), filterShape, SeShapeFilter.METHOD_AI);

            SeShapeFilter[] filters = new SeShapeFilter[1];
            filters[0] = filter;

            query.setSpatialConstraints(SeQuery.SE_SPATIAL_FIRST, 
                false, filters);
            // At Here!

            query.execute();
   
            SeRow row;
            while((row = query.fetch()) != null)
            {
                SeShape shp = row.getShape(2);

                double coordinates[][][] = shp.getAllCoords();
                System.out.println(
                    "coordinates.length = " + coordinates.length);
                System.out.println(
                    "coordinates[0].length = " + coordinates[0].length);
                System.out.println(
                    "coordinates[0][0].length = " + coordinates[0][0].length);

                double partCoords[] = coordinates[0][0];
                for(int i=0; i

코드 번호 29~42에 해당하는 새로운 코드가 추가되었습니다.

완전하 SELECT 구문에 대한 SQL 문으로 Row을 읽어 오는 코드는 다음과 같습니다.

import java.util.*;   
import com.esri.sde.sdk.client.*; 

public class TestArcSDEQuery {
    public static void main(String[] args) {
        SeConnection conn = null;   
        String server = "222.237.78.208";   
        int service = 5151;   
        String database = "";   
        String user = "na_gis";   
        String password = "na_gis";   

        try {
            conn = new SeConnection(server, service, database, user, password); 
         
            SeQuery query = new SeQuery(conn);   
            query.prepareSql("SELECT * FROM CC_META");
            query.execute();   
  
            SeRow row;
            while((row = query.fetch()) != null)   
            {
                int cntFields = query.getNumColumns();
                SeColumnDefinition[] tableDef = row.getColumns();
             
                for(int iField=0; iField
	

[GIS] DuraMap-Xr, 특정 레이어를 최상단으로 이동시키기

특정 레이어의 이름을 gl이라고 할때.. 이 gl이라는 이름의 레이어를 최상단에 위치시키는 코드입니다.

String lastLayerName = XrMain.Layers.GetLayerName(XrMain.Layers.Count - 1);
if(lastLayerName.CompareTo("gl") != 0)
{
    XrMain.Layers.MoveLayer("gl", lastLayerName, false);
    XrMain.Update();
}

가장 먼저 최상단에 위치하는 레이어를 구하고.. 최상단의 레이어가 gl이 아닐 경우에 4번 코드의 MoveLayer 매서드를 통해 최상단으로 이동시켜 주면 됩니다. MoveLayer의 세번째 인자값을 false로 주었는데.. 이동시킬 위치를 기준으로 앞으로 이동시킬 것인지.. 뒤로 이동시킬 것인지에 대한 값입니다. 여기서는 false로 주었으므로 뒤쪽으로 이동시킨다는 의미입니다.

잠시 듀라맵을 이용하여 작업하던차에… 정리해 봅니다..

[GIS] DuraMap-Xr, 편집 Event 정리

듀라맵에서 그래픽 레이어나 SHP 파일의 도형을 편집할때.. 편집에 대한 흐름을 보다 융통성있게 처리하기 위해 다음과 같은 이벤트를 제공합니다.

  • OnEditAddedRow – 마우스를 통해 새로운 도형이 그려져 레이어에 추가된 직후 발생하는 이벤트
  • OnEditBeforeDeleteRows – 선택된 도형(들)을 DEL 키를 눌러 삭제하기 직전에 발생하는 이벤트
  • OnEditDeletedRows – 선택된 도형을 DEL 키를 눌러 삭제된 직후 발생하는 이벤트
  • OnEditModifiedRows – 선택된 도형(들)이 편집된 직후 발생되는 이벤트
  • OnEditSelectionChanging – 마우스를 통해 도형(들)이 새롭게 선택된 직후 발생하는 이벤트
  • OnEditSelectionChanged – 마우스를 통해 도형(들)을 새롭게 선택하기 직전에 발생하는 이벤트

편집에 대해서 총 6개의 이벤트를 제공합니다. 언급된 순서대로 하나 하나 예제 코드를 통해 간단히 살펴보도록 하겠습니다.

먼저 OnEditAddedRow의 이벤트에 대한 C# 코드는 다음과 같습니다.

private void Xr_OnEditAddedRow(object sender,
    XrMapLib._IXrMapControlEvents_OnEditAddedRowEvent e)
{
    int fid = e.fID;

    ....
}

이벤트 매서드의 인자로 넘겨진 XrMapLib._IXrMapControlEvents_OnEditAddedRowEvent e에는 fID라는, 새롭게 추가된 도형에 대한 ID 값이 담겨있습니다.

그리고 다음은 OnEditBeforeDeleteRows에 대해 알아보겠습니다. 이 이벤트는 도형을 삭제하기 전에 정말 삭제할지의 여부를 사용자에게 묻기 위한 용도로 사용될 수 있습니다. 코드 예입니다.

private void Xr_OnEditBeforeDeleteRow(object sender, EventArgs e)
{
    DialogResult DR = MessageBox.Show(
        "Are you sure that selected POIs be deleted?", "Delete POI",       
        MessageBoxButtons.YesNoCancel);
    Xr.Edit.AllowDeleteByKeyboard = (DR == DialogResult.Yes);
}

먼저 메세지 상자를 통해 선택된 도형을 지울 것인지를 묻습니다. 사용자가 Yes가 아닌 버튼을 누르면 Xr의 Edit 프로퍼티인 AllowDeleteByKeyboard를 false로 지정하여 삭제하지 않습니다.

다음은 OnEditDeletedRows 이벤트입니다. 이 이벤트는 선택한 도형을 DEL 키를 눌러 실제로 삭제된 직후에 발생하는 이벤트이며 도형이 삭제된 후에 어떤 일을 해야 한다면 이 이벤트에서 처리해 주면 됩니다.

그리고 OnEditModifiedRows입니다. 선택된 도형(들)을 이동하거나 새로운 정점을 추가하거나 삭제할때 발생하는 코드입니다. 코드 예는 다음과 같습니다.

private void Xr_OnEditModifiedRows(object sender, EventArgs e)
{
    ValueList id = Xr.Edit.GetSelectedRows();
    if (id.Count > 0)
    {
        ....
    }
}

위의 코드는 도형이 편집된 직후 어떤 도형이 편집되었는지를 파악하기 위해서 Xr.Edit의 GetSelectedRows 매서드를 통해 ID의 목록을 얻어올 수 있습니다. 도형이 편집된 직후 무언가를 처리해야 한다면 바로 이 이벤트에서 처리해주면 됩니다.

다음으로 OnEditSelectionChanging 입니다. 이 이벤트는 사용자가 선택된 도형에 대한 선택 상태를 변경하기 직전에 발생하는 코드로써.. 예를 들어 A라는 도형이 선택되었을때.. 아니면 선택된 도형이 없는 상태에서 B라는 다른 도형을 선택하거나 A라는 도형의 선택을 해제했을때 발생합니다. 이 이벤트에 대한 응용예는 선택된 도형을 DEL 키를 통해 삭제했을때 어떤 도형을 삭제했는지를 파악할때 사용할 수 있는 이벤트입니다.

끝으로 OnEditSelectionChanged 이벤트입니다. 이 이벤트는 사용자가 도형을 선택하거나 선택 상태를 해제한 직후에 발생하는 이벤트입니다. 사용 예제 코드는 다음과 같습니다.

private void Xr_OnEditSelectionChanged(object sender, EventArgs e)
{
    ValueList vl = Xr.Edit.GetSelectedRows();
    if (vl.Count > 0)
    {
        ....
    }
}

사용자가 도형을 선택한 후 어떤 일을 수행해야 할때 이 이벤트를 사용할 수 있습니다.

[ActionScript] 서버로 Binary 데이터 전송

액션스크립트에서 바이너리 데이터를 서버로 전송하는 코드입니다. 물론… 서버 측에서 바이너리 데이터를 받아 들이는 서비스를 제공해야겠지요.. 바이너리 데이터 전송이므로.. 방식은 POST를 사용합니다.. 액션스크립트 코드는 다음과 같습니다..

protected function button1_clickHandler(event:MouseEvent):void
{
    var loader:URLLoader = new URLLoader();
    var request:URLRequest = new URLRequest("http://127.0.0.1:8080/Xr?edChkLog");

    var bytes:ByteArray = new ByteArray();
    bytes.writeDouble(3.14);
    bytes.writeInt(34321);
    bytes.position = 0;
   
    request.data = bytes;
    request.method = URLRequestMethod.POST;
    request.contentType = "application/octet-stream";

    loader.dataFormat = URLLoaderDataFormat.BINARY;
    loader.addEventListener(Event.COMPLETE, onCompleted);
    loader.addEventListener(IOErrorEvent.IO_ERROR, onError);

    loader.load(request);
}

바이너리 데이터는 ByteArray 타입에 저장되며.. 원하는 데이터를 write 매서드 군으로 저장시킵니다. 9번 코드와 15번 코드가 꼭 필요한지는 제거해서 확인해 보시기 바랍니다..

서버측은.. 저 같은 경우  자바의 서블릿을 사용했는데… POST 방식으로 요청을 받으므로 doPost 매서드를 오버라이드해서 데이터를 읽어오면 됩니다.. 바이너리 데이터를 쉽게 읽기 위해 DataInputStream을 사용했는데.. 이해를 돕고자.. 관련된 코드를 제시한다면 다음과 같습니다..

    ....

    ServletInputStream in = req.getInputStream();
    System.out.println("readable size: " + in.available());

    DataInputStream dis = new DataInputStream(in);
    System.out.println(dis.readDouble() + " , " + dis.readInt());

    ....

req는 서블릿의 요청에 대한 HttpServletRequest 객체입니다.. 화면상에 읽을 수 있는 바이너리의 바이트 수와 클라이언트가 보낸 실수값(3.14)와 정수값(34321)이 찍히면 정상입니다..

플래시의 액션스크립트를 이용해 서버측으로부터 바이너리 데이터를 읽을 수도.. 또.. 전송할 수 있도 있음으로 퍼포먼스를 향상 시킬 수 있겠습니다..

[ActionScript] 사용자 정의 이벤트 추가하기

추가해야할 이벤트는 많으면서 또 짐작하기 어렵고.. 시간이 없으니.. 일단 정리를 하여.. 추후 까막까막해져 혼란이 없으면 하는 바램에서 사용자 정의 이벤트를 추가하는 방법에 대해 정리를 해봅니다..

먼저 이벤트 처리 함수(이벤트 핸들러 함수)로 전달될 객체에 대한 클래스를 정의합니다.. 아래는 예입니다..

package geoservice.events
{
    import flash.events.Event;
    import flash.events.MouseEvent;

    public class XrMapMouseEvent extends Event
    {
        public static const XRMAP_MOUSE_DOWN_EVENT:String 
                = "mapMouseDownEvent";
        public static const XRMAP_MOUSE_UP_EVENT:String 
                = "mapMouseUpEvent";
        public static const XRMAP_MOUSE_CLICK_EVENT:String 
                = "mapMouseClickEvent";
        public static const XRMAP_MOUSE_DBLCLICK_EVENT:String 
                = "mapMouseDblClickEvent";
        public static const XRMAP_MOUSE_MOVE_EVENT:String 
                = "mapMouseMoveEvent";
  
        public var mapX:Number;
        public var mapY:Number;
        public var mouseEvent:MouseEvent;
  
        public function XrMapMouseEvent(type:String, mouseEvent:MouseEvent, 
            mapX:Number, mapY:Number, bubbles:Boolean=false, 
            cancelable:Boolean=false)
        {
            super(type, bubbles, cancelable);

            this.mapX = mapX;
            this.mapY = mapY;
            this.mouseEvent = mouseEvent;
        }

        override public function clone():Event
        {
            return new XrMapMouseEvent(type, mouseEvent, mapX, mapY, 
                bubbles, cancelable);
        }
    }
}

하나의 이벤트 클래스에서 총 5개 종류의 이벤트를 책임지고 있습니다.. 만약 클래스 하나가 단 하나의 이벤트만을 책임진다면 생성자에서 type 인자는 필요치 않고 super를 호출할때 첫번째 인자 를 바로 값으로 지정해 주면 됩니다.

다음으로 이벤트를 발생시켜 주는 코드입니다..

parent.dispatchEvent(
    new XrMapMouseEvent(XrMapMouseEvent.XRMAP_MOUSE_DOWN_EVENT, e,
        coordMapper.V2W_X(mouseX), coordMapper.V2W_Y(mouseY)
    )
);

이벤트가 발생되는 적절한 위치에 위의 코드가 존재하면 되며.. 위의 코드는 XRMAP_MOUSE_DOWN_EVENT라는 한 종류의 이벤트를 발생시키는 코드입니다..

이제 마크업(MXML)쪽에서 이벤트를 인식하도록 하기 위한 메타 태그의 정의를 추가합니다..

[Event(name="mapMouseDownEvent", type="geoservice.events.XrMapMouseEvent")]
[Event(name="mapMouseUpEvent", type="geoservice.events.XrMapMouseEvent")]
[Event(name="mapMouseClickEvent", type="geoservice.events.XrMapMouseEvent")]
[Event(name="mapMouseDblClickEvent", type="geoservice.events.XrMapMouseEvent")]
[Event(name="mapMouseMoveEvent", type="geoservice.events.XrMapMouseEvent")]

public class XrMap extends UIComponent
{
    ....

여기까지가 사용자 정의 이벤트를 추가하기 위한 전부이고.. 다음은 이 사용하는 쪽의 코드입니다.. addEventListener를 사용해 이벤트를 추가해도 되고.. 아니면 마크업쪽에서 이벤트를 추가해도 됩니다..

마크업 쪽의 방법만을 살펴보면 다음과 같습니다.. 흔히 일반적인 컴포넌트에 대한 이벤트를 정의하는 것과 동일합니다..

      mapMouseDownEvent="map_mapMouseDownEventHandler(event)"
     mapMouseClickEvent="map_mapMouseClickEventHandler(event)"
     mapMouseDblClickEvent="map_mapMouseDblClickEventHandler(event)"
     mapMouseMoveEvent="map_mapMouseMoveEventHandler(event)"
     mapMouseUpEvent="map_mapMouseUpEventHandler(event)"/>