FingerEyes-Xr for HTML5와 GeoService-Xr를 활용한 설비 공간정보시스템

특화된 GIS 시스템 개발을 견고하고 빠르게 개발할 수 있는 GIS 엔진으로써, FingerEyes-Xr과 GeoService-Xr을 활용해 설비를 관리하는 GIS 시스템을 개발하고 있습니다.

다양한 기능 중 웹에서 공간 데이터를 SHP 파일로 바로 내보내기하여 저장할 수 있는 기능에 대해 소개합니다.

공간서버인 GeoService-Xr이 접근(Access)하는 공간 데이터가 저장된 DBMS로부터 SHP 파일을 다운로드할 수 있는 방법은 단지 URL의 호출만으로 이루어집니다. 즉, 공간 데이터에 대한 레이어의 고유 식별자가 ecl_alts_main이라고 하고, SHP 파일로 저장할 영역의 범위를 MBR로써, 각각 MinX, MinY, MaxX, Max라고 한다면 아래의 URL을 호출하면 SHP 파일을 압축 파일로 바로 다운로드 받을 수 있습니다.

http://localhost/Xr?gdownload|ecl_alts_main|MinX|MinY|MaxX|MaxY

아래의 화면은 SHP 파일을 저장하는 기능에 대한 실제 화면입니다. 레이어를 관리하는 UI에서 바로 원하는 레이어를 SHP 파일이나 KML로 저장할 수 있도록 하였습니다.

위의 UI 중 SHP 파일로 저장하기 원하는 레이어 명 옆의 SHP 버튼을 클릭하면 아래와 같이 웹브라우저에서 흔히 볼 수 있는 다운로드가 시작됩니다.

다운로드를 위한 URL 호출을 통해 클라이언트 측에 다운로드를 받기 위해 JavaScript 코드를 간단히 설명하겠습니다. 먼저 주요 줄기를 언급하면 iframe의 src 속성에 url을 지정하면 되는데요. 이를 위해서 해당 웹 페이지에 다음과 같이 숨겨진 iframe을 추가합니다.

var iframe = $('');
iframe.appendTo(this._containerDiv);

그리고 SHP 파일을 저장하는 버튼에 대한 클릭 이벤트에 다음과 같은 코드가 필요합니다.

function onDownloadSHPClick(event) {
    var layerId = $(this).attr('layerId');
    var cm = g_map.coordMapper();
    var mbr = cm.viewportMBR();
    var iframe = $("#downloadFrame");
    var url = mg_MapLayers.GIS_HTTP_SERVER + '/Xr?gdownload|' + layerId + '|' + mbr.minX + '|' + mbr.minY + '|' + mbr.maxX + '|' + mbr.maxY;
    
    iframe.attr("src", url);

위의 코드 중 실제 의미 있는 부분은 5~8번 코드입니다.

이상으로 FingerEyes-Xr과 GeoService-Xr을 이용하여 GIS 시스템에서 공간 데이터를 바로 SHP 파일로 저장할 수 있는 기능과 JavaScript 코드에 대해 살펴 보았습니다.

태양광 설비 관리를 위한 현장지원시스템 개발에 앞서서 …

태양광 설비를 관리하는 현장지원시스템을 모바일 GIS 엔진인 BlackPoint-Xr를 이용해 개발하게 되었습니다.

아래의 화면은 Mr.Tiler-Xr에서 드론으로 촬영한 영상 이미지를 배경도로 하고, 태양광과 관련된 설비인 태양광 어레이(Array)와 모듈(Module) 그리고 접속함, 인버터, 분전반, 배전반 등에 대한 설비를 표시한 것입니다.

Mr.Tiler-Xr은 수치지도를 이용해 배경지도를 디자인하고 타일맵으로 가공할 수 있는 툴이면서, 공간 데이터를 편집할 수 있는 기능도 제공합니다. 저는 개인적으로 GIS 시스템 개발을 위해 제공받은 SHP 파일이나 항공영상을 Mr.Tiler-Xr를 통해 살펴봅니다.

이 글은 현장지원 앱의 개발에 앞서 태양광 설비의 관리 업무에서 개발에 필요한 최소한의 지식을 얻기 위해 몇가지 설비를 간략하게 서술식으로 정리한 것입니다.

“태양광 모듈은 태양광 셀로 구성이 되는데요. 셀 1개가 생성하는 전압은 0.5V~0.6V이며, 전류는 4A~8A입니다. 또한 여러개의 모듈을 하나의 어레이로 단위로 구성합니다. 그리고 태양광발전소에 설치되는 분전반, 인버터, 분전반, 배전반은 생산된 전류를 모으고, 과전류가 발생하면 전류를 차단시키며 직류를 교류로, 또 교류를 직류르 변환하는 역활과 저압과 고압으로 변성하는 역활 등을 담당하는 장치들입니다.”

추후 태양광 설비 관리를 위한 현장지원시스템 개발이 완료되면 블로그를 통해 다시 소개해 드리도록 하겠습니다.

[Java] 우선순위 큐(Priority Queue) 활용 예제코드

자바에서 제공하는 컨테이너(Container) 중 어떤 데이터에 대해 우선순위 값을 부여하고, 이 우선순위를 기준으로 자동으로 정렬되어, 우선순위에 따라 데이터를 꺼내어 사용할 수 있는 우선순위 큐에 대한 예제 코드를 정리합니다.

먼저 우선순위 값을 갖는 데이터에 대한 타입 정의가 필요합니다. 아래처럼 Node라는 클래스를 추가해 타입을 정의합니다.

package tstPriorityQueue;

public class Node implements Comparable<Node> {
	private String UUID;
	private String parentUUID;
	private double G;
	private double H;
	
	public Node(String UUID, double G, double H) {
		this.UUID = UUID;
		this.parentUUID = null;
		this.G = G;
		this.H = H;
	}
	
	public double getF() { return G + H; }
	public double getG() { return G; }
	public double getH() { return H; }
	public String getNode() { return UUID; }
	public String getParentNode() { return parentUUID; }
	
	public void setG(double v) { G = v; }
	public void setH(double v) { H = v; }
	public void setParentNode(String v) { parentUUID = v; }
	
	@Override
	public int compareTo(Node target) {
	    if (this.getF() > target.getF()) {
            return 1;
        } else if (this.getF() < target.getF()) {
            return -1;
        }

	    return 0;
	}
	
	public String toString() {
		return UUID + '(' + getF() + ')';
	}
}

위의 클래스에서 중요한 부분은 우선순위값을 얻기 위한 getF() 함수입니다. 이 함수는 데이터의 상대적인 크기의 비교를 위한 인터페이스인 Comparable 구현할 때 사용되는 함수인데요. 바로 compareTo 라는 함수로써, 위의 경우에는 우선순위값이 작은 것을 먼저 꺼내어 사용하겠다는 정의입니다.

실제로, 위의 Node 클래스에 대한 타입으로 정의된 데이터를 컨테이너에 넣고, 사용하는 코드는 아래와 같습니다.

package tstPriorityQueue;

import java.util.PriorityQueue;

public class EntryMain {

	public static void main(String[] args) {
		// Create items
		Node node1 = new Node("423182c4-edb5-11e6-bc64-92361f002671", 1.0, 5.1);
		Node node2 = new Node("42318742-edb5-11e6-bc64-92361f002671", 1.0, 2.4);
		Node node3 = new Node("42318878-edb5-11e6-bc64-92361f002671", 1.0, 3.8);
		Node node4 = new Node("42318968-edb5-11e6-bc64-92361f002671", 1.0, 6.2);
		Node node5 = new Node("42318a3a-edb5-11e6-bc64-92361f002671", 1.0, 4.5);
		
		// Create priority queue
		PriorityQueue<Node> pQueue = new PriorityQueue<Node>();
		
		// Add items to queue
		pQueue.offer(node1); // same code as pQueue.add(node1)
		pQueue.offer(node2);
		pQueue.offer(node3);
		pQueue.offer(node4);
		pQueue.offer(node5);
		
		// Get items from queue
		while(!pQueue.isEmpty()) {
			Node node = pQueue.poll();
			System.out.println(node);
		}
	}

}

데이터를 5개 생성해서, 우선순위 큐 저장소에 저장하고 최종적으로 26번 코드를 통해 5개의 데이터를 우선순위에 따라 꺼내어 화면에 표시합니다. 그 결과는 아래와 같습니다.

42318742-edb5-11e6-bc64-92361f002671(3.4)
42318878-edb5-11e6-bc64-92361f002671(4.8)
42318a3a-edb5-11e6-bc64-92361f002671(5.5)
423182c4-edb5-11e6-bc64-92361f002671(6.1)
42318968-edb5-11e6-bc64-92361f002671(7.2)

[C++] binary_search 정리

메모리 기반의 방대한 데이타가 있다고 가정을 할 때.. 이 데이터 목록 중 어떤 데이타가 존재하는지의 여부를 가장 빠르게 검색해주는 방식이 binary_search인데요. 이 STL 함수를 정리해 봅니다.

먼저 메모리 기반의 방대한 데이터를 준비해 봅니다.

#include <iostream>
#include <list>
#include <algorithm>
#include <iterator>

using namespace std;

int main()
{
    list<int> values{ 1, 2, 8, 7 ,6, 5, 4, 1, 9, 3, 5, 6, 7 };

}

데이타가 몇개 되지 않지만 방대하다고 칩시다. binary_search를 사용하기 위해서는 먼저 데이터가 정렬되어 있어야 합니다. 내림차순으로 정렬하는 코드를 추가하면.. 다음과 같습니다.

#include <iostream>
#include <list>
#include <algorithm>
#include <iterator>

using namespace std;

int main()
{
    list<int> values{ 1, 2, 8, 7 ,6, 5, 4, 1, 9, 3, 5, 6, 7 };
    auto predicate = [](int a, int b) { return a > b; };
    values.sort(predicate);

    copy(values.begin(), values.end(), ostream_iterator<double>{ std::cout, " "});
    cout << endl;
}

람다 함수로 내림차순 정렬을 위한 기준으로 삼았습니다. 그리고 잘 정렬되었는지 콘솔에 표시해 보았구요. 이제 binary_search 함수를 사용하기 위한 데이터 덩어리가 준비 되었고, 다음처럼 데이터 덩어리중 값 5가 존재하는지의 여부를 검색하는 코드는 다음과 같습니다.

#include <iostream>
#include <list>
#include <algorithm>
#include <iterator>

using namespace std;

int main()
{
    list<int> values{ 1, 2, 8, 7 ,6, 5, 4, 1, 9, 3, 5, 6, 7 };
    auto predicate = [](int a, int b) { return a > b; };
    values.sort(predicate);

    copy(values.begin(), values.end(), ostream_iterator<double>{ std::cout, " "});
    cout << endl;

    int wanted{ 5 };
    bool bSearched = binary_search(begin(values), end(values), wanted, predicate);
    if (bSearched) {
        cout << "Found !" << endl;
    } else {
        cout << "Not found." << endl;
    }
}

DuraMap-Xr을 이용한 좌표 변환 코드 샘플

DuraMap-Xr은 proj.4 기반의 문자열을 토대로 좌표계를 변환할 수 있는데요. 특히 좌표 변환시 10 파라메터에 해당하는 Molodensky-Badekas 모델을 지원합니다.

아래의 코드는 “대한민국 TM 중부원점(Bessel 타원체) – 10.405 보정”의 좌표계의 한 좌표인 (492385.95, 188096.47)를 “대한민국 TM 중부원점(GRS80 타원체)” 좌표계로 변환하는 코드 예입니다.

XrMapLib.Coord Pt;

Pt.X = 492385.95;
Pt.Y = 188096.47;

XrMapLib.Projection proj = new XrMapLib.Projection();

// 대한민국 TM 중부원점(Bessel 타원체) - 10.405 보정
proj.SourceProj4String = "+proj=tmerc +lat_0=38N +lon_0=127.0028902777778E +ellps=bessel +x_0=200000 +y_0=600000 +k=1 +units=m +no_defs";

// 대한민국 TM 중부원점(GRS80 타원체)
proj.TargetProj4String = "+proj=tmerc +lat_0=38N +lon_0=127E +ellps=GRS80 +x_0=200000 +y_0=600000 +k=1 +units=m +no_defs";

// 10 파라메터 적용(towgs84 파라메터)
proj.Set10Parameters(-145.907, 505.034, 685.756, -1.162, 2.347, 1.592, 6.342, -3159521.31, 4068151.32, 3748113.85);

proj.Transform(Pt);

코드를 보면 좌표계 지정을 위해서 사용한 proj.4 문자열에 towgs84에 해당하는 값을 14번째 코드처럼 별도의 매서드(Set10Parameters)로 지정한다는 것입니다. 3개 또는 7개의 파라메터 지정을 위해서는 각각 Set3Parameters, Set7Parameters 매서드를 사용합니다.

최종 좌표변환은 16번 코드의 Transform 매서드로 수행되는데요. 이 매서드는 변환할 입력 좌표 객체를 필요로 하고, 변환된 좌표는 다시 이 입력 좌표 객체에 저장되므로, 입력 좌표를 계속 유지하려면 복사본을 가지고 있어야 합니다.