GIS Developer, 공간정보시스템 개발 블로그 -
   GIS Developer, 공간정보시스템 개발 블로그  
Front Page
Notice | E-Mail | Admin | Write Article   
 
2016/06/25 09:46 2016/06/25 09:46
[BlackPoint-Xr] 퍼펙트 튜토리얼 - 4 : 속성 확인하기(Identify Attributes)

공간 데이터는 실세계 좌표를 갖는 공간 데이터, 그리고 이러한 공간 데이터와 1:1로 관계를 가지는 속성 데이터를 구성됩니다. 이 튜토리얼에서는 화면에 표시된 도형을 사용자가 터치했을 때 터치한 도형에 대한 속성 데이터를 사용자에게 제공하는 기능에 대해 설명합니다. 이 설명을 통해 수치지도에서 속성 데이터에 대한 정보를 파악하기 위한 블랙포인트의 API를 설명 드리겠습니다.

이 튜토리얼은 이전 튜토리얼에서 작성된 코드를 시작으로 새로운 코드가 추가됩니다.

먼저 화면에 표시된 수치지도 레이어에 대한 도형을 클릭했을 때의 이벤트에 대한 리스너를 맵뷰에 추가해야 합니다. LayersLoadingTask 클래스의 doInBackground 함수의 코드 중 기존의 setLabels(); 코드 밑에 새로운 setShowingAttributeEvent() 함수를 아래처럼 호출합니다.

@Override
protected Void doInBackground(Void... args) {
    String ess = Environment.getExternalStorageState();
 
    if(ess.equals(Environment.MEDIA_MOUNTED)) {
        .
        .
        .

        setSymbol();
        setLabel();
        setShowingAttributeEvent();
    }

    return null;
}

setShowingAttributeEvent() 함수는 다음과 같습니다.

private void setShowingAttributeEvent() {
    ShapeLayer lyr = (ShapeLayer)map.layers().getLayerByName("건물");
    lyr.setKeepShownFID(true);

    map.setOnTapUpListener(new MapOnTapUpEventListener(map));
}

ID로써 이름이 '건물'인 레이어 객체를 얻어오고, 이 레이어 객체에 대해서 setKeepShowingFID 함수를 인자값 true를 주어 호출합니다. 이 호출을 통해 화면 상에 표시되는 '건물' 레이어에 대한 도형들의 ID 값들이 캐쉬되어 빠르게 터치된 도형에 대한 ID 값을 얻을 수 있습니다. 이 함수를 호출하지 않아도 터치된 도형의 ID 값을 얻을 수 있지만, 속도를 위해 이렇게 하였습니다. 단, 이 함수의 호출을 통해 화면에 표시된 도형만큼 정해진 메모리를 사용하게 되므로, 메모리에 대한 문제 발생에 따라 호출 여부를 개발자가 결정해야 합니다. 그리고 지도뷰의 setOnTapUpListener 함수를 통해 지도뷰에 대한 Tab Up 이벤트 리스너를 설정합니다. Tap Up은 사용자가 화면을 터치하고 손가락을 뗐을때 호출되는 이벤트입니다. 이 이벤트에 대한 처리는 새로운 클래스인 MapOnTapUpEventListener에서 처리하며 다음과 같은 코드를 갖습니다.

public class MapOnTapUpEventListener implements OnTapUpEventListener {
    private XrMap map = null;

    public MapOnTapUpEventListener(XrMap map) {
        this.map = map;
    }

    @Override
    public void onTapUp(MotionEvent event) {
        // ...    
    }
}

생성자를 통해 지도뷰 객체를 전달해 변수에 저장해 두고 있습니다. 그리고 실제 Tap Up 이벤트에 대한 코드는 아래처럼 입력합니다.

boolean bDrawing = map.getRendererManager().isWorking();
map.getRendererManager().waitForDrawing(true);

String layerName = "건물";
ShapeLayer layer = (ShapeLayer)map.layers().getLayerByName(layerName);
if(layer != null) {
    PointD touchPt = new PointD(event.getX(), event.getY());
    PointD coord = map.getCoordMapper().V2W(touchPt);

    try {
        Vector<Integer> fids = layer.getFIDsByMapCoord(coord);
        if(fids != null) {
            int cntFIDs = fids.size();
            ShapeAccess sa = (ShapeAccess)layer.getAccess();
            AttributeRowSet ars = sa.getAttributeRowSet();

            for(int iFID=0; iFID<cntFIDs; ++iFID) {
                int FID = fids.get(iFID);

                if(sa.beginLoading()) {
                    AttributeRow ar = sa.loadAttributeById(FID);
                    StringBuilder msg = new StringBuilder();
                    FieldSet fs = ars.getFieldSet();
                    int cntFields = fs.getFieldsCount();

                    for(int iField=0; iField<cntFields; ++iField) {
                        Field field = fs.getField(iField);
                        String fieldName = field.getFieldName();
                        String fieldValue = ar.getValueAsString(iField);

                        msg.append(fieldName);
                        msg.append(": ");
                        msg.append(fieldValue);
                        msg.append("\n");
                    }

                    sa.unload(ar);
                    sa.endLoading();

                    new AlertDialog.Builder(map.getContext())
                        .setTitle("지번조회 결과(" + (iFID+1) + "/" + cntFIDs + ")")
                        .setMessage(msg)
                        .setPositiveButton("확인", 
                             new DialogInterface.OnClickListener() {
                                 public void onClick(DialogInterface dialog, int whichButton) {}
                             }).show();
                }
            }

            if(bDrawing) {
                map.update();
            }

            return;
        }
    } catch(IOException e) {
        e.printStackTrace();
    }
}

new AlertDialog.Builder(map.getContext())
    .setTitle("지번조회 결과")
    .setMessage("검색 결과가 없습니다.")
    .setPositiveButton("확 인",
        new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {}
        }
    ).show();

if(bDrawing) {
    map.update();
}

1번 코드는 현재 지도뷰에서 지도를 화면에 그리고(Drawing) 있는 중인지의 여부를 저장합니다. 맵뷰의 지도 그리기는 별도의 스레드를 통해 실행됩니다. 2번 코드는 현재 지도 그리기 스레드의 실행을 중지시킵니다. 정확히 언급하면 waitForDrawing 함수는 지도 그리기 스레드가 완료될때까지 대기하라는 함수이지만 인자값으로 true를 주면 즉시 종료하라는 의미가 됩니다. 4~6번 코드는 이름이 '건물'인 레이어 객체를 얻어옵니다. 7번 코드와 8번 코드는 사용자가 화면에 터치한 지도 좌표값을 얻어와 coord 변수에 저장합니다. 11번 코드는 사용자가 터치한 지도 좌표를 통해 해당 지도 좌표에 존재하는 도형의 ID 값들을 얻어오는 레이어 객체에 대한 getFIDsByMapCoord 함수의 호출입니다. 12번 코드는 사용자가 터치한 좌표에 도형이 존재할 경우에 대한 if 문입니다. 13번은 사용자가 터치한 곳에 존재하는 도형의 개수를 얻어옵니다. 참고로 사용자가 터치한 곳에 여러개의 도형 데이터가 있을 경우도 있습니다. 14번 코드는 건물 레이어에 대한 도형과 속성 정보에 접근할 수 있는 ShapeAccess 객체를 얻습니다. 그리고 이 ShapeAccess를 통해 속성 정보가 저장되는 AttributeRowSet 객체를 얻습니다. 17번 코드는 사용자가 터치한 도형의 개수만큼 코드를 실행하는 반복문입니다. 이 반복문을 통해 터치된 도형의 ID 값을 18번 코드를 통해 얻습니다. 그리고 20번에서 beginLoading 함수를 반드시 호출해 데이터를 읽을 준비를 합니다. 21번 코드는 터치한 도형의 ID 값에 대한 AttributeRow 객체를 얻는데, 이 객체를 통해 실제 속성값을 읽을 수 있습니다. 23번 코드는 속성값의 필드 이름을 얻기 위해 FieldSet 객체를 얻고 24번 코드에서 이 객체를 통해 필드의 개수를 얻습니다. 26~35번까지는 각 필드에 대한 실제 값들을 얻어와 문자열로 구성하는 코드입니다. 37번과 38번은 메모리에 적재한 속성 데이터를 바로 해제해주는 코드입니다. 40번 코드는 앞서 구성한 속성값들의 문자열을 대화상자를 통해 표시해 줍니다.그리고 50번~52번은 앞서 지도가 그려지는 중이였다면 다시 지도를 그리는 스레드를 실행해 줍니다. 61번~68번은 사용자가 터치한 곳에 어떠한 도형도 없을 경우에 표시되는 대화상자입니다.

이제 실행해서 지도 화면을 확대하고 화면 상에 표시된 건물을 터치하면 다음과 같은 결과를 볼 수 있습니다.

2016/06/21 23:35 2016/06/21 23:35
[BlackPoint-Xr] 퍼펙트 튜토리얼 - 4 : 텍스트 라벨 설정하기

이 글은 이전 튜토리얼에서 작성된 코드를 이용하여, 각 ShapeLayer에 대해 속성값을 라벨로 표시하는 블랙포인트의 API에 대해 설명합니다.

텍스트 라벨을 설정하는 코드는 레이어를 모두 맵뷰에 추가한 이후가 적당합니다. 이는 앞 튜토리얼에서 레이어의 심벌을 설정하는 코드의 위치와 동일합니다. 즉, 라벨을 설정하는 함수를 setLabel로 만들고 이 함수를 레이어를 추가하는 LayersLoadingTask 클래스의 doInBackground 함수안에서 호출하면 됩니다. 더 정확하게는 아래의 코드와 같이 기존의 doInBackground 함수 안의 코드 중 setSymbol() 함수 호출 바로 다음입니다.

@Override
protected Void doInBackground(Void... args) {
    String ess = Environment.getExternalStorageState();

    if(ess.equals(Environment.MEDIA_MOUNTED)) {
        
        .
        .
        .
        
        setSymbol();
        setLabel();
    }

    return null;
}

이제 setLabel 함수를 살펴보겠습니다. 아래의 코드는 setLabe 함수에 대한 코드입니다.

private void setLabel() {
    // 시 라벨 정의
    ShapeLayer lyrTL_SCCO_SIG =  (ShapeLayer)map.layers().getLayerByName("시");
    ShapeLayerLabel lblTL_SCCO_SIG = (ShapeLayerLabel)lyrTL_SCCO_SIG.getLabel();
    lblTL_SCCO_SIG.setEnable(true);
    lblTL_SCCO_SIG.setFieldName("SIG_KOR_NM");

    SimpleDrawLabelTheme themeTL_SCCO_SIG = 
        (SimpleDrawLabelTheme)lblTL_SCCO_SIG.getTheme();
    FontSymbol fsTL_SCCO_SIG = themeTL_SCCO_SIG.getFontSymbol();
    fsTL_SCCO_SIG.setTextSize(70);
    fsTL_SCCO_SIG.setTextColor(Color.WHITE);
    fsTL_SCCO_SIG.setOutlineColor(Color.BLACK);

    Visibility vsbTL_SCCO_SIG = lblTL_SCCO_SIG.getVisibility();
    vsbTL_SCCO_SIG.setVisibleByScale(true);
    vsbTL_SCCO_SIG.setFromScale(180000);
    vsbTL_SCCO_SIG.setToScale(Double.MAX_VALUE);

    // 동 라벨 정의
    ShapeLayer lyrTL_SCCO_EMD =  (ShapeLayer)map.layers().getLayerByName("동");
    ShapeLayerLabel lblTL_SCCO_EMD = (ShapeLayerLabel)lyrTL_SCCO_EMD.getLabel();
    lblTL_SCCO_EMD.setEnable(true);
    lblTL_SCCO_EMD.setFieldName("EMD_KOR_NM");

    SimpleDrawLabelTheme themeTL_SCCO_EMD = 
        (SimpleDrawLabelTheme)lblTL_SCCO_EMD.getTheme();
    FontSymbol fsTL_SCCO_EMD = themeTL_SCCO_EMD.getFontSymbol();
    fsTL_SCCO_EMD.setTextSize(55);
    fsTL_SCCO_EMD.setTextColor(Color.WHITE);
    fsTL_SCCO_EMD.setOutlineColor(Color.BLACK);

    Visibility vsbTL_SCCO_EMD = lblTL_SCCO_EMD.getVisibility();
    vsbTL_SCCO_EMD.setVisibleByScale(true);
    vsbTL_SCCO_EMD.setFromScale(0);
    vsbTL_SCCO_EMD.setToScale(180000);

    // 리 라벨 정의
    ShapeLayer lyrTL_SCCO_LI =  (ShapeLayer)map.layers().getLayerByName("리");
    ShapeLayerLabel lblTL_SCCO_LI = (ShapeLayerLabel)lyrTL_SCCO_LI.getLabel();
    lblTL_SCCO_LI.setEnable(true);
    lblTL_SCCO_LI.setFieldName("LI_KOR_NM");

    SimpleDrawLabelTheme themeTL_SCCO_LI = 
        (SimpleDrawLabelTheme)lblTL_SCCO_LI.getTheme();
    FontSymbol fsTL_SCCO_LI = themeTL_SCCO_LI.getFontSymbol();
    fsTL_SCCO_LI.setTextSize(40);
    fsTL_SCCO_LI.setTextColor(Color.WHITE);
    fsTL_SCCO_LI.setOutlineColor(Color.BLACK);

    Visibility vsbTL_SCCO_LI = lblTL_SCCO_LI.getVisibility();
    vsbTL_SCCO_LI.setVisibleByScale(true);
    vsbTL_SCCO_LI.setFromScale(0);
    vsbTL_SCCO_LI.setToScale(100000);

    // 건물 라벨 정의
    ShapeLayer lyrTL_SPBD_BULD = (ShapeLayer)map.layers().getLayerByName("건물");
    ShapeLayerLabel lblTL_SPBD_BULD = (ShapeLayerLabel)lyrTL_SPBD_BULD.getLabel();
    lblTL_SPBD_BULD.setEnable(true);
    lblTL_SPBD_BULD.setFieldName("BULD_NM");

    SimpleDrawLabelTheme themeTL_SPBD_BULD = 
        (SimpleDrawLabelTheme)lblTL_SPBD_BULD.getTheme();
    FontSymbol fsTL_SPBD_BULD = themeTL_SPBD_BULD.getFontSymbol();
    fsTL_SPBD_BULD.setTextSize(45);
    fsTL_SPBD_BULD.setTextColor(Color.WHITE);
    fsTL_SPBD_BULD.setOutlineColor(Color.BLACK);

    Visibility vsbTL_SPBD_BULD = lblTL_SPBD_BULD.getVisibility();
    vsbTL_SPBD_BULD.setVisibleByScale(true);
    vsbTL_SPBD_BULD.setFromScale(0);
    vsbTL_SPBD_BULD.setToScale(25000);

    // 도로중심선 라벨 정의
    ShapeLayer lyrTL_SPRD_MANAGE = 
        (ShapeLayer)map.layers().getLayerByName("도로중심선");
    ShapeLayerLabel lblTL_SPRD_MANAGE = 
        (ShapeLayerLabel)lyrTL_SPRD_MANAGE.getLabel();
    lblTL_SPRD_MANAGE.setEnable(true);
    lblTL_SPRD_MANAGE.setFieldName("RN");

    SimpleDrawLabelTheme themeTL_SPRD_MANAGE = 
        (SimpleDrawLabelTheme)lblTL_SPRD_MANAGE.getTheme();
    FontSymbol fsTL_SPRD_MANAGE = themeTL_SPRD_MANAGE.getFontSymbol();
    fsTL_SPRD_MANAGE.setTextSize(43);
    fsTL_SPRD_MANAGE.setTextColor(Color.BLACK);
    fsTL_SPRD_MANAGE.setOutlineColor(Color.WHITE);

    Visibility vsbTL_SPRD_MANAGE = lblTL_SPRD_MANAGE.getVisibility();
    vsbTL_SPRD_MANAGE.setVisibleByScale(true);
    vsbTL_SPRD_MANAGE.setFromScale(0);
    vsbTL_SPRD_MANAGE.setToScale(5000);
}

상당히 긴 코드입니다. 라벨을 설정하고 있는 레이어는 모두 5개입니다. 각 5개에 대한 라벨을 설정하는 코드에 대한 패턴이 동일합니다. 즉, 라벨 텍스트 값으로 사용할 필드값을 설정하고, 라벨을 활성화하고, 라벨 텍스트 폰트에 대한 크기와 색상을 설정합니다. 또한 라벨이 표시되는 축척 레벨을 설정할 수 있습니다. 5개의 레이어에 대한 라벨 설정 방식은 동일하므로 '시'에 대한 라벨 설정 코드만을 살펴보겠습니다. 3번 코드는 레이어를 추가할때 각 레이어에 고유하게 부여한 ID값인 '시' 문자열 값으로 ShapeLayer 객체를 얻습니다. 이렇게 얻는 ShapeLayer 객체에 대한 라벨 객체를 얻는 코드가 4번입니다. 이 라벨 객체를 이용해 5번 코드에서 라벨 기능을 활성화 하고, 6번에서 라벨값을 얻기 위해 사용할 필드명을 지정합니다. 그리고 라벨 텍스트의 크기와 색상을 지정하기 위한 코드는 8번~13번입니다. 끝으로 라벨의 표시될 축척 범위를 지정하기 위한 코드는 15번~18번 코드까지입니다. 이제 실행해 보면 다음과 같은 화면을 볼 수 있습니다.

BLOG main image
GIS Developer, 김형준 / (주)지오서비스 서비스개발팀 팀장 / '모바일 3D 그래픽스' 번역 및 출판 / '모바일 GIS 프로그래밍' 집필 및 출판
 Notice
 Category
전체 (759)
GIS 개발 (240)
공간DB 공유 (3)
프로그래밍 (298)
스치는 생각들 (146)
번역 또는 집필 (4)
 TAGS
GIS Xr OpenGL Shader BlackPoint FingerEyes C++ Algorithm Android Java DuraMap Map Engine WPF ActionScript C# ArcObjects ArcGIS 안드로이드 FingerEyes-Xr BlackPoint-Xr JavaScript HTML5 WPF 3D template Flex DuraMap-Xr OrangeMap Pattern Service .NET Graphic OpenGL ES XML Mr.Tiler-Xr OOD Mobile GIS XGE WebService Design GPS GIS Korea PSP KASS WKT PostGIS GeoService iOS 모바일 GIS Edit Web
 Recent Entries
[BlackPoint-Xr] 퍼펙트 ...
[BlackPoint-Xr] 퍼펙트 ...
[BlackPoint-Xr] 퍼펙트 ...
[BlackPoint-Xr] 퍼펙트 ...
[BlackPoint-Xr] 퍼펙트 ...
 Archive
2016/06
2016/05
2016/04
 Visitor Statistics
Total : 4166405
Today : 525
Yesterday : 939
태터툴즈 배너
rss