팡이네

이전에 작성했던 행정구역 표시 방법은

 

https://neopang.tistory.com/entry/%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%A7%80%EB%8F%84-%ED%96%89%EC%A0%95%EA%B5%AC%EC%97%AD-%ED%91%9C%EC%8B%9Cshp-geojson

 

shp 파일을 이용하여 자신의 서버에 geojson 데이터를 올려두고

사용하고자 할 때 유용했지만 아래와 같은 단점이 있었다.

 

1. shp -> geojson 변환과정이 복잡

2. simplify 과정에서 맞닿아 있는 경계구역의 모양이 서로 맞지 않는 문제

3. 경계구역이 변경될 경우 유지보수 문제

 

이러한 문제점을 해결할 수 있는 오픈API가 있어서 소개한다.

 

공간정보 오픈플랫폼 오픈API 사용

 

https://dev.vworld.kr/

 

1. 회원가입 > 인증키 발급 > 이메일 인증

 

2. API 인증키 발급

회원가입 후 인증키 관리 메뉴에서 발급가능

https://www.vworld.kr/dev/v4dv_apikey_s002.do

 

3. 오픈API > 데이터 API 레퍼런스

https://www.vworld.kr/dev/v4dv_2ddataguide2_s001.do

 

분류를 '경계' 조회 -> 광역시도, 시군구, 읍면동, 리

 

광역시도

https://dev.vworld.kr/dev/v4dv_2ddataguide2_s002.do?svcIde=adsido

 

시군구

https://dev.vworld.kr/dev/v4dv_2ddataguide2_s002.do?svcIde=adsigg

 

읍면동

https://dev.vworld.kr/dev/v4dv_2ddataguide2_s002.do?svcIde=ademd

 

https://dev.vworld.kr/dev/v4dv_2ddataguide2_s002.do?svcIde=adri

 

경계구역 - 서울특별시 중구 태평로1가

(주의사항)

geojson 데이터에 bbox 값이 있을 경우

map.data.addGeoJson() 메소드 호출할 때 해당 위치로 자동 이동한다.

지도의 자동 이동을 원하지 않는다면 bbox 값을 삭제

버튼 클릭 시 테두리 및 효과 삭제

1
2
3
4
5
6
7
8
9
10
11
12
.btn { color: #fff; background-color: #fff; cursor: default; }
.btn:active, .btn:focus { border: none; box-shadow: none; }
 
/* 또는 */
 
.btn { color: #fff; background-color: #fff; cursor: default; }
.btn:active, .btn:focus { border: none !important; box-shadow: none !important; }
 
/* 또는 */
 
.btn { color: #fff; background-color: #fff; cursor: default; }
.btn:active, .btn:focus { outline: none !important; box-shadow: none !important; }

전에 올린 마커 클러스터링 문서와 이어지는 내용

마커 목록에서 마커를 선택했을 경우 지도 상에 표시된 해당 마커의 디자인을 변경해서 표시

 

[CSS 추가]

1
2
3
4
5
6
7
8
9
10
11
12
//--------------------------------------
// CSS 추가
//--------------------------------------
// 마커용 css
.list > ol > li { display: flex; align-items: center; }
.list > ol > li:hover { background-color: rgba(3, 133, 255, 0.5); }
.list > ol > li > input { margin-right: 5px; }
 
.cm { display: flex; font-weight: bold; align-items: center; text-align: center; }
.cm > div:nth-child(1) { width: 32px; height: 32px; }
.cm > div:nth-child(2) { position: absolute; background-color: #2f96fc; width: 32px; height: 32px; border-radius: 28px; line-height: 32px; margin-bottom: 8px; z-index: 9; }
.cm > div:nth-child(3) { position: absolute; background-color: #ffffff; width: 24px; height: 24px; border-radius: 24px; line-height: 24px; margin-left: 4px; margin-bottom: 8px; z-index: 10; }

[마커 설정]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//--------------------------------------
// 마커 설정
//--------------------------------------
var markers = [];
 
for (var i = 0, ii = options.length; i < ii; i++) {
    markers.push(new naver.maps.Marker({
        position: new naver.maps.LatLng(options[i].pos[0], options[i].pos[1]),
        zIndex: 100,
        icon: {
            content: [
                '<div>',
                '   <div><img src="marker.png" /></div>',
                '</div>',
            ].join(''),
            anchor: new naver.maps.Point(11, 33),
        }
    }));
}

[마커 표시]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//--------------------------------------
// 마커 표시
//--------------------------------------
// 마커 info 속성 표시
function showMarkerInfo(marker) {
    var img = $(marker.getIcon().content).find('img');
    var chk = img.attr('choose');
    var json = attr2json(img.attr('info'));
    var li = $('마커 : code = '+ json.code +', name = '+ json.name +'');
    $('#list').append(li);
     
    //체크박스 change 이벤트 설정
    li.on('change', chooseMarker.bind(this, json.code, marker));
}

[선택한 마커 아이콘 변경]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//--------------------------------------
// 선택한 마커 아이콘 변경
//--------------------------------------
var _cCount = 0;
 
//체크한 마커 아이콘 변경
function chooseMarker(c, marker) {
    var img = $(marker.getIcon().content).find('img');
     
    if ($('#cb'+ c).prop('checked')) {
        _cCount++;
         
        var icon = {
            content: [
                '<div class="cm">',
                '   <div><img src="marker.png" /></div>',
                '   <div> </div>',
                '   <div>'+ _cCount +'</div>',
                '</div>',
            ].join(''),
            anchor: new naver.maps.Point(16, 33),
        };
    } else {
        _cCount--;
         
        var icon = {
            content: [
                '<div>',
                '   <div><img src="marker.png" /></div>',
                '</div>',
            ].join(''),
            anchor: new naver.maps.Point(11, 33),
        };
    }
     
    marker.setIcon(icon);
}

네이버 지도에서 마커를 표시할 때 지도가 축소되면 마커를 묶어서 표시

네이버에서는 마커를 묶어서 표시하기 위해 예제를 제공하고 있다.(아래 참고)

그 예제를 약간 수정하여 마커 클러스터링을 구현한 소스

 

준비물: jquery-3.5.1.min.js, MarkerClustering.js

 

[지도 html]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//--------------------------------------
// 지도 html
//--------------------------------------
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>마커 클러스터링</title>
    <script type="text/javascript" src="jquery-3.5.1.min.js"></script>
    <script type="text/javascript" src="marker-clustering-custom.js"></script>
    <style type="text/css">
    body { font-size: 12px; font-family: dotum; }
    input { height: 22px; background-color: #efefef; border: 1px solid #333333; border-radius: 4px; }
     
    .div_map { display:flex; flex-direction: row; }
    .map { width: 1000px; height: 600px; border: 1px solid #c0c0c0; border-radius: 4px; }
     
    .div_list { height: 600px; }
    .list_title { height: 30px; line-height: 30px; background-color: #efefef; border: 1px solid #c0c0c0; border-radius: 4px; margin: 0 0 5px 5px; font-weight: bold; padding-left: 10px; }
    .list { width: 320px; height: 563px; border: 1px solid #c0c0c0; border-radius: 4px; margin-left: 5px; overflow-y: auto; }
    .list > ol { line-height: 1.7em; }
     
    .ctl { display: flex; flex-direction: row; justify-content: space-between; width:1000px; margin-top: 5px; }
    .pos { display: flex; flex-direction: row; justify-content: space-between; }
    .ctl input {  text-align: center; }
    .pos button { height: 26px; border: 1px solid #333333; border-radius: 4px; }
    .pos > div:nth-child(2) { margin-left: 6px; }
    </style>
</head>
<body>
<div>
    <div class="div_map">
        <div id="map" class="map"> </div>
        <div class="div_list">
            <div class="list_title">마커 목록</div>
            <div class="list"> </div>
        </div>
    </div>
    <div class="ctl">
        <div><input id="info" readonly="readonly" size="40" type="text" value="" /></div>
        <div class="pos">
            <div>
                <input id="lat" readonly="readonly" size="20" type="text" value="" />
                <input id="lng" readonly="readonly" size="20" type="text" value="" />
            </div>
            <div>
                <button type="button">좌표 복사</button>
                <button type="button">원위치</button>
            </div>
        </div>
    </div>
</div>
</body>
</html>

[지도 및 이벤트 설정]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//-----------------------------------------------
// 지도 및 이벤트 설정
//-----------------------------------------------
var _BASE = new naver.maps.LatLng(37.566605, 126.9783931);  // 서울시청
var _ZOOM = 16;     // 지도 표시 레벨
var _list = [];     // 현재 화면에 표시된 마커 목록
var marker1 = null; // 좌표 조회용 마커
 
// 지도 옵션
var mapOptions = {
    center: _BASE,
    zoom: _ZOOM,
    zoomControl: true,      // 확대,축소
    zoomControlOptions:  { position : naver.maps.Position.TOP_RIGHT },
    //logoControlOptions:  { position : naver.maps.Position.BOTTOM_RIGHT }, // 네이버로고
    scaleControl: true,     // 축척
    scaleControlOptions: { position : naver.maps.Position.BOTTOM_RIGHT },
    mapDataControl: false// 저작권
};
 
// 지도 표시
var map = new naver.maps.Map('map', mapOptions);
 
// 지도 이벤트 설정
naver.maps.Event.addListener(map, 'zoom_changed', function(e) {
    if (e > 15) {
        marker1.setMap(map);    // 마커1 표시
    } else {
        marker1.setMap(null);   // 마커1 삭제
    }
});
 
// 마커 목록 삭제
function clearList() {
    $('#list').empty();
}
 
// 마커 목록 표시
function showList() {
    _list.forEach(function(item) {
        showMarkerInfo(item);
    });
     
    _list = [];
}
 
// 현재 위치 표시
showPosition(_BASE);
 
// 위도 경도 표시
function showPosition(latlng) {
    $('#lat').val(latlng.lat());
    $('#lng').val(latlng.lng());
}
 
// 원위치 버튼 클릭
function resetPosition() {
    map.setCenter(_BASE);
    map.setZoom(_ZOOM);
     
    if (marker1) {
        marker1.setPosition(_BASE);
    }
     
    showPosition(_BASE);
    $('#info').val(null);
}
 
// 좌표를 클립보드에 복사
function copyPosition() {
    var info = $('#info');
     
    info.val($('#lat').val() +', '+ $('#lng').val());
     
    info.select();
    document.execCommand('copy');
}
 
// 주소 변경
function changeAddr(code) {
    if (code == '') {
        map.setCenter(_BASE);
    } else {
        showArea(code);
    }
}
 
// 마커1(기본 마커)
marker1 = new naver.maps.Marker({
    position: _BASE,
    map: map,
    zIndex: 200,
    draggable: true,    // 마커1 드래그 허용
});
 
// 마커1 드래그 허용
//marker1.setDraggable(true);
 
// 마커1 드래그 이벤트 설정
naver.maps.Event.addListener(marker1, 'drag', function(e) {
    showPosition(e.coord);
});

[마커 데이터]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//-----------------------------------------------
// 마커 데이터
//-----------------------------------------------
var options = [
    { pos: [ 37.5675575, 126.9778996 ], info: "code: '1001', name: '1001 사무실'", type: '사무실' },
    { pos: [ 37.5674628, 126.9777064 ], info: "code: '1002', name: '1002 사무실'", type: '사무실' },
    { pos: [ 37.56749,   126.9779417 ], info: "code: '1003', name: '1003 사무실'", type: '사무실' },
    { pos: [ 37.5673746, 126.9778567 ], info: "code: '1004', name: '1004 사무실'", type: '사무실' },
    { pos: [ 37.5671748, 126.9780283 ], info: "code: '1005', name: '1005 사무실'", type: '사무실' },
    { pos: [ 37.5679912, 126.9779639 ], info: "code: '1006', name: '1006 사무실'", type: '사무실' },
    { pos: [ 37.567515,  126.9781677 ], info: "code: '1007', name: '1007 사무실'", type: '사무실' },
     
    { pos: [ 37.5696834, 126.980453  ], info: "code: '1011', name: '1011 사무실'", type: '사무실' },
    { pos: [ 37.5698195, 126.9811396 ], info: "code: '1012', name: '1012 사무실'", type: '사무실' },
    { pos: [ 37.5693093, 126.9813327 ], info: "code: '1013', name: '1013 사무실'", type: '사무실' },
     
    { pos: [ 37.5698535, 126.9739299 ], info: "code: '1021', name: '1021 공실'", type: '공실' },
    { pos: [ 37.56976,   126.9731896 ], info: "code: '1022', name: '1022 공실'", type: '공실' },
     
    { pos: [ 37.5683143, 126.9765907 ], info: "code: '1031', name: '1031 호텔'", type: '호텔' },
    { pos: [ 37.5685609, 126.9760328 ], info: "code: '1032', name: '1032 호텔'", type: '호텔' },
    { pos: [ 37.5686204, 126.9764405 ], info: "code: '1033', name: '1033 호텔'", type: '호텔' },
    { pos: [ 37.5680676, 126.9758611 ], info: "code: '1034', name: '1034 호텔'", type: '호텔' },
    { pos: [ 37.5679315, 126.9765048 ], info: "code: '1035', name: '1035 호텔'", type: '호텔' },
     
    { pos: [ 37.5670727, 126.9817405 ], info: "code: '1041', name: '1041 맛집'", type: '맛집' },
    { pos: [ 37.5677275, 126.9823842 ], info: "code: '1042', name: '1042 맛집'", type: '맛집' },
    { pos: [ 37.5678721, 126.9819122 ], info: "code: '1043', name: '1043 맛집'", type: '맛집' },
     
    { pos: [ 37.5643939, 126.9755392 ], info: "code: '1051', name: '1051 공공기관'", type: '공공기관' },
    { pos: [ 37.5643939, 126.9750028 ], info: "code: '1052', name: '1052 공공기관'", type: '공공기관' },
     
    { pos: [ 37.5649382, 126.9818692 ], info: "code: '1061', name: '1061 쇼핑'", type: '쇼핑' },
    { pos: [ 37.5647001, 126.9830279 ], info: "code: '1062', name: '1062 쇼핑'", type: '쇼핑' },
    { pos: [ 37.5647001, 126.9838862 ], info: "code: '1063', name: '1063 쇼핑'", type: '쇼핑' },
    { pos: [ 37.5651763, 126.9813972 ], info: "code: '1064', name: '1064 쇼핑'", type: '쇼핑' },
    { pos: [ 37.5651678, 126.983146  ], info: "code: '1065', name: '1065 쇼핑'", type: '쇼핑' },
    { pos: [ 37.5653294, 126.9837575 ], info: "code: '1066', name: '1066 쇼핑'", type: '쇼핑' },
     
    { pos: [ 37.5657886, 126.9713335 ], info: "code: '1071', name: '1071 학교'", type: '학교' },
    { pos: [ 37.5661118, 126.9706469 ], info: "code: '1072', name: '1072 학교'", type: '학교' },
    { pos: [ 37.5656355, 126.9718056 ], info: "code: '1073', name: '1073 학교'", type: '학교' },
     
    { pos: [ 37.5694623, 126.9856243 ], info: "code: '1081', name: '1081 은행'", type: '은행' },
    { pos: [ 37.5693178, 126.9850128 ], info: "code: '1082', name: '1082 은행'", type: '은행' },
    { pos: [ 37.5695133, 126.9862465 ], info: "code: '1083', name: '1083 은행'", type: '은행' },
    { pos: [ 37.568782,  126.9859676 ], info: "code: '1084', name: '1084 은행'", type: '은행' },
];

[마커 등록 및 이벤트 설정]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//-----------------------------------------------
// 마커 등록 및 이벤트 설정
//-----------------------------------------------
var markers = [];
 
for (var i = 0, ii = options.length; i &lt; ii; i++) {
    // info, text 속성을 넣기 위해 HTML 마커 사용
    markers.push(new naver.maps.Marker({
        position: new naver.maps.LatLng(options[i].pos[0], options[i].pos[1]),
        zIndex: 100,
        icon: {
            content: '',
            anchor: new naver.maps.Point(11, 33),
        }
    }));
}
 
// 마커 클릭 이벤트 설정
for (var i = 0, ii = markers.length; i < ii; i++) {
    naver.maps.Event.addListener(markers[i], 'click', getClickHandler(i));
}
 
// 마커 클릭 이벤트
function getClickHandler(seq) {
    return function(e) {
        clearList();
        showMarkerInfo(markers[seq]);
    }
}
 
// html 속성값을 json 변환
function attr2json(attr) {
    var json = {};
    var s = attr.trim().split(',');
     
    for (var i = 0, ii = s.length; i &lt; ii; i++) {
        var a = s[i].split(':');
        json[a[0].trim()] = a[1].trim().replace(/''/g,'');
    }
     
    return json;
}

[클러스터 마커 생성, 변경]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//-----------------------------------------------
// 클러스터 마커 생성, 변경
//-----------------------------------------------
// 클러스터 마커 생성(HTML 마커)
function newMarker(radius, count, name) {
    var cvs = document.createElement('canvas');
     
    return changeMarker(cvs, radius, count, name);
}
 
// 클러스터 마커 변경(HTML 마커)
function changeMarker(cvs, radius, count, name) {
    cvs.width = radius * 2;
    cvs.height = radius * 2;
 
    var ctx = cvs.getContext('2d');
     
    // 원 표시
    ctx.beginPath();
 
    ctx.arc(radius, radius, radius, 0, Math.PI * 2);
    ctx.fillStyle = 'rgba(3, 133, 255, 0.5)';   //#0385ff
    //ctx.fillStyle = hex2rgba(fillColor, fillOpacity);
    ctx.fill();
 
    ctx.closePath();
     
    // 텍스트 표시
    ctx.textAlign = 'center';
    ctx.fillStyle = 'white';            // text color
     
    if (name != null) {
        ctx.font = '16px dotum bold';
        ctx.fillText(name, radius, radius - 4);
        y = radius + 20 - 4;
    } else {
        ctx.textBaseline = 'middle';    // 텍스트가 1줄일 경우 사용
        y = radius;
    }
     
    if (count != null) {
        ctx.font = '20px dotum bold';
        ctx.fillText(count, radius, y);
    }
 
    return {
        content: cvs,
        anchor: naver.maps.Point(radius, radius),
    }
}

[클러스터 마커 설정]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//-----------------------------------------------
// 클러스터 마커 설정
//-----------------------------------------------
var cMarker1 = newMarker(50, 'm1', 0);
var cMarker2 = newMarker(50, 'm2', 0);
var cMarker3 = newMarker(50, 'm3', 0);
 
// 마커 클러스터링
var markerClustering = new MarkerClustering({
    minClusterSize: 2,                      // 클러스터 마커를 표시할 최소 마커 개수
    maxZoom: 17,                            // 최대 지도 확대 레벨(maxZoom &gt; map zoom, 클러스터 마커 표시)
    map: map,                               // 클러스터 마커 표시할 지도
    markers: markers,                       // 클러스터 마커에서 사용할 마커 목록
    disableClickZoom: true,                 // 클러스터 마커 클릭 시 지도 확대여부
    //averageCenter: true,                  // 마커들의 중간 좌표를 계산하여 클러스터 마커 표시여부
    gridSize: 100,                          // 클러스터 마커 그리드 크기(단위: 픽셀)
    icons: [cMarker1, cMarker2, cMarker3],  // 클러스터 마커용 아이콘
    indexGenerator: [10, 50, 100],          // 아이콘 표시용 마커 개수 설정
    stylingFunction: function(clusterMarker, count, name) { // 클러스터 마커 갱신 시 호출
        _list = _list.concat(members);
         
        var radius = getRadius(count);
         
        // 클러스터 마커 표시
        clusterMarker.setIcon(newMarker(radius, count, name));
         
        // 클러스터 마커 이벤트 설정
        naver.maps.Event.addListener(clusterMarker, 'mouseover', function(e) {
            var cvs = clusterMarker.getIcon().content;
            clusterMarker.setIcon(changeMarker(cvs, radius + 15, count, name));
        });
 
        naver.maps.Event.addListener(clusterMarker, 'mouseout', function(e) {  
            var cvs = clusterMarker.getIcon().content;
            clusterMarker.setIcon(changeMarker(cvs, radius, count, name));
        });
         
        naver.maps.Event.addListener(clusterMarker, 'click', function(e) {
            clearList();
             
            members.forEach(function(item) {
                showMarkerInfo(item);
            });
        });
    }
});
 
// 마커 개수에 따라 반지름 변경
function getRadius(c) {
    var r = 50;
     
    switch (true) {
        case (c <  3): r = 30; break;
        case (c <  5): r = 40; break;
        case (c < 10): r = 50; break;
        case (c < 20): r = 60; break;
        case (c < 50): r = 70; break;
        case (c <100): r = 80; break;
    }
     
    return r;
}
 
// 마커 info 속성 표시
function showMarkerInfo(marker) {
    var info = $(marker.getIcon().content).attr('info');
    var json = attr2json(info);
    $('#list').append('마커 : code = '+ json.code +', name = '+ json.name +'');
}

[MarkerClustering.js 수정 부분]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//-----------------------------------------------
// MarkerClustering.js 수정 부분
//-----------------------------------------------
    /**
     * 클러스터의 아이콘, 텍스트를 갱신합니다.
     * @private
     */
    _updateClusters: function() {
        var clusters = this._clusters;
         
        clearList();    //추가
         
        for (var i = 0, ii = clusters.length; i < ii; i++) {
            clusters[i].updateCluster();
        }
         
        showList();     //추가
    },
     
    /**
     * 클러스터를 구성하는 마커 수를 갱신합니다.
     */
    updateCount: function() {
        var stylingFunction = this._markerClusterer.getStylingFunction();
         
        //추가
        var name = null;
         
        if (this._clusterMember.length > 0) {
            var cZoom = this._markerClusterer.getMaxZoom();
            var mZoom = this._markerClusterer.getMap().getZoom();
         
            //지도의 줌 레벨이 클러스터 마커 표시 최대 레벨일 때만 명칭 표시
            if (cZoom - 1 == mZoom) {
                //첫번째 HTML 마커의 text 속성 조회
                //문제점: 마커의 명칭이 여러 종류일 경우 명칭 표기에 혼선
                name = $(this._clusterMember[0].getIcon().content).attr('text');
            }
        }
        //추가
         
        stylingFunction && stylingFunction(this._clusterMarker, this.getCount(), name, this._clusterMember);    //추가
    },

[참고]

https://github.com/navermaps/marker-tools.js

네이버 지도 마커에 원 도형과 텍스트를 함께 표시하기 위해 사용자 정의 오버레이를 사용해보자.

 

[사용자 정의 오버레이 설정]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//--------------------------------------
// 사용자 정의 오버레이 설정
//--------------------------------------
// 사용자 정의 오버레이
var CustomOverlay = function(options) {
    this._element = $('');
     
    this._code = options.code || null;
    this._position = options.position;
    this._radius = options.radius || 50;
    this._fillColor = options.fillColor || '#0385ff';
    this._fillOpacity = options.fillOpacity || 0.6;
    this._strokeColor = options.strokeColor || '#0385ff';
    this._strokeOpacity = options.strokeOpacity || 0.6;
    this._strokeWeight = 1;
     
    this._textColor = options.textColor || 'white';
};
 
// CustomOverlay는 OverlayView를 상속받습니다.
CustomOverlay.prototype = new naver.maps.OverlayView();
CustomOverlay.prototype.constructor = CustomOverlay;

[필수 메소드 재정의] onAdd(), onRemove(), draw()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//--------------------------------------
// 필수 메소드 재정의
//--------------------------------------
CustomOverlay.prototype.onAdd = function() {
    // 오버레이 표시 레이어(floatPane(정보 레이어, 상단)/overlayImage(마커 레이어, 중단)/overlayLayer(도형 레이어, 하단)
    var overlayLayer = this.getPanes().floatPane;
     
    this._element.appendTo(overlayLayer);
};
 
CustomOverlay.prototype.draw = function() {
    // 지도 객체가 설정되지 않았으면 draw 기능을 하지 않습니다.
    if (!this.getMap()) {
        return;
    }
 
    // projection 객체를 통해 LatLng 좌표를 화면 좌표로 변경합니다.
    var projection = this.getProjection();
    var position = this.getPosition();
    var pixelPosition = projection.fromCoordToOffset(position);
     
    var radius = this._radius;
     
    this._element.css('left', pixelPosition.x - radius);
    this._element.css('top', pixelPosition.y - radius);
     
    var cvs = this._element[0];
    cvs.width = radius * 2;
    cvs.height = radius * 2;
     
    var ctx = cvs.getContext('2d');
     
    // 원 그리기
    ctx.beginPath();
     
    ctx.arc(radius, radius, radius - this._strokeWeight, 0, Math.PI * 2);   // 반지름 = radius - this._strokeWeight
    ctx.fillStyle = hex2rgba(this._fillColor, this._fillOpacity);   //rgba(3, 133, 255, 0.5) #0385ff
    ctx.fill();
     
    ctx.lineWidth = this._strokeWeight;         // 기본값: lineWidth = 1
    ctx.strokeStyle = hex2rgba(this._strokeColor, this._strokeOpacity); //기본값: strokeStyle = '#000000'
    ctx.stroke();
     
    ctx.closePath();
     
    // 원 내부에 텍스트 표시
    ctx.textAlign = 'center';
    //ctx.textBaseline = 'middle';                      // 텍스트가 1줄일 경우 사용
    ctx.fillStyle = hex2rgba(this._textColor, null);    // text color
    ctx.font = '14px dotum bold';
    ctx.fillText(this._code.name, radius, radius - 4);      // 글꼴의 크기에 따라 세로 출력 위치 조정
    ctx.font = '20px dotum bold';
    ctx.fillText(this._code.count, radius, radius + 20 - 4);    // 글꼴의 크기에 따라 세로 출력 위치 조정
};
 
CustomOverlay.prototype.onRemove = function() {
    this._element.remove();
     
    // 이벤트 핸들러를 설정했다면 정리합니다.
    this._element.off();
};

[속성 메소드 정의]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//--------------------------------------
// 속성 메소드 정의
//--------------------------------------
CustomOverlay.prototype.getCode = function() {
    return this._code;
};
 
CustomOverlay.prototype.setCode = function(code) {
    this._code = code;
};
 
CustomOverlay.prototype.getPosition = function() {
    return this._position;
};
 
CustomOverlay.prototype.setPosition = function(position) {
    this._position = position;
};
 
CustomOverlay.prototype.getRadius = function() {
    return this._radius;
};
 
CustomOverlay.prototype.setRadius = function(radius) {
    this._radius = radius;
};
 
CustomOverlay.prototype.getFillColor = function() {
    return this._fillColor;
};
 
CustomOverlay.prototype.setFillColor = function(color) {
    this._fillColor = color;
};

[hex to rgb, colorname to hex]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//--------------------------------------
// hex to rgb, colorname to hex
//--------------------------------------
// 색깔코드를 rgba로 변경(color, opacity)
function hex2rgba(c, o) {
    if (c.charAt(0) == '#') {
        c = c.substr(1);
    } else {
        return colorName2Hex(c);    //색깔명 사용할 경우 opacity 무시
    }
     
    if (o == null || o > 1.0 || o < 0) o = 1.0;
     
    if (c.length == 3) {
        c = c.substr(0,1) + c.substr(0,1) + c.substr(1,2) + c.substr(1,2) + c.substr(2,3) + c.substr(2,3);
    }
     
    var r = c.charAt(0) + '' + c.charAt(1); r = parseInt(r, 16);
    var g = c.charAt(2) + '' + c.charAt(3); g = parseInt(g, 16);
    var b = c.charAt(4) + '' + c.charAt(5); b = parseInt(b, 16);
     
    return 'rgba(' + r + ',' + g + ',' + b + ','+ o +')';
}
 
// 색깔명을 색깔코드로 변경
function colorName2Hex(color) {
    var colors = {"aliceblue":"#f0f8ff","antiquewhite":"#faebd7","aqua":"#00ffff","aquamarine":"#7fffd4","azure":"#f0ffff",
    "beige":"#f5f5dc","bisque":"#ffe4c4","black":"#000000","blanchedalmond":"#ffebcd","blue":"#0000ff","blueviolet":"#8a2be2","brown":"#a52a2a","burlywood":"#deb887",
    "cadetblue":"#5f9ea0","chartreuse":"#7fff00","chocolate":"#d2691e","coral":"#ff7f50","cornflowerblue":"#6495ed","cornsilk":"#fff8dc","crimson":"#dc143c","cyan":"#00ffff",
    "darkblue":"#00008b","darkcyan":"#008b8b","darkgoldenrod":"#b8860b","darkgray":"#a9a9a9","darkgreen":"#006400","darkkhaki":"#bdb76b","darkmagenta":"#8b008b","darkolivegreen":"#556b2f",
    "darkorange":"#ff8c00","darkorchid":"#9932cc","darkred":"#8b0000","darksalmon":"#e9967a","darkseagreen":"#8fbc8f","darkslateblue":"#483d8b","darkslategray":"#2f4f4f","darkturquoise":"#00ced1",
    "darkviolet":"#9400d3","deeppink":"#ff1493","deepskyblue":"#00bfff","dimgray":"#696969","dodgerblue":"#1e90ff",
    "firebrick":"#b22222","floralwhite":"#fffaf0","forestgreen":"#228b22","fuchsia":"#ff00ff",
    "gainsboro":"#dcdcdc","ghostwhite":"#f8f8ff","gold":"#ffd700","goldenrod":"#daa520","gray":"#808080","green":"#008000","greenyellow":"#adff2f",
    "honeydew":"#f0fff0","hotpink":"#ff69b4",
    "indianred ":"#cd5c5c","indigo":"#4b0082","ivory":"#fffff0","khaki":"#f0e68c",
    "lavender":"#e6e6fa","lavenderblush":"#fff0f5","lawngreen":"#7cfc00","lemonchiffon":"#fffacd","lightblue":"#add8e6","lightcoral":"#f08080","lightcyan":"#e0ffff","lightgoldenrodyellow":"#fafad2",
    "lightgrey":"#d3d3d3","lightgreen":"#90ee90","lightpink":"#ffb6c1","lightsalmon":"#ffa07a","lightseagreen":"#20b2aa","lightskyblue":"#87cefa","lightslategray":"#778899","lightsteelblue":"#b0c4de",
    "lightyellow":"#ffffe0","lime":"#00ff00","limegreen":"#32cd32","linen":"#faf0e6",
    "magenta":"#ff00ff","maroon":"#800000","mediumaquamarine":"#66cdaa","mediumblue":"#0000cd","mediumorchid":"#ba55d3","mediumpurple":"#9370d8","mediumseagreen":"#3cb371","mediumslateblue":"#7b68ee",
    "mediumspringgreen":"#00fa9a","mediumturquoise":"#48d1cc","mediumvioletred":"#c71585","midnightblue":"#191970","mintcream":"#f5fffa","mistyrose":"#ffe4e1","moccasin":"#ffe4b5",
    "navajowhite":"#ffdead","navy":"#000080",
    "oldlace":"#fdf5e6","olive":"#808000","olivedrab":"#6b8e23","orange":"#ffa500","orangered":"#ff4500","orchid":"#da70d6",
    "palegoldenrod":"#eee8aa","palegreen":"#98fb98","paleturquoise":"#afeeee","palevioletred":"#d87093","papayawhip":"#ffefd5","peachpuff":"#ffdab9","peru":"#cd853f","pink":"#ffc0cb","plum":"#dda0dd","powderblue":"#b0e0e6","purple":"#800080",
    "rebeccapurple":"#663399","red":"#ff0000","rosybrown":"#bc8f8f","royalblue":"#4169e1",
    "saddlebrown":"#8b4513","salmon":"#fa8072","sandybrown":"#f4a460","seagreen":"#2e8b57","seashell":"#fff5ee","sienna":"#a0522d","silver":"#c0c0c0","skyblue":"#87ceeb","slateblue":"#6a5acd","slategray":"#708090","snow":"#fffafa","springgreen":"#00ff7f","steelblue":"#4682b4",
    "tan":"#d2b48c","teal":"#008080","thistle":"#d8bfd8","tomato":"#ff6347","turquoise":"#40e0d0",
    "violet":"#ee82ee",
    "wheat":"#f5deb3","white":"#ffffff","whitesmoke":"#f5f5f5",
    "yellow":"#ffff00","yellowgreen":"#9acd32"};
 
    if (typeof colors[color.toLowerCase()] != 'undefined')
        return colors[color.toLowerCase()];
 
    return false;
}

[오버레이 표시 및 이벤트 설정]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//--------------------------------------
// 오버레이 표시 및 이벤트 설정
//--------------------------------------
var options = [
    { position: TEST1, radius: 50, code: { name: '숙소',   count: '5',  city: '11', sig: '11290', emd: '11200104', text: '테스트1' } },
    { position: TEST2, radius: 50, code: { name: '사무실', count: '99', city: '11', sig: '11290', emd: '11200104', text: '테스트2' } },
    { position: TEST3, radius: 50, code: { name: '맛집',   count: '3',  city: '11', sig: '11290', emd: '11200104', text: '테스트3' } },
];
 
var markers = [];
 
// 오버레이 생성
for (var idx in options) {
    markers.push(new CustomOverlay(options[idx]));
}
 
// 오버레이 표시, 이벤트 설정
for (var idx in markers) {
    markers[idx].setMap(map);
     
    markers[idx]._element.on('mouseover', changeRadius(idx, 100));
    markers[idx]._element.on('mouseout',  changeRadius(idx, 50));
    markers[idx]._element.on('click',     clickMarker(idx));
}
 
function changeRadius(idx, r) {
    return function(e) {
        var marker = markers[idx];
        marker.setRadius(r);
        marker.draw();
    }
}
 
function clickMarker(idx) {
    return function(e) {
        console.log(markers[idx].getCode());
    }
}

[참고]

https://navermaps.github.io/maps.js.ncp/docs/naver.maps.OverlayView.html

https://navermaps.github.io/maps.js.ncp/docs/tutorial-6-CustomOverlay.html

https://navermaps.github.io/maps.js.ncp/docs/tutorial-custom-overlay.example.html

 

준비물: 시도 shp, 시군구 shp, 읍면동 shp

프로그램: mapshaper, QGIS, python 3.9.1

 

국내 행정구역 shp 파일은 용량도 크고 너무 세밀하게 표현된 부분이 많아서

웹에 단순한 행정구역을 표시할 때 용량과 속도 등에서 제약사항이 발생한다.

 

국내 행정구역 shp 파일에서 사용된 좌표계는 GRS80 (EPSG: 5179) 이므로

네이버 지도에서 사용하는 위도와 경도 좌표계 WGS84 (EPSG: 4326)로 변환해야 한다.

 

변환 과정 요약

1. mapshaper 등의 프로그램으로 simplify 과정을 거쳐 단순화(shp ▶ shp)

2. QGIS 등의 프로그램을 이용하여 WGS 84 좌표로 변환(shp ▶ json(geojson))

3. python 등을 이용해 전체 geojson 파일에서 필요한 행정구역 파일로 분할 저장(json ▶ json(geojson))

 

[시군구 geojson 분할 저장] python 3.9.1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//--------------------------------------
// 시군구 geojson 분할 저장
//--------------------------------------
#python 3.9.1
import json
 
#json 파일 읽기
with open('d:\\sig_202005_wgs84.geojson', 'r', encoding='UTF8') as rf:
    geojson = json.load(rf)
 
#시군구 반복
for item in geojson['features']:
    code = item['properties']['SIG_CD']     #행정구역코드
    name = item['properties']['SIG_KOR_NM'] #행정구역명
    item['id'] = code;                      #네이버 지도용 id 설정
 
    #json 파일 저장 경로
    path = 'd:\\sig\\'+ code +'.json'
    print(path +' '+ name)
 
    #json 객체 생성
    geo = dict()
    geo['type'] = 'FeatureCollection'
    geo['features'] = []            #features는 배열
    geo['features'].append(item)
 
    #json 파일 저장
    with open(path, 'w', encoding='UTF8') as wf:
        json.dump(geo, wf, ensure_ascii=False#한글 유니코드 저장 방지

[행정구역 json 파일 로드]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//--------------------------------------
// 행정구역 json 파일 로드
//--------------------------------------
var HOME_PATH = window.HOME_PATH || '.',
    urlPrefix = HOME_PATH +'/data/region',
    urlSuffix = '.json',
    regionGeoJson = [],
    loadCount = 0;
 
// 행정구역 json 파일 로드
naver.maps.Event.once(map, 'init_stylemap', function () {
    for (var i = 1; i < 18; i++) {
        var keyword = i +'';
 
        if (keyword.length === 1) {
            keyword = '0'+ keyword;
        }
 
        $.ajax({
            url: urlPrefix + keyword + urlSuffix,
            success: function(idx) {
                return function(geojson) {
                    regionGeoJson[idx] = geojson;
 
                    loadCount++;
 
                    if (loadCount === 17) {
                        startDataLayer();
                    }
                }
            }(i - 1)
        });
    }
});

[Geojson 파일 형태]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//--------------------------------------
// Geojson 파일 형태
//--------------------------------------
// 툴팁 설정
var tooltip = $('<div style="position: absolute; z-index: 1000; padding: 5px 10px; background-color: #fff; border: solid 2px #000; font-size: 14px; pointer-events: none; display: none;"> </div>');
tooltip.appendTo(map.getPanes().floatPane);
 
// 로드된 행정구역은 아래와 같은 형태
regionGeoJson.push({
    "type": "FeatureCollection",
    "features": [{
        "id": "11140",
        "type": "Feature",
        "properties": {"SIG_CD": "11140", "SIG_ENG_NM": "Jung-gu", "SIG_KOR_NM": "중구"},
        "geometry": {
            "type": "MultiPolygon",
            "coordinates": [[[
                [127.0233653, 37.5719143], [127.0233917, 37.5719073], [127.0235305, 37.5671585], [127.0235359, 37.5669135], [127.0235319, 37.5668462],
                [127.0235326, 37.5668336], [127.0217938, 37.5575103], [127.0217328, 37.5575278], [127.0195773, 37.5574955], [127.0196807, 37.5572629],
                [127.0180939, 37.5568746], [127.0178901, 37.5567028], [127.0162301, 37.5528015], [127.0159829, 37.552589], [127.015938, 37.5525498],
                [127.015858, 37.552471], [127.0089881, 37.5441349], [127.0050173, 37.5461716], [127.0061278, 37.5481932], [127.0046885, 37.5486851],
                [127.0045604, 37.5498499], [127.0043733, 37.5501537], [127.0043557, 37.5501974], [127.0043362, 37.5502112], [127.0042094, 37.5501507],
                [127.0034213, 37.5500195], [127.0028642, 37.5496416], [127.0016841, 37.5500597], [126.9983251, 37.5496954], [126.9944593, 37.547488],
                [126.9787954, 37.5540872], [126.9724082, 37.5548773], [126.96571, 37.5541518], [126.963376, 37.552259], [126.962778, 37.551848],
                [126.9626113, 37.5517331], [126.9623428, 37.5515489], [126.9623691, 37.5516032], [126.962376, 37.5516286], [126.9624048, 37.5516477],
                [126.9623107, 37.55277], [126.9620066, 37.5540349], [126.9619314, 37.554448], [126.9617737, 37.5547951], [126.9617773, 37.554982],
                [126.9619781, 37.5551268], [126.9621418, 37.5555309], [126.9624769, 37.5559106], [126.9627196, 37.5559726], [126.9631727, 37.5565488],
                [126.9633575, 37.5566854], [126.9634369, 37.5568535], [126.9634273, 37.5569479], [126.9634093, 37.5569943], [126.963052, 37.5574148],
                [126.9630411, 37.5580298], [126.9620761, 37.5584566], [126.9615951, 37.5584184], [126.9617358, 37.5588129], [126.961711, 37.5589526],
                [126.9616892, 37.5589824], [126.9616726, 37.5590258], [126.962956, 37.5591763], [126.9632096, 37.5591958], [126.9633065, 37.5592155],
                [126.9668376, 37.561274], [126.9674439, 37.5615089], [126.9681526, 37.5616215], [126.969265, 37.5619273], [126.9694319, 37.5619679],
                [126.9694641, 37.5619758], [126.9693301, 37.5622305], [126.9689586, 37.5629149], [126.967562, 37.5648787], [126.9666982, 37.56582],
                [126.9668651, 37.5659861], [126.9668874, 37.5660078], [126.9966167, 37.5686865], [126.9968368, 37.5687099], [126.997025, 37.5687315],
                [127.0007881, 37.5693806], [127.0013078, 37.5694769], [127.014613, 37.5697183], [127.0147018, 37.5697206], [127.0176066, 37.5702101],
                [127.0177723, 37.5702912], [127.0233653, 37.5719143]
            ]]]
        }
    }]
});

[행정구역 표시]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//--------------------------------------
// 행정구역 표시
//--------------------------------------
function startDataLayer() {
    map.data.setStyle(function(feature) {
        var styleOptions = {
            fillColor: '#ff0000',
            fillOpacity: 0.0001,
            strokeColor: '#ff0000',
            strokeWeight: 4,
            strokeOpacity: 0.4
        };
 
        if (feature.getProperty('focus')) {
            styleOptions.fillOpacity = 0.6;
            styleOptions.fillColor = '#0f0';
            styleOptions.strokeColor = '#0f0';
            styleOptions.strokeWeight = 4;
            styleOptions.strokeOpacity = 1;
        }
 
        return styleOptions;
    });
 
    regionGeoJson.forEach(function(geojson) {
        map.data.addGeoJson(geojson);
    });
 
    map.data.addListener('click', function(e) {
        var feature = e.feature;
 
        if (feature.getProperty('focus') !== true) {
            feature.setProperty('focus', true);
        } else {
            feature.setProperty('focus', false);
        }
    });
 
    map.data.addListener('mouseover', function(e) {
        var feature = e.feature;
        var regionName = feature.getProperty('SIG_KOR_NM');
 
        tooltip.css({
            display: '',
            left: e.offset.x,
            top: e.offset.y
        }).text(regionName);
 
        map.data.overrideStyle(feature, {
            fillOpacity: 0.6,
            strokeWeight: 4,
            strokeOpacity: 1
        });
    });
 
    map.data.addListener('mouseout', function(e) {
        tooltip.hide().empty();
        map.data.revertStyle();
    });
}
 
startDataLayer();

참고

https://blog.hkwon.me/draw-korean-map-chart-with-geojson/

http://www.gisdeveloper.co.kr/?p=2332

아래의 파일을 업로드

~/styles 폴더의 shCore.css 및 테마(shTheme*.css) 파일
~/scripts 폴더의 shCore.js 및 브러시(shBrush*.js) 파일


HTML head 태그 부분에 아래의 문장 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<head>
    ...
    <link rel="stylesheet" media="screen" type="text/css" href="./images/shCore.css">
    <link rel="stylesheet" media="screen" type="text/css" href="./images/shThemeRDark.css"> //테마
    <script type="text/javascript" src="./images/shCore.js"></script>
    <script type="text/javascript" src="./images/shBrushCss.js"></script>
    <script type="text/javascript" src="./images/shBrushJava.js"></script>
    <script type="text/javascript" src="./images/shBrushJScript.js"></script>
    <script type="text/javascript" src="./images/shBrushCSharp.js"></script>
    <script type="text/javascript" src="./images/shBrushSql.js"></script>
    <script type="text/javascript" src="./images/shBrushXml.js"></script>
    <script type="text/javascript">SyntaxHighlighter.all();</script>
    ...
</head>

사용법

html 편집모드에서
1
2
3
<pre class="brush:언어별 브러시명">
    ... 코드 ...
</pre>

언어별 브러시명 및 사용 js 파일은 아래의 페이지를 참조

언어별 브러시

택배 조회 URL

기타2018. 9. 18. 13:29

아래의 택배 배송 조회 URL은


다음 배송조회 화면을 통해서 현재(2018.09.18) 확인된 URL 입니다.


아래 URL은 택배사의 사정에 따라 언제든지 변경될 수 있으니 확인하고 사용하세요.


CJ 대한통운

https://www.doortodoor.co.kr/parcel/doortodoor.do?fsp_action=PARC_ACT_002&fsp_cmd=retrieveInvNoACT&invc_no=운송장번호


우체국택배

https://service.epost.go.kr/trace.RetrieveEmsRigiTraceList.comm?POST_CODE=운송장번호


롯데택배https://www.lotteglogis.com/personalService/tracking/06/tracking_goods_result.jsp?InvNo=운송장번호


로젠택배

http://www.ilogen.com/iLOGEN.Web.New/TRACE/TraceView.aspx?gubun=slipno&slipno=운송장번호


한진택배

http://www.hanjin.co.kr/Delivery_html/inquiry/result_waybill.jsp?wbl_num=운송장번호


드림택배(구KG로지스)

http://www.idreamlogis.com/delivery/delivery_result.jsp?item_no=운송장번호

1
2
.image { position:relative;text-align:center;color:#FFFFFF; }
.image div { position: absolute;top:50%;left:50%;transform: translate(-50%, -50%); background-color:#FFFFFF;text-shadow: -1px 0 1px black, 0 1px 1px black, 1px 0 1px black, 0 -1px 1px black; }
1
2
3
4
<div class="image">
  <img src="">
  <div>이미지 위에 표시할 텍스트</div>
</div>

마이플랫폼에서 Base64 이미지 문자열을 이미지 컴포넌트에 표시하고자 할 때

java 단에서 먼저 해당 컬럼을  DB에서 조회한 다음 일반적인 DRRecordSet으로 반환하면

String 데이터 컬럼으로 변환되어 이미지 컴포넌트에 표시하지 못한다.


그래서 DRRecordSet을 새로 생성하여 해당 컬럼을 BLOB로 만들어서 반환하면

이미지 컴포넌트에서 이미지를 제대로 표시할 수 있다.


DSRecordSet rs = DB에서 조회한 목록


DSRecordSet rs2 = new DSRecordSet();

rs2.addColumn("base64Image", ColumnInfo.CY_COLINFO_BLOB, -1);


for (int i = 0; i < rs.getRowCount(); i++) {

int row = rs2.appendRow();

String imageData = rs.getColumn(row, "base64Image").toString();

rs2.setColumn(row, "base64Image", Base64.decode(imageData.getBytes()));

}


생성한 rs2를 반환한다.


이후 마이플랫폼에서 원하는 이미지 컴포넌트에 표시한다.


imgPhoto.ImageID = ds.GetColumn(nRow, 'base64Image');


단, 여기서 주의할 점은 이미지 컴포넌트 imgPhoto의 ImageID 값은 항상 null 이다.

그래서 if (imgPhoto.ImageID != '')와 같은 구문은 항상 거짓이 된다.


그리고 이미지 컴포넌트의 이미지를 지우거나 초기화할 경우 빈문자열 대신 null을 이용한다. imgPhoto.ImageID = null