크리깅(Kriging) 공간분석

공간 상에 분포된 값 기반의 보간 방식 중 하나인 크리깅을 기능 단위로 만든 코드를 정리한 글입니다. GIS 엔진은 웹 GIS 컴포넌트인 FingerEyes-Xr를 사용했습니다. 크리깅 알고리즘은 오픈소스 라이브러리인 kriging.js를 사용하였으므로 Leaflet이나 OpenLayers에 대한 API에 익숙한 개발자라면 해당 GIS 컴포넌트로도 크리깅 결과에 대한 효과적인 시각화를 구현할 수 있을 것입니다. 웹 기반에서 수행되지만 별도의 서버가 필요하지 않습니다. 지도는 VWorld의 배경지도를 그대로 이용할 것이며 크리깅을 위한 입력 데이터는 실행시 동적으로 생성할 것이기 때문입니다.

결과를 이미지로 먼저 살펴보면 아래와 같습니다.

0~100까지의 값을 가지는 총 35개의 지점이 있고 특정 영역 안에서 크리깅 분석을 수행해 그 결과를 그라디언트 색상으로 표현하는 것인데요. 먼저 특정 영역에 대한 좌표를 지정하고 지도에 표시하는 코드는 다음과 같습니다.

let psd = new Xr.data.PolygonShapeData([[
    new Xr.PointD(14289447.80, 4437166.67),
    new Xr.PointD(14289920.87, 4437347.65),
    new Xr.PointD(14289963.20, 4437210.07),
    new Xr.PointD(14290033.05, 4437198.42),
    new Xr.PointD(14289981.19, 4436636.45),
    new Xr.PointD(14289942.04, 4436488.28),
    new Xr.PointD(14289879.60, 4436349.64),
    new Xr.PointD(14289733.55, 4436420.55),
    new Xr.PointD(14289717.67, 4436438.54)
]]);

let psr = new Xr.data.PolygonGraphicRow(0, psd);

psr.brushSymbol().opacity(0);
psr.penSymbol().color('#ff0000');

gl.rowSet().add(psr);

0~100까지 난수로 부여된 값을 가지는 총 35개의 지점을 지도에 표시하는 코드는 다음과 같구요.

const x = [
    14289539.88, 14289633.01, 14289713.44, 14289812.93, 14289912.41,
    14289598.08, 14289698.63, 14289809.75, 14289883.83, 14289952.62,
    14289747.31, 14289760.01, 14289779.06, 14289793.88, 14289815.04,
    14289836.21, 14289864.78, 14289920.87, 14289698.63, 14289665.82,
    14289758.95, 14289697.57, 14289771.65, 14289732.49, 14289804.46,
    14289828.8, 14289839.38, 14289853.14, 14289885.95, 14289853.14,
    14289878.54, 14289876.42, 14289942.04, 14289929.34, 14289912.41
];

const y = [
    4437041.79, 4437076.72, 4437109.52, 4437148.68, 4437185.72,
    4436892.57, 4436923.26, 4436970.88, 4436999.46, 4436999.46,
    4436877.75, 4436834.36, 4436785.67, 4436736.99, 4436681.96,
    4436626.92, 4436563.42, 4436512.62, 4436824.83, 4436761.33,
    4436740.17, 4436667.14, 4436638.57, 4436577.18, 4436553.90,
    4436927.49, 4436879.87, 4436834.36, 4436790.97, 4436723.23,
    4436687.25, 4436629.04, 4436890.45, 4436744.40, 4436600.47
];

const t = [];

for (let i = 0; i < x.length; i++) {
    t[i] = Math.random() * 100.0;

    let p = new Xr.PointD(x[i], y[i]);
    let tsd = new Xr.data.TextShapeData({ x: x[i], y: y[i], text: t[i] >> 0 });
    let pgr = new Xr.data.TextGraphicRow(i + 1, tsd);

    gl.rowSet().remove(i + 1);
    gl.rowSet().add(pgr);
}

크리깅을 위한 입력값이 준비되어 있으므로 크리깅 분석을 아래의 코드처럼 수행합니다.

const variogram = kriging.train(t, x, y, "spherical", 0, 100);

이제 크리깅 분석 결과를 지도에 표시하기 위해 GridLayer를 사용하게 되는데, 크리깅 분석 결과 모델을 토대로 모르는 지점에 대한 값도 예측할 수 있으며 이 값들을 토대로 적절하게 색상을 배합하면 됩니다. 이러한 코드는 다음과 같습니다.

gridLyr.reset();

const minX = psr.MBR().minX;
const maxX = psr.MBR().maxX;
const minY = psr.MBR().minY;
const maxY = psr.MBR().maxY;

for (let x = minX; x < maxX; x += cellRes) {
    for (let y = minY; y < maxY; y += cellRes) {
        const v = kriging.predict(x, y, variogram);
        gridLyr.value(x, y, v);
    }
}

let clrTbl = new Xr.ColorTable(6);
                    
clrTbl.set(5, 225, 228, 177, 230);
clrTbl.set(4, 190, 208, 122, 230);
clrTbl.set(3, 152, 193, 99, 230);
clrTbl.set(2, 97, 168, 93, 230);
clrTbl.set(1, 46, 146, 85, 230);
clrTbl.set(0, 20, 102, 59, 230);

if (clrTbl.build()) {
    gridLyr.updateByColorTable(clrTbl, psd);
    map.update();
}

GridLayer에 대한 변수는 gridLyr이며 크리깅 보간을 통해 gridLayer 내부의 전체 셀값들이 업데이트됩니다. updateByColorTable 함수를 통해 표현할 색상과 원하는 경계 이외의 셀은 투명하게 잘리게 됩니다. 원하는 경계는 psd라는 변수로 지정하고 있는데 이 psd는 앞서 생성한 특정 영역에 대한 그래픽 요소 객체입니다. 참고로 gridLyr 객체의 생성 코드는 다음과 같습니다.

let cellRes = 1;
let gridLyr = new Xr.layers.GridLayer("grid",
    {
        mbr: psr.MBR(),
        resolution: cellRes
    }
);

이상으로 잘 만들어진 크리깅 라이브러리를 이용하여 웹 상에서 크리깅 분석을 수행하고 그 분석 결과를 시각화하는 코드에 대해 살펴 보았습니다.

Javascript의 Array, Set, Object, Map에 대한 데이터 순회하기

자바스크립트에서 데이터 컨테이너로써의 관점에서 데이터를 순회하는 코드를 정리합니다.

먼저 Array입니다.

const arr = new Array(1, 2, 3, 4, 5)

console.log('Array Iteration 1')
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i])
}

console.log('Array Iteration 2')
arr.forEach(v => {
    console.log(v)
})

console.log('Array Iteration 3')
for (let v of arr) {
    console.log(v)
}

console.log('Array Iteration 4')
for (let i in arr) {
    console.log(arr[i])
}

다음은 Set입니다.

const set = new Set([1, 2, 3, 4, 'Hello'])

console.log('Set Iteration 1')
for (let v of set) {
    console.log(v)
}

console.log('Set Iteration 2')
for (let v of set.values()) {
    console.log(v)
}

console.log('Set Iteration 3')
set.forEach(v => {
    console.log(v)
});

다음은 Object입니다.

const obj = { a: 1, b: 2, c: 3, 9: 4, e: 'Hello' }

console.log('Object Iteration 1')
const keys = Object.keys(obj) // [ 'a', 'b', 'c', '9', 'e' ]

for (let i = 0; i < keys.length; i++) {
    const k = keys[i]
    const v = obj[k]
    console.log(k, v)
}

console.log('Object Iteration 2')
const values = Object.values(obj) // [ 1, 2, 3, 4, 'Hello' ]
for (let i = 0; i < values.length; i++) {
    const v = values[i]
    console.log(v)
}

console.log('Object Iteration 3')
const entries = Object.entries(obj) // [ ['a', 1], ['b', 2], ['c', 3], ['9', 4], ['e', 'Hello'] ]
for (let i = 0; i < entries.length; i++) {
    const k = entries[i][0]
    const v = entries[i][1]

    console.log(k, v)
}

console.log('Object Iteration 4')
for (let k in obj) {
    const v = obj[k]
    console.log(k, v)
}

끝으로 Map입니다.

const map = new Map( [ ['a', 1], ['b', 2], ['c', 3], ['9', 4], ['e', 'Hello'] ])

console.log('Map Iteration 1')
for (let [k, v] of map) {
    console.log(k, v)
}

console.log('Map Iteration 2')
for (let k of map.keys()) {
    console.log(k, map.get(k))
}

console.log('Map Iteration 3')
for (let v of map.values()) {
    console.log(v)
}

console.log('Map Iteration 4')
for (let [k, v] of map.entries()) {
    console.log(k, v)
}

console.log('Map Iteration 5')
map.forEach(function(v, k) {
    console.log(k, v)
})

사실 Map의 출현으로 더 이상 Object를 데이터 컨테이너로써 사용하는 것은 옳지 않습니다. Object는 기본적으로 생성될 때 기본 키값을 갖지만 Map은 개발자가 추가하지 않은 데이터는 갖지 않습니다. 또한 Map의 Key는 문자열 객체 이외에도 타입도 가능합니다. 또한 저장한 순서대로 값을 얻을 수 있으며 데이터의 개수도 size 함수를 통해 바로 얻을 수 있습니다. 게다가 Map은 데이터의 추가와 삭제 시 Object보다 성능이 뛰어납니다.