div 안의 img에 Inner Shadow 적용하기

아래와 같은 DOM 계층이 있다고 하자.

위의 div에 아래와 같은 스타일을 적용해 안쪽 그림자(Inner Shadow) 효과를 넣고자 한다면..

div {
    box-shadow: rgba(0, 0, 0, 0.5) 3px 3px 10px inset;
}

div 요소의 자식인 img에 부모인 그림자 효과가 표시되어야 할 것이라고 기대했지만, 부모인 div의 그림자가 자식인 img에 가려져 아래와 같은 결과만을 볼 수 있다.

그렇다면 자식인 img에 의해 가려진 부모의 그림자를 들어내기 위해서 어떻게 해야할까? 부모의 그림자 역시 DOM 계층적으로 보면 또 하나의 공개되지 않은 DOM 요소이다. 이 그림자 DOM 요소가 img에 가려지므로 이 img의 z-index의 값과 z-index 속성값이 영향을 받도록 하기 위해 position 속성을 relative나 absolute로 지정하면 되는데, 아래와 같다.

img {
    position: relative;
    z-index: -1;
}

이제 그 결과를 보면, div 요소에 안쪽 그림자 효과가 반영되어, 좀더 입체적으로 이미지가 표현되는 것을 확인할 수 있다.

넥스젠(NexGen)의 DEM 데이터를 활용한 측정 기능

“넥스젠 GIS 기반 솔루션”은 항공영상, DEM, 수치지도 등과 같은 공개된 공간 DB를 활용할 수 있는 시스템으로, 주소 검색 등과 같은 기본적인 GIS 기능을 제공하는 솔루션으로써, 별도의 커스터마이징 작업을 통해 고객이 원하는 공간 DB와 기능을 추가할 수 있습니다. 아래는 넥스젠에서 DEM 데이터를 활용해 수행할 수 있는 표고 측정, 단면도 측정, 평균경사도측정과 3차원 가시화 기능에 대한 동영상입니다.

FingerEyes-Xr, 공간 데이터에 대한 Custom Draw (사용자 정의 그리기)

공간 데이터는 위치 데이터와 속성 데이터의 조합입니다. 단순히 공간 데이터를 일괄적으로 원하는 색상이나, 아이콘 등으로 표현할 수 있지만 각각의 공간 데이터에 대한 속성 데이터를 활용하여 다양한 형태로 그리고자 할 때가 있습니다. 예를 들어 아래와 같은 공간 데이터 테이블이 있다고 합시다.

위의 테이블에서 images는 서버 측에 저장된 이미지 파일명에 대한 리스트이고, tags는 이미지에 대한 해시태그 성격의 값입니다. 그리고 the_geom은 좌표 데이터입니다. 이 3개의 값을 이용해 특정 위치에 이미지와 해시태그를 표현하고자 할 때, 클라이언트 GIS 엔진인 FingerEyes-Xr의 어떤 API를 활용해 구현할 수 있는지 정리해 봅니다. 먼저 아래는 위의 3개의 속성값을 이용해 실제로 구현된 결과에 대한 화면입니다.

FingerEyes-Xr에서는 공간 데이터를 그릴 때, 특히 포인트 데이터에 대해서 다음처럼 표현하고자 하는 마커 심벌을 지정할 수 있습니다.

var customSym = new GeoHashTagMarkerSymbol();
layer.theme().markerSymbol(customSym);

즉, 공간 데이터를 그리는 방식을 지정하는 GeoHashTagMarkerSymbol 클래스를 정의해 생성한 후, 공간 데이터에 대한 레이어의 Theme에 대해 MarkerSymbol로써 지정하면 됩니다. 바로 이 GeoHashTagMarkerSymbol의 Javascript의 구현은 아래와 같습니다.

GeoHashTagMarkerSymbol = Xr.Class({
    requires: [Xr.symbol.IMarkerSymbol],

    construct: function () { },

    methods: {
        /* SVG Element */ create: function (/* PointD */ point, 
            /* AttributeRow */ attrRow, /* CoordMapper */ coordMapper) { 
            var vp = coordMapper.W2V(point);
            var vpx = vp.x, vpy = vp.y;

            var tags = attrRow.valueAsString(0); // tags 필드의 값
            var imageFile = attrRow.valueAsString(1); // images 필드의 값
            var w = this.wSize(), h = this.hSize();
            var g = document.createElementNS(xmlns, "g");

            var image = document.createElementNS(xmlns, "image");
            image.setAttribute("x", vpx - w / 2);
            image.setAttribute("y", vpy - w / 2);
            image.setAttribute("width", w);
            image.setAttribute("height", w);
            image.setAttributeNS("http://www.w3.org/1999/xlink", "href", imageFile);

            g.appendChild(image);

            var textStroke = document.createElementNS(xmlns, "text");
            textStroke.setAttribute("x", vpx);
            textStroke.setAttribute("y", vpy + h / 2 - 6);
            textStroke.setAttribute("text-anchor", "middle");
            textStroke.setAttribute("stroke", "black");
            textStroke.textContent = tags;
            g.appendChild(textStroke);

            return g;
        },

        /* number */ wSize: function () { 
            return this._width;
        },

        /* number */ hSize: function () {
            return this._height;
        },
    }
});

2번 코드에서처럼 Xr.symbol.IMarkerSymbol 인터페이스를 구현하겠다고 명시하고, 4번 코드의 생성자에서 필요로 값을 미리 정의해 두며, 10번 코드의 create에서 지도 상에 표현할 SVG 요소를 생성해 반환합니다. 40번과 44번 코드의 wSize, hSize 함수를 통해 심벌의 크기를 정하게 됩니다.

JavaScript의 Class 정의 정리

모던한(?).. 즉 현대적인 Javascript에서는 클래스를 정의하기 위한 class 키워드를 제공하기는 하지만, 현재의 IE에서 아직도 지원하지 않아 나름대로의 class 정의 방식을 사용하고 있습니다. 웹 기반의 GIS 엔진인 FingerEyes-Xr도 이러한 class 정의 방식으로 개발 되었습니다. 수백여개의 클래스를 이 방식으로 정의해 왔음에도 새로운 클래스를 정의할라치면 기존에 만들어진 소스를 Copy 해서 Paste 해 고치는 것이 세련미 떨어져.. 직접 키보드로 한땀 한땀 입력하고자 정리해 봅니다.

먼저 클래스 정의하는 최소한의 구문입니다.

MyClass = Xr.Class({
    /* name: "MyClass", */ // optional

    construct: function () { /* 생성자 */ }
});

private 변수를 추가하는 구문입니다. 반드시 생성자 안에서 밑줄(_)로 시작해서 정의합니다.

MyClass = Xr.Class({
    construct: function () {
        this._privateVariable = 0;
    }
});

맴버 함수를 추가하는 구문입니다. private는 밑줄로 시작하고, public은 밑줄이 아닌 영문 소문자로 시작합니다.

MyClass = Xr.Class({
    construct: function () {},

    methods: {
        _privateFunction: function() { },
        publicFunction: function() { },
    }
});

클래스 차원에서 접근할 수 있는 static 변수 정의입니다. 아래와 같다면, MyClass.STATIC_VARIABLE의 값은 0이 됩니다.

MyClass = Xr.Class({
    statics: {
        STATIC_VARIABLE: 0
    },

    construct: function () {}
});

[ToDo] 상속, 인터페이스에 대한 내용은 추후 필요하면 그때 정리할 것