FingerEyes-Xr에서 여러 개의 속성값 합쳐 라벨 표시하기

FingerEyes-Xr에서 수치지도에 대한 라벨을 표시할 때, 일반적으로 하나의 필드명을 지정해 지정된 필드값을 라벨로 표시합니다. 아래는 어떤 수치지도의 first_nv_n 필드를 라벨로 지정해 지도를 표시하는 코드(코드A)의 예입니다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />

    <style>
        body {
            margin: 0px;
            padding: 0px;
        }

        #map {
            top: 0px;
            left: 0px;
            position: absolute;
            width: 100%;
            height: 100%;
            border: none;
            outline: none;
        }
    </style>

    <script src="../../scripts/fingereyes-xr/Xr.js"></script>

    <script>
        var map = null;

        function onLoad() {
            map = new Xr.Map("map", {});

            var lyr = new Xr.layers.ShapeMapLayer("lyr",
                {
                    url: "http://168.192.76.10:8080/Xr?layerName=li_a@test"
                }
            );
            
            var theme = lyr.theme();
            theme.penSymbol().color("black").width(4);
            theme.brushSymbol().color("lightgray");

            // Label Setting
            var label = lyr.label();

            label.enable(true);
            label.formatter().fieldName("first_nv_n");
            label.theme().symbol().size(40).strokeWidth(5);
            // .


            var lm = map.layers();
            lm.add(lyr);

            map.onLayersAllReady(onLayersReady);
            window.addEventListener("resize", onResize);
        }

        var bFinishResizing = true;

        function onResize() {
            if (bFinishResizing) {
                bFinishResizing = false;

                setTimeout(function () {
                    var newWidth = window.innerWidth;
                    var newHeight = window.innerHeight;

                    map.resize(newWidth, newHeight);
                    map.update();

                    bFinishResizing = true;
                }, 500);                
            }
        }

        function onLayersReady() {
            var cm = map.coordMapper();
            var lyr = map.layers("lyr");
            var mbr = lyr.MBR();

            cm.zoomByMBR(mbr);

            map.update();
        }
    </script>

    <title></title>
</head>

<body onload="onLoad()">
    <div id="map" />
</body>
</html>

위의 코드를 실행하면 아래의 결과를 볼 수 있습니다.

위처럼 단순히 하나의 필드가 아닌 여러 개의 필드 값을 조합하여 라벨로 표시하고자 할때가 있습니다. 아래의 화면은 first_nv_n와 sum_ho_res에 대한 2개의 필드값을 조합하여 라벨을 표시하고 있는 예입니다.

위의 화면을 보면 지역에 대한 명칭(first_nv_n)과 해당 지역의 인구수(sum_ho_res)를 단위와 함께 표시하고 있는데요. 이를 위해 다음과 같은 사용자 정의 클래스 코드가 필요합니다.

CustomLabelFormatter = Xr.Class({
    name: "CustomLabelFormatter",
    extend: Xr.label.ProgrammableLabelFormatter,
    requires: [Xr.label.ILabelFormatter],

    construct: function (layer) {
        this.superclass(layer);
        this._idx_first_nv_n = -1;
        this._idx_sum_ho_res = -1;
    },

    methods: {
        value: function (shapeRow, fieldSet, attributeRow) {
            if (this._idx_first_nv_n == -1) {
                this._idx_first_nv_n = fieldSet.fieldIndex("first_nv_n");
            }

            if (this._idx_sum_ho_res == -1) {
                this._idx_sum_ho_res = fieldSet.fieldIndex("sum_ho_res");
            }

            var first_nv_n = attributeRow.valueAsString(this._idx_first_nv_n);
            var sum_ho_res = attributeRow.valueAsString(this._idx_sum_ho_res);

            return first_nv_n + "(" + sum_ho_res + "명)";
        }
    }
});

위의 클래스는 라벨에 대한 형식을 지정할 수 있는 기능을 갖고 있으며, 이처럼 라벨의 형식을 지정하기 위한 기능을 갖는 클래스는 Xr.label.ProgrammableLabelFormatter 클래스를 상속받고 Xr.label.ILabelFormatter 인터페이스를 구현해야 합니다. 이 클래스에서 실제 라벨의 값에 대한 문자값을 반환해 주는 함수는 value인데요. 이 함수를 살펴보면, 속도 향상을 위해 사용할 필드의 인덱스 값을 미리 저장해 두고 실제 필드의 값을 얻어와 원하는 형태로 문자값을 구성해 반환해 주고 있습니다. 이제 이 클래스를 사용하기 위해 코드A의 45번 코드를 다음 코드로 대체해주면 됩니다.

var formatter = new CustomLabelFormatter(lyr);
label.formatter(formatter);

FingerEyes-Xr에서 주제도(Theme Map) 표현

FingerEyes-Xr for HTML5에서 수치지도 레이어를 속성값에 따라 표현 심벌을 변경하여 주제도를 표현하는 기능에 대한 예제 코드를 정리해 봅니다.

먼저 다음과 같은 수치지도 레이어가 있다고 하겠습니다.

위의 결과를 표출하는 코드(코드A)는 아래와 같습니다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />

    <style>
        body {
            margin: 0px;
            padding: 0px;
        }

        #map {
            top: 0px;
            left: 0px;
            position: absolute;
            width: 100%;
            height: 100%;
            border: none;
            outline: none;
        }
    </style>

    <script src="../../scripts/fingereyes-xr/Xr.js"></script>

    <script>
        var map = null;

        function onLoad() {
            map = new Xr.Map("map", {});

            var lyr = new Xr.layers.ShapeMapLayer("lyr",
                {
                    url: "http://168.192.76.10:8080/Xr?layerName=li_a@test"
                }
            );
            
            var theme = lyr.theme();
            theme.penSymbol().color("black");
            theme.brushSymbol().color("lightgray");

            var lm = map.layers();
            lm.add(lyr);

            map.onLayersAllReady(onLayersReady);

            window.addEventListener("resize", onResize);
        }

        var bFinishResizing = true;

        function onResize() {
            if (bFinishResizing) {
                bFinishResizing = false;

                setTimeout(function () {
                    var newWidth = window.innerWidth;
                    var newHeight = window.innerHeight;

                    map.resize(newWidth, newHeight);
                    map.update();

                    bFinishResizing = true;
                }, 500);                
            }
        }

        function onLayersReady() {
            var cm = map.coordMapper();
            var lyr = map.layers("lyr");
            var mbr = lyr.MBR();

            cm.zoomByMBR(mbr);

            map.update();
        }
    </script>

    <title></title>
</head>

<body onload="onLoad()">
    <div id="map" />
</body>
</html>

이제 위에서 본 단순한 수치지도 표현을 속성값에 따라 주제도로 표현해 보도록 하겠습니다. 즉, 아래처럼 말입니다.

위와 같은 주제도 표현을 위해서 도형에 대한 속성값에 따라 채움색을 다르게 지정하고 있는데요. 사용한 속성필드는 sum_ho_res입니다. DBMS에서 이 레이어의 sum_ho_res에 대한 최대값과 최소값을 얻어보면 각각 13과 104인데요. 이 값의 구간을 5개로 나눠 색상을 지정하고, 각 값에 대해서 해당 구간의 색상으로 레이어를 그려주게 되면 주제도가 완성됩니다. 이러한 주제도를 표현하기 위해 속성값에 대한 그리기 심벌을 지정해주기 위한 클래스는 아래와 같습니다. (코드B)

CustomLayerTheme = Xr.Class({
    name: "CustomLayerTheme",
    extend: Xr.theme.ProgrammableShapeDrawTheme,
    requires: [Xr.theme.IShapeDrawTheme],

    construct: function (/* ShapeMapLayer */ layer) {
        this.superclass(layer);

        this._fieldIndex = -1;

        var minValue = 13;
        var maxValue = 104;
        var stepCount = 5;
        var stepValue = (maxValue - minValue) / stepCount; 
        var colors = ['#f1c40f', '#f39c12', '#e67e22', '#e74c3c', '#c0392b'];
        var symbols = [];

        for (var i = 0; i < stepCount; i++) {
            var SDS = new Xr.symbol.ShapeDrawSymbol();

            SDS.brushSymbol().color(colors[i]);
            SDS.penSymbol().color('#ffffff');
            SDS.penSymbol().width(2);
                    
            var symbol = {
                fromValue: minValue + (i * stepValue),
                toValue: minValue + ((i + 1) * stepValue),
                symbol: SDS
            };
                    
            symbols[i] = symbol;
        }

        this._symbols = symbols;
        this._stepCount = stepCount;
    },

    methods: {
        /* ShapeDrawSymbol */ symbol: function (/* ShapeRow */ shapeRow, /* FieldSet */ fieldSet, /* AttributeRow */ attributeRow) {
            if (this._fieldIndex === -1) {
                this._fieldIndex = fieldSet.fieldIndex("sum_ho_res");
            }

            var value = attributeRow.valueAsString(this._fieldIndex);
            var stepCount = this._stepCount;
            var symbols = this._symbols;
            var symbol = undefined;

            for (var i = 0; i < stepCount; i++) {
                symbol = symbols[i];

                if (value >= symbol.fromValue && value < symbol.toValue) {
                    break;
                }
            }

            return symbol.symbol;
        },

        /* boolean */ needAttribute: function () {
            return true;
        }
    }
});

수치지도에 대한 주제도를 정의하기 위해서는 Xr.theme.ProgrammableShapeDrawTheme 클래스를 상속하고 Xr.theme.IShapeDrawTheme 인터페이스를 구현 해야 합니다. 이렇게 만든 클래스가 바로 CustomLayerTheme입니다. 이 클래스의 생성자에서 11번~32번까지의 코드는 sum_ho_res 속성값 범위에 대해서 최대값과 최소값(실제 개발에서는 SQL Query를 통한 해당값을 얻어와야 함)을 이용해 5개의 구간으로 나누고 각 구간에 대한 그리기 심벌(채움색과 선색) 객체를 미리 생성해 두고 있습니다. 또한 도형을 그리기 위한 심벌을 반환해주는 symbol 함수에서는 속도 향상을 위해 sum_ho_res 필드에 대한 필드 인덱스를 미리 계산해 두고, 그릴 대상이 되는 도형의 sum_ho_res 필드값을 얻어, 그 값에 해당하는 심벌을 반환합니다. 이제 이 CustomLayerTheme 클래스를 해당 레이어에 지정하기 위해서는 코드A에서 37번-39번의 코드 대신 아래의 코드로 대체해야 합니다.

var newTheme = new CustomLayerTheme(lyr)
lyr.theme(newTheme);