DuraMap-Xr에서 Image Symbol 크기 조정

듀라맵에서 ShapeMapLayer에 대한 이미지 심벌을 지정할 경우에, 그 크기는 원본 이미지 크기 그대로 표시됩니다. 이에 대해 개발자가 직접 이미지의 크기를 지정할 수 있는 방식도 제공되는데요. 아래의 코드는 이미지의 크기를 가로 84px로 지정하는 경우입니다. 세로의 크기는 가로와 세로에 대한 비율을 유지하도록 자동으로 계산됩니다.

axXr1.Layers.AddShapeMapLayer("lyr", "D:/__Data__/points.shp");
axXr1.WaitForAllConnections();

if (axXr1.Resources.AddImageResource("img", "d:/__data__/temperature.png"))
{
    var sym = axXr1.Layers.GetLayerAsShapeMap("lyr").PointSymbol;

    sym.SetImage("img", axXr1.Resources);
    sym.Size = 84; // 이미지 심벌을 가로로 84px로 지정 !!
}

axXr1.Update();

위의 코드와 함께 듀라맵에 소소한 개선으로 두가지가 이루어졌는데요. 첫째는 휠마우스에 대한 Delta 값을 보다 정확히 음수와 양수로 얻을 수 있다는 것과 두번째는 GetFIDFromMousePoint 매서드를 통해 도형을 선택할 때, 포인트 심벌의 크기에 맞춰 선택할 수 있도록 개선되었는데요. 기존에는 포인트 심벌의 크기를 고려하지 않아 포인트의 심벌 중앙을 클릭해야 선택되는 사용상의 불편함이 해소되었습니다.

환경계측데이터에 대한 보간 및 TMS 가공

환경과 관련된 계측 데이터에는 풍속, 풍향, 온도, 미세먼지, 오존농도 등이 있습니다. 이러한 계측 데이터는 일정한 시간 주기로 측정되고 측정된 값은 관련된 시스템의 서버에 저장되어, 이를 필요로 하는 곳에서 활용할 수 있도록 다양한 방식으로 제공되는데요. 제공되는 방식은 파일 기반으로 다운로드 받을 수 있거나 OpenAPI 형태로 효과적으로 서비스 받을 수 있습니다.

계측 데이터는 공간 상에 특정한 위치에 계측기를 설치하여 측정되는데요. 측정되지 못하는 지점에 대한 계측값은 이미 계측된 다른 가까운 지점들을 이용하여 보간(Interpolation)됩니다. 공간에 대한 보간 알고리즘은 Spline, Kernel Density, IDW, Kriging 등이 있고 각 보간 알고리즘마다 하나의 좋은 주제로써 추후 시간을 내어 연구해 볼만합니다.

이 글은 이러한 환경과 관련된 계측 데이터를 보간하고, 보간된 결과를 웹에서 누구나 쉽게 조회할 수 있도록 지도로 서비스할 수 있는 TMS 형태로 가공하여 OpenLayer나 Leaflet와 같은 가장 많이 사용하는 오픈소스 기반의 클라이언트 지도 엔진에서 활용하는 것에 대한 내용을 정리해 보았는데요. 각 처리 과정에서 활용한 툴은 자체적으로 개발된 엔진을 이용하여 보다 빠르게 처리할 수 있었습니다.

최종적으로 웹 기반의 지도로 서비스될 계측 데이터는 ‘온도’로 하였습니다. 전세계의 온도 데이터는 다양한 경로를 통해 다운로드받을 수 있습니다. 아래의 그림은 전세계의 위경도에 대해 1도마다 측정된 온도 데이터를 보다 쉽게 보간할 수 있도록 SHP 파일 형태로 변환하는 내용입니다.

온도에 대한 계측 지점에 대한 온도값 속성으로 단위가 켈빈인 K 값을 담고 있습니다.

이제 이 계측값에 대한 SHP 파일을 보간을 통해 모든 지점에 대한 계측값을 계산할 수 있는데요. 아래의 그림은 보간 방법 중 IDW 알고리즘을 사용한 것으로, 그 결과에 대해 의미있는 색상을 적용해 표현한 것입니다.

보간된 데이터는 그리드(Grid) 형태입니다. 이를 TMS 형태의 격자로 가공하기 위해 아래의 화면과 같이 Mr.Tiler-Xr이라는 타일맵 가공툴을 이용해 가공합니다.

이렇게 가공된 TMS 형식의 타일맵에 약간의 투명도를 주어 OpenLayers를 이용해 OpenStreetMap을 배경지도 상에 정확히 중첩되는지 확인한 화면은 아래와 같습니다.

추후 이러한 일련된 과정을 자동화하여 주기적으로 들어오는 계측값을 자동으로 TMS 형식의 타일맵으로 가공해 서버측에 저장해 두는 기능을 고려하고 있습니다.

[golang] 배열을 포인터로 전달하는 함수

Go는 배열을 함수로 전달할때 배열의 전체를 복사한 값 형식으로 함수에 전달합니다. 결국 함수 안에서 해당 배열의 요소를 변경하여도 파라메터로 전달되어진 그 원래의 배열에 변경이 생기지 않습니다. 그러나 이 배열을 포인터로 전달하면 함수 내부에서 변경되는 대상이 원본이므로 변경 내용을 유지됩니다. 아래는 배열을 포인터 타입으로 함수에 전달하는 예제입니다.

package main

import "fmt"

func f(a *[3]int) {
	a[1] = 100
}

func main() {
	a := [3]int{1, 2, 3}

	f(&a)

	fmt.Println(a[1])
}

DuraMap-Xr에서 마우스 Wheel로 지도 확대, 축소하기

DuraMap-Xr의 Mouse Wheel 이벤트를 이용해 지도를 확대 축소하는 이벤트에 대한 글을 정리합니다. 배경지도는 TMS 형식으로 활용할 수 있는 VWorld를 이용합니다. 먼저 VWorld의 배경지도에 대한 Layer는 아래처럼 추가할 수 있는데요. 저는 아래의 코드를 폼(Form)이 Shown 이벤트에 추가하였습니다.

XrMapLib.ValueList URLs = new XrMapLib.ValueList();
URLs.AddValueByString(VWORLD_TMS_URL);

XrMapLib.Extent MaxMBR = new XrMapLib.Extent();
MaxMBR.MinX = -20037508.342789244;
MaxMBR.MinY = -20037508.342789244;
MaxMBR.MaxX = 20037508.342789244;
MaxMBR.MaxY = 20037508.342789244;

double[] upps = {
    78000, 39000, 19600, 9800, 4900, 2400, //Dummy
    1222.9924523925781, 611.4962261962891, 305.74811309814453,
    152.87405654907226, 76.43702827453613, 38.218514137268066,
    19.109257068634033, 9.554628534317017, 4.777314267158508,
    2.388657133579254, 1.194328566789627, 0.5971642833948135, 0.5971642833948135/2 };

XrMapLib.ValueList UPPs = new XrMapLib.ValueList();

Scales.Clear();

for (int iUPP = 0; iUPP < upps.Length; ++iUPP)
{
    double UPP = upps[iUPP];
    double MapScale = axXr1.GetMapScaleFromUPP(UPP);

    UPPs.AddValueByFloat(UPP, 14);

    if (iUPP > 5) // upps 배열에서 0~4번째는 한국에서는 사용되지 않음
    {
        Scales.Add(MapScale);
    }
}

axXr1.Layers.AddTMSLayer("basemap", URLs, MaxMBR, UPPs, true);
axXr1.WaitForAllConnections();

XrMapLib.Coord MapPt = new XrMapLib.Coord();
MapPt.X = 14145315;
MapPt.Y = 4514689;
            
axXr1.MapCenter = MapPt;
axXr1.MapScale = Scales[0];
axXr1.Update();

axXr1.MouseMode = XrMapLib.XrMapViewModeEnum.XrPanMode;

참고로 듀라맵 지도 뷰어 컨트롤의 변수는 axXr1입니다. 위의 코드를 보면 Scales라는 전역 변수가 보입니다. 이 Scales는 다음처럼 정의되어 있습니다. Scales 컨터이너 변수는 각 지도 줌 단계에 대한 축척값을 담고 있습니다.

private List<double> Scales = new List<double>();

이제 지도 컨트롤의 마우스 휠 이벤트를 등록합니다. 아래의 코드는 듀라맵 지도 뷰 컨트롤의 휠 이벤트에 할당된 이벤트 함수입니다.

private void axXr1_OnMouseWheel(object sender, AxXrMapLib._IXrMapControlEvents_OnMouseWheelEvent e)
{
    double NewMapScale = axXr1.MapScale;

    // 현재 지도 축척값 중 가장 가까운 줌 레벨 얻기
    int cntLevels = Scales.Count;
    double Gap = Double.MaxValue;
    int CurrentLevel = -1;
    for (int i = 0; i < cntLevels; i++)
    {
        double ThatGap = Math.Abs(NewMapScale - Scales[i]);
        if (Gap > ThatGap)
        {
            Gap = ThatGap;
            CurrentLevel = i;
        }
    }

    if(e.delta == 120) // Wheel Up
    {
        int idx = CurrentLevel + 1;
        if (idx >= Scales.Count) idx = Scales.Count - 1;
        axXr1.MapScale = Scales[idx];
    }
    else  // Wheel Down
    {
        int idx = CurrentLevel - 1;
        if (idx < 0) idx = 0;
        axXr1.MapScale = Scales[idx];
    }

    axXr1.Update();
}