GDAL은 geotif 등과 같은 Raster 데이터에 대해 수행할 수 있는 다양한 Command를 제공합니다. Raster 파일에 대한 Extent와 좌표계 정보를 얻을 수 있는 gdalinfo.exe에서부터 영상에서 특정 영역만을 뽑아내 또 다른 영상 파일로 저장할 수도 있고, 좌표계에 대한 투영변환도 가능하며 GCP를 지정해 Georeferencing도 가능합니다. GIS 개발자에게 Raster 데이터를 저수준에서 처리하기 위해 GDAL는 매우 활용도가 높고 중요한 오픈소스 라이브러리입니다. (GDAL 라이브러리만 있다면 어떠한 GIS 데이터 포맷이든 모두 내 손 안에 있다규 !!)
GDAL에서 제공하는 Command 명령 중 실제 작업 중에 사용했던 것을 정리해 둡니다.
1. Raster 데이터의 정보 보기
Z:\> gdalinfo A.tif
위의 명령은 A.tif 파일에 대한 좌표계 및 Extents 등에 대한 정보를 얻기 위한 명령입니다.
위의 연속적으로 실행되는 2개의 명령 중 첫번째는 GCP를 3개 사용해 입력 파일인 A.jpg 파일에 대해 Georeferencing 하여 B.tif 파일로 저장하라는 명령이고, 두번째는 저장된 B.tif를 Resizing 알고리즘 중 lanczos를 사용해 c.tif 파일로 저장하라는 명령입니다.
JavaScript 언어를 이용해 SVG에 PieChart를 위한 도형을 생성하기 위한 코드를 함수로 정리해 둡니다. 최종 결과는 아래처럼 4개의 항목으로 구성된 가운데 구멍이 뚫린 모양입니다.
먼저 아래처럼 svg와 PieChart를 구성하는 4개의 항목을 path로 지정해 둡니다. (실제 활용시에는 이 부분을 모두 JavaScript 코드로 동적 생성하도록 해야 합니다)
위의 4개의 path에 대해 각각 파이차트의 구성 요소로 만들어 주는 함수는 아래와 같습니다.
function toPieChartItemPath(x, y, radiusIn, radiusOut, startAngle, endAngle) {
function _toXY(cX, cY, r, degrees) {
var rad = (degrees) * Math.PI / 180.0;
return {
x: cX + (r * Math.cos(rad)),
y: cY + (r * Math.sin(rad))
};
}
var startIn = _toXY(x, y, radiusIn, endAngle);
var endIn = _toXY(x, y, radiusIn, startAngle);
var startOut = _toXY(x, y, radiusOut, endAngle);
var endOut = _toXY(x, y, radiusOut, startAngle);
var arcSweep = (endAngle - startAngle) <= 180 ? "0" : "1";
var d = [
"M", startIn.x, startIn.y,
"L", startOut.x, startOut.y,
"A", radiusOut, radiusOut, 0, arcSweep, 0, endOut.x, endOut.y,
"L", endIn.x, endIn.y,
"A", radiusIn, radiusIn, 0, arcSweep, 1, startIn.x, startIn.y,
"z"
].join(" ");
return d;
}
이 함수의 인자를 설명하면, x와 y는 파이 차트의 중심점이고 radiusIn과 radiusOut은 파이차트의 내부 반지름과 외부 반지름 값입니다. 그리고 startAngle와 endAngle는 시작각도(3시 방향이 0도)와 종료각도(시계방향)입니다. 이 함수를 통해 앞서 구성한 svg와 path 요소를 파이차트로 구성하는 코드는 아래와 같습니다.
“소프트웨어 개발이 좋은 사람”이라는 블로그에서 d3를 이용해 아날로그 시계를 만든 글을 보고 코드를 살펴 보았습니다. d3는 개인적으로도 관심이 많고, 상당이 깊이 있게 공부를 했던(하지만 활용력은 아직까지도 얇은..) 자바스크립트 기반의 데이터 시각화 라이브러리입니다. 그 결과는 아래와 같습니다.
[xyz-ihs snippet=”D3-Clock”]
위의 시계에 대한 코드를 d3의 학습겸 해서 정리해 보고자 합니다. 먼저 시계에 대한 뷰는 아래처럼 오직 svg 태그 하나입니다.
시계의 크기에 해당하는 이 svg의 크기를 지정하기 위해 다음처럼 스타일을 지정합니다.
#clock {
width: 400px;
height: 400px;
}
시계의 구성요소는 시계의 형태인 큰 동그라미 부분, 12개의 숫자 부분, 시분초에 대한 바늘 부분으로 구성됩니다. 시분초에 대한 바늘은 Timer를 사용해 1초마다 갱신해 진짜 시계처럼 움직이도록 합니다.
스크립트 코드를 작성해 볼 것인데요. 가장 먼저 앞서 추가해둔 svg 요소를 d3의 선택자를 이용해 선택해 둡니다.
var clock = d3.select("#clock");
var center = parseInt(clock.style("height")) / 2;
var radius = center;
위의 코드는 추가적으로 svg의 높이값의 반값으로 중심점을 위한 center 변수와 시계의 반지름 크기값을 위한 radius 변수도 정의되어 있습니다.
앞서 시계는 3개의 부분으로 구성된다고 하였는데요. 다시 언급하면 시계의 형태인 큰 동그라미 부분, 12개의 숫자 부분, 시분초에 대한 바늘 부분입니다. 먼저 시계의 형태인 큰 동그라미 부분을 그리는 함수 drawFace를 반지름값(radius 변수)을 인자로 해 호출합니다.
drawFace(radius);
function drawFace(radius) {
clock.append("circle")
.attr("cx", center)
.attr("cy", center)
.attr("r", radius)
.attr("class", "face");
clock.append("circle")
.attr({
"cx": center,
"cy": center,
"r": radius * 0.1,
"fill": "#000"
});
}
위의 코드에서 face라는 class를 통해 스타일을 지정하고 있는데요. 이 .face에 대한 스타일 정의는 아래와 같습니다.
.face {
fill : #FFF;
stroke-width: 2px;
stroke: #000;
}
또 아래는 12개의 숫자 부분을 구성하는 코드입니다.
drawNumbers(radius);
function drawNumbers(radius) {
var pos = radius * 0.85;
for (var num = 1; num < 13; num++) {
var deg = 30 * num;
var x = pos * Math.cos(Math.PI * (deg - 90) / 180);
var y = pos * Math.sin(Math.PI * (deg - 90) / 180);
var cx = x + center;
var cy = y + center;
var text = clock.append("text")
.attr({ "x": cx, "y": cy, "class": "number" })
.text(num)
.style("font-size", radius * 0.15 + "px")
.attr("transform", "rotate(" + deg + ", " + cx + ", " + cy + ")");
}
}
위의 코드에서 number라는 class를 통해 숫자의 문자 스타일을 지정하고 있는데요. 이 .number에 대한 스타일 정의는 아래와 같습니다.
또 아래는 시, 분, 초에 대한 바늘을 구성하는 코드입니다. 시침, 분침, 초침을 각각 hourHand, minuteHand, secondHand 변수에 저장하고 있는데요. 이 변수를 통해 1초마다 각 침의 형태를 변경해 주게 됩니다.
var hourHand = drawHand(0, 0, radius*0.07);
var minuteHand = drawHand(0, 0, radius*0.05);
var secondHand = drawHand(0, 0, radius*0.01);
function drawHand(x, y, width) {
var hand = clock.append("line")
.attr({
"x1": center,
"y1": center,
"x2": x + center,
"y2": y + center,
"class": "hands" })
.style("stroke-width", width);
return hand;
}
위의 코드에서 hand라는 class를 통해 바늘의 스타일을 정의하고 있습니다. 이 .hand에 대한 스타일 정의는 아래와 같습니다.
.hands {
stroke: #000;
stroke-linecap: round;
}
이제 1초마다 제 시간에 맞게 바늘을 움직도록 하는 코드는 아래와 같습니다.
drawClock();
function drawClock() {
drawTime(radius);
setTimeout(drawClock, 1000);
}
위의 코드에서 1초마다 실행되는 drawTime의 함수가 보이는데요. 바로 이 drawTime이 3개의 바늘을 현재 시간에 맞게 그 형상을 변경해 주는 함수이며 아래와 같습니다.
function drawTime(radius){
var now = new Date();
var hour = now.getHours();
var minute = now.getMinutes();
var second = now.getSeconds();
//hour
var pos = radius * 0.5;
x = pos * Math.cos(Math.PI*((hour*30)-90+30/60*minute+30/60/60*second)/180);
y = pos * Math.sin(Math.PI*((hour*30)-90+30/60*minute+30/60/60*second)/180);
hourHand.attr({"x1": center, "y1": center, "x2": x+center, "y2": y+center});
//minute
pos = radius*0.65;
x = pos * Math.cos(Math.PI*((minute*6)-90+6/60*second)/180);
y = pos * Math.sin(Math.PI*((minute*6)-90+6/60*second)/180);
minuteHand.attr({"x1": center, "y1": center, "x2": x+center, "y2": y+center});
// second
pos = radius*0.9;
x = pos * Math.cos(Math.PI* ((second*6)-90)/180);
y = pos * Math.sin(Math.PI* ((second*6)-90)/180);
secondHand.attr({"x1": center, "y1": center, "x2": x+center, "y2": y+center});
}
공간 서버 단에서 kml 문서를 생성해 주는 기능을 개발하면서 Point, Polyline, Polygon 요소에 대한 kml 문서를 생성해 주는 Java 라이브러리를 만들어 사용했는데요. 이에 대해 공개합니다. 단순히 좌표에 대한 형상 정보만을 생성해 주는 라이브러리이고 style과 같은 내용은 아직 지원하지 않습니다. 이 라이브러리의 클래스 구조도는 다음과 같습니다.
이 라이브러리를 이용해 Point에 대한 kml 문서를 생성해 주는 코드의 예는 다음과 같습니다.
import java.util.LinkedList;
import geoservice.kmlwriter.Coordinate;
import geoservice.kmlwriter.Document;
import geoservice.kmlwriter.IGeometry;
import geoservice.kmlwriter.LineString;
import geoservice.kmlwriter.LinearRing;
import geoservice.kmlwriter.Placemark;
import geoservice.kmlwriter.Point;
import geoservice.kmlwriter.Polygon;
public class MainEntry {
private static String testPointPlaceMarkKML() {
Document kmlDoc = new Document();
Coordinate coord = new Coordinate(128, 38); //(경도, 위도)
IGeometry geom = new Point(coord);
Placemark placemark = new Placemark(geom, "My Point Placemark");
kmlDoc.addPlacemark(placemark);
String kml = kmlDoc.build();
return kml;
}
....
Polyline에 대한 kml 문서를 생성해 주는 코드는 아래와 같습니다.
import java.util.LinkedList;
import geoservice.kmlwriter.Coordinate;
import geoservice.kmlwriter.Document;
import geoservice.kmlwriter.IGeometry;
import geoservice.kmlwriter.LineString;
import geoservice.kmlwriter.LinearRing;
import geoservice.kmlwriter.Placemark;
import geoservice.kmlwriter.Point;
import geoservice.kmlwriter.Polygon;
public class MainEntry {
private static String testPolylinePlaceMarkKML() {
Document kmlDoc = new Document();
LinkedList<Coordinate> coords = new LinkedList<Coordinate>();
coords.add(new Coordinate(128, 38));
coords.add(new Coordinate(129, 38));
coords.add(new Coordinate(129, 39));
IGeometry kmlGeom = new LineString(coords);
Placemark placemark = new Placemark(kmlGeom, "My Polyline Placemark");
kmlDoc.addPlacemark(placemark);
String kml = kmlDoc.build();
return kml;
}
....