차량이 달리는 방향을 지정할때 방위각을 많이 사용합니다. 차량의 경로를 구성하는 지점에 대한 값으로 위치(X,Y)와 속도 그리고 방향으로써 방위각을 가지고 있습니다. 방위각을 통해 차량이 달리고 있는 방향을 표현할 수 있는데.. 이 방위각의 각도값이 어떤 의미인지를 명확하게 이해하고.. 시스템 구현에 반영할때 참고할 목적으로 정리해 봅니다.
C#의 스레드(Thread)에서 UI 컨트롤 사용하기
기본적으로 UI를 갖는 컨트롤은 메인 스레드가 아닌 다른 스레드에서 접근될때 충돌(Crash)이 발생합니다. 현재 C/S 기반의 맵엔진인 듀라맵을 서버단에서 사용하고 있으며, 서버에서 여러 개의 스레드에서 UI 컨트롤인 듀라맵에 접근할때 아래와 같은 충돌이 발생합니다.

이에 대한 해결 방안은 델리게이트(Delegate)와 UI 컨트롤을 담는 Form 클래스의 Invoke 매서드를 사용해 해결할 수 있습니다.
예를 들어서 지도 엔진인 듀라맵을 이용해 어떤 지점이 특정 폴리곤에 포함(Contain)하는지에 대한 기능을 수행하고자 할 때를 살펴 보겠습니다.
듀라맵을 이용해 다음과 같은 Contain이라는 새로운 사용자 정의 매서드를 만들었습니다. 이 Contain 매서드의 위치는 어디라도 상관이 없습니다. 중요한 것은, 바로 이 Contain이라는 매서드가 별도의 스레드를 통해 호출된다는 것이고 델리게이트 기법을 사용하지 않는다면 충돌이 발생합니다.
public bool Contain(double X, double Y, int FID)
{
//UI 컨트롤 사용
}
별도의 스레드에서 문제 없이 위에서 언급한 Contain이라는 매서드를 호출하기 위해서는, Contain 매서드를 직접 호출하지 않고 델리게이트(대리자)를 사용하면 됩니다. 델리게이트를 다음처럼 Contain 매서드가 존재하는 같은 클래스에 선언합니다.
private delegate bool DelegateContain(double X, double Y, int FID); private DelegateContain delegateContain;
이제 위의 델리게이트와 Contain 매서드를 연결하는 코드는 다음과 같습니다.
delegateContain = new DelegateContain(Contain);
이제 메인 스레드가 아닌 스레드에서 Contain 매서드의 기능을 활용하기 위해서는 Contain에 대한 델리게이트인 delegateContain를 사용하면 됩니다. 즉, 다음처럼…
bool bContain = (bool)form.Invoke(delegateContain, X, Y, FID);
스레드에서 사용하고자 하는 UI 컨트롤이 담긴 폼 객체의 Invoke 매서드를 사용해 delegateContain을 호출하며 Contain 매서드의 인자는 Invoke 매서드를 통해 전달하면 됩니다. Invoke는 가변 인자를 받으므로 인자의 제약이 덜합니다. 또한 Invoke의 반환값은 델리게이트 대상이 되는 함수의 반환값이며 object 타입이므로 Contain 매서드의 반환값으로 형변환하여 사용하면 됩니다.
[GIS] 오픈소스, 자바스크립트 좌표계 변환 라이브러리, proj4js
C언어 기반의 좌표계 변환을 자바스크립트(Javascript) 언어로 그대로 포팅한 proj4js에 대한 사용에 있어 간단한 예를 통해 정리해 보려고 합니다. 아직 다양한 좌표계 간의 상호 변환에 적용해 보지는 않았으나 OpenLayers와 같은 오픈소스에서 사용하는 좌표계 변환 API이므로 이미 검증은 되었다고 판단할 수 있습니다.
먼저 proj4js는 http://trac.osgeo.org/proj4js/ 에서 다운로드 받을 수 있고.. 저는 여기서 WGS84 경위도를 Bessel 타원체 경위도로 변환하는 것과.. WGS84 타원체 경위도를 카텍(Katec) TM 직각 좌표계로 변환하는 것에 대한 2가지 예를 정리해 봅니다. 먼저 WGS84 경위도를 Bessel 경위도로 변환하는 코드입니다.
코드를 설명하면.. 1번 코드는 proj4s 라이브러리를 사용할 수 있도록 가져오는 것입니다. 그리고 4번 코드는 proj4js를 사용함에 있어서 오류가 있다면 오류에 대한 메세지를 표시합니다. 여기서는 alert 함수를 사용하여 메세지 창으로 표시하도록 하였습니다. 그리고 5번과 6번이 proj4의 좌표를 정의하는 문자열입니다. 그리고 9번과 10번은 변환을 위한 Proj 객체를 생성합니다. 12번 코드는 변환할 좌표입니다.
13번이 실제로 좌표계를 변환하는 transfrom 함수입니다. 이 함수의 첫번째 인자는 원본 좌표계이고 두번째는 변환되어질 좌표계입니다. 세번째는 변환할 좌표인데.. 변환이 성공하면 다시 이 인자에 결과가 저장됩니다. 주의할 점은 서로 다른 타원체 간의 변환이므로 5번과 같이 towgs84 파라메터를 반드시 지정해야 합니다. 또한 동일한 타원체 간의 변환에서는 towgs84를 지정해서는 않됩니다. 여기서는 3개의 파라메터를 사용하였으나 보다 정확한 변환을 위해 다른 파라메터를 사용하셔도 됩니다. 다음으로 WGS84 경위도를 카텍으로 변환하는 코드입니다.
중요한 부분은 6번에서 카텍에 대한 좌표계 정보에 대한 문자열 값입니다. 또한 towgs84 값을 반드시 지정하였는데.. 이유는 서로 다른 타원체 간의 변환이기 때문입니다. 즉, WGS84 타원체에서 카텍이 사용하는 타원체인 Bessel의 변환이기 때문입니다. 만약 서로 동일한 타원체 간의 변환이라면 towgs84 파라메터를 지정해서는 않됩니다.
[C++] URL로부터 바이너리 데이터 다운로드
예전에 만들어 놓은 것이 있는데.. 도통 찾을 수가 없어서 다시 만들어 본 함수입니다. URL 경로에 존재하는 데이터를 다운로드하여 로컬 파일로 저장해 주는 함수입니다. 실제 개발에 사용할 요량으로 인자가 제법 복잡합니다.
DWORD Download(HINTERNET hInternet, char *pszURL,
char *pszFileName, BYTE *pBuffer) {
HINTERNET hURL = InternetOpenUrl(hInternet, pszURL, NULL, 0, 0, 0);
if(hURL == NULL) {
InternetCloseHandle(hInternet);
return -2;
}
HANDLE hFile = CreateFile(pszFileName, GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE) return -3;
DWORD dwSize = 2048;
DWORD dwRead, dwWritten, dwTotalSize;
bool bOK = HttpQueryInfo(hURL, HTTP_QUERY_CONTENT_LENGTH,
pBuffer, &dwRead, NULL);
if(!bOK) return -4;
dwTotalSize = atoi((const char *)pBuffer);
do {
InternetQueryDataAvailable(hURL, &dwSize, 0, 0);
InternetReadFile(hURL, pBuffer, dwSize, &dwRead);
WriteFile(hFile, pBuffer, dwRead, &dwWritten, NULL);
} while(dwRead != 0);
InternetCloseHandle(hURL);
CloseHandle(hFile);
return dwTotalSize;
}
아래는 위의 함수를 직접 사용하는 코드입니다.
HINTERNET hInternet = InternetOpen("MyAGENT", INTERNET_OPEN_TYPE_PRECONFIG,
NULL, NULL, 0);
if(hInternet == NULL) return 0;
BYTE *pBuffer = new BYTE[1024*1024];
DWORD dwTotalSize;
dwTotalSize = Download(hInternet, "http://www.s.com/a.zip", "c:/a.zip", pBuffer);
printf("TotalSize: %d\n", dwTotalSize);
dwTotalSize = Download(hInternet, "http://www.s.com/b.zip", "c:/b.zip", pBuffer);
printf("TotalSize: %d\n", dwTotalSize);
delete [] pBuffer;
InternetCloseHandle(hInternet);
1번 코드에서처럼 가장먼저 hInternet 객체를 만듭니다. 이 객체를 재활용하여 다수의 URL을 통해 파일을 다운로드할 수 있습니다. 5번 코드는 파일을 다운로드하는데 사용하는 버퍼입니다. Download 함수를 여러번 사용할 것을 대비하여 버퍼를 재활용할 수 있도록 하였습니다. 또한 Downoad 함수의 결과값은 다운로드된 바이너리 데이터의 전체 바이트 수입니다. 음수인 경우 ERROR로 간주할 수 있습니다. 14번과 15번 코드처럼 사용한 리소스는 반환합니다.
추가로 아래의 Download2 함수는 다운로드된 데이터를 파일로 기록하지 않고 메모리 버퍼에 저장하는 함수입니다. 다운로드된 데이터를 파일에 저장하지 않고 바로 메모리 상에서 사용하고자 할때 사용할 수 있습니다.
DWORD Download2(HINTERNET hInternet, char *pszURL, BYTE *pBuffer) {
HINTERNET hURL = InternetOpenUrl(hInternet, pszURL, NULL, 0, 0, 0);
if(hURL == NULL) {
InternetCloseHandle(hInternet);
return -2;
}
DWORD dwSize;
DWORD dwRead, dwWritten, dwTotalSize;
DWORD dwCursor = 0;
bool bOK = HttpQueryInfo(hURL, HTTP_QUERY_CONTENT_LENGTH, pBuffer,
&dwRead, NULL);
if(!bOK) return -4;
dwTotalSize = atoi((const char *)pBuffer);
do {
InternetQueryDataAvailable(hURL, &dwSize, 0, 0);
InternetReadFile(hURL, (LPVOID)(pBuffer + dwCursor), dwSize, &dwRead);
dwCursor += dwRead;
} while(dwRead != 0);
InternetCloseHandle(hURL);
return dwTotalSize;
}
끝으로 이 함수 사용을 위해 헤더 파일로 wininet.h와 라이브러리 파일로 wininet.lib가 필요합니다.
[JavaScript] 클래스 정의 API ㅡ 3/3
이제 끝으로 Rectangle를 상속받는 PositionedRectangle 클래스를 정의해 보겠습니다. Rectangle 클래스는 단순히 가로와 세로 크기값만을 가지고 있습니다. 여기에 위치값까지 갖도록 한 클래스가 바로 PositionedRectangle 클래스입니다.
var PositionedRectangle = Class({
name: "PositionedRectangle",
extend: Rectangle,
construct: function(x,y,w,h) {
this.superclass(w, h);
this._x = x;
this._y = y;
},
methods: {
draw: function() {
alert("draw PositionedRectangle");
},
getPosition: function() {
return new Coordinate(this._x, this._y);
}
},
requires: [aShape],
statics: {
isSame: function(a, b) {
return a._x == b._x && a._y == b._y &&
a._width == b._width && a._height == b._height;
}
}
});
중요한 부분은 바로 3번 코드로 상속받고자 하는 클래스를 지정하는 extend입니다. 바로 이 extend로 지정된 클래스의 모든 속성값을 상속받게 되는데 이 PositionedRectangle 클래스는 Rectangle 클래스의 모든 속성(변수와 매서드)를 상속받게 됩니다.
추가로 18번 코드는 정적 속성(변수와 매서드)를 정의하는 방법입니다. 이는 C++과 자바와 같은 언어에서 클래스 차원에서 제공하는 바로 그 기능입니다.
이제 이렇게 정의한 클래스들을 직접 사용하는 예를 보이면 다음과 같습니다.
var c = new Circle(100, 100, 30); var r = new PositionedRectangle(10, 10, 100, 100) alert(r instanceof Rectangle); alert(r instanceof PositionedRectangle); alert(r instanceof Object); alert(r instanceof Circle); c.draw(); r.draw(); alert(c.getCenter().toString()); alert(PositionedRectangle.isSame(r, r));
1번과 2번 코드는 클래스를 통해 객체를 생성하고 있습니다. 그리고 4~7번 코드에서 각 객체가 어떤 클래스의 인스턴스인지를 검사하고 있습니다. 결과값은 순서대로 true, true, true, false입니다. 그리고 9~10번 코드는 draw 함수를 호출하고 있습니다. 마친가지로 12번 ~ 13번 코드는 정의한 매서드를 사용하고 있습니다.
이상으로 클래스 정의 API에 대한 내용을 마무리합니다. 중요한 것은 자바스크립트는 결코 완벽한 클래스 정의를 제공해주지 못합니다. 이는 언어적 한계입니다. 이러한 한계를 해결한 버전이 자바스크립트 2.0입니다. 자바스크립트 2.0이 나오기 전까지는 이러한 클래스 정의 API를 개발자가 직접 만들고 이해하여 사용해야 하며 스스로에 대한 약속을 정해 반드시 준수해야 합니다. 이러한 약속은 private 변수의 경우 변수명 앞에 밑줄(_)을 넣는 등에 대한 것입니다. 가장 중요한 것은 문서화 입니다. 자바스크립트는 매우 유연한 언어이므로 문서화를 통해 약속을 분명히 기술하여 지킬 수 있도록 해야합니다.
