주소 좌표 변환 툴, Geocoder-Xr

이 프로그램은 2022년 7월 29일까지만 서비스 됩니다. 대신 웹에서 동일한 기능을 제공하는 GEOSERVICE-WEB을 이용하시면 됩니다. GEOSERVICE-WEB에 대한 간단한 소개는 아래 소개를 참고하시기 바랍니다.

지오서비스웹(GEOSERVICE-WEB)

아래부터는 Geocoder-Xr에 대한 내용입니다.

개 요

Geocoder-Xr은 주소를 좌표로 변환하거나 좌표를 주소로 변환하여 그 결과를 SHP 파일로 저장해 주는 툴입니다. 전국의 지번주소와 도로명주소를 기반으로 지속적으로 DB가 업데이트됩니다. 이 프로그램은 특별한 공지가 없는 한 사용에 있어서 변환 건수 등과 같은 제한이 없습니다. 본 서비스의 사용자가 많은 관계로 보다 더 많은 사용자가 원할하게 이용할 수 있도록 서비스 속도가 실제보다 수초 정도 지연될 수 있습니다.

Release Note

  • [2022/08/01] 이 프로그램은 더 이상 사용할 수 없으며 웹버전인 GEOSERVICE-WEB을 이용하시기 바랍니다.
  • [2022/02/28] 주소DB(도로명주소, 지번주소, 행정동)가 2022년 2월 DB로 업데이트 되었습니다.
  • [2021/01/19] 기존의 정좌표 및 인근좌표를 포함하여 새롭게 대표좌표(행정명, 도로명)를 지원하며 주소 DB(지번주소, 도로명주소, 행정동)가 2021년도 1월 자료로 업데이트 되었습니다.
  • [2020/06/17] 지오코딩 정확도 개선과 주소 DB(지번주소, 도로명주소, 행정동)가 2020년도 5월 자료로 업데이트 되었습니다.
  • [2020/06/10] 지오코딩 정확도 향상 및 인근좌표 변환 기능을 지원합니다. 기존 버전도 그대로 사용할 수 있지만 보다 원할하게 사용하기 위해서 버전4를 다운로드해 사용하시기 바랍니다.
  • [2020/04/10] 주소에서 불필요한 문자열을 제거하는 코드가 추가되어 성공율을 향상시켰습니다.
  • [2020/04/06] 속성 문자에 대한 인코딩을 UTF-8과 EUC-KR(기본값) 중 선택할 수 있도록 개선 되었습니다.
  • [2019/10/23] 법정동 뿐만 아니라 행정동(2019년 10월 DB)에 대한 지오코딩이 가능하도록 개 선되었습니다.
  • [2019/10/16] 도로명주소 및 지번주소가 2019년 9월 DB로 업데이트되었습니다.
  • [2019/07/17] 도로명주소가 2019년 5월 DB로 업데이트되었으며, 좌표 변환시 타원체가 동일한 경우에는 변환 파라메터를 적용하지 않도록 하여 정확한 좌표로 변환됩니다.
  • [2018/12/13] 주소를 좌표로 변환하는 기능 뿐만 아니라 좌표를 주소로 변환할 수 있는 기능, 주소를 좌표로 변환 시 좌표체계 설정 기능 추가 및 한글깨짐 문제 등이 해결 되었습니다.
  • [2018/11/01] 지번주소 및 도로명주소 DB가 2018년 10월 데이터로 업데이트 되었습니다.
  • [2017/06/08] 주요지명(POI) 및 필지가 ‘산’인 주소에 대한 지오코딩이 가능하며 지오코딩 성공율이 더욱 향상되었습니다.
  • [2017/05/11] 3.0으로 버전업 되었으며 도로명 주소와 지적도가 각각 2017년 3월, 2017년 5월 구축 DB로 업데이트 되었습니다.
  • [2016/05/03] 도로명 주소 데이터가 최신 DB(2016년 2월 버전)으로 업데이트 되으며, 지적도 DB를 활용하여 지번주소도 변환이 가능합니다.
  • [2015/06/06] 도로명 주소 데이터가 최신 DB(2015년 6월 버전)으로 업데이트 되었습니다.
  • [2014/09/28] 도로명 주소 데이터가 최신 DB(2014년 9월 버전)으로 업데이트 되었으며 매칭율이 더 향상 되었습니다.

지원되는 EPSG

주소를 좌표로 변환할 때 적용되는 좌표계는 EPSG 방식으로 지정할 수 있으며 현재 지원되는 EPSG는 다음과 같습니다.

  • EPSG:4326 – WGS84 타원체의 경위도 좌표계
  • EPSG:3857 – Google, VWorld의 좌표계
  • EPSG:5174 – 경도 10.405초 보정된 Bessel 타원체의 TM 중부원점
  • EPSG:5179 – GRS80 타원체의 UTM-K 좌표계

사용 설명

주소를 좌표로 변환하기

변환할 주소는 아래의 그림처럼 엑셀 또는 메모장을 통해 CSV 포맷으로 저장하여 사용할 수 있습니다. CSV는 각 데이터를 컴마(,)로 구분하는 데이터 형식이며 첫줄은 반드시 영문의 필드명으로 구성되어야 합니다.

이 입력 데이터를 GeoCoder-Xr 프로그램에서 “입력파일” 버튼을 통해 읽은 후 주소 필드를 지정하고 결과 포맷으로 저장할 SHP 파일을 지정하면 아래의 화면처럼 구성됩니다.

이제 좌표로 변환하기 위한 시작 버튼을 누르면 좌표로 변환됩니다. 성공적으로 변환되면 아래의 화면처럼 변환된 지점이 지도에 표시됩니다.

좌표를 주소로 변환하기

반대로, 좌표를 주소로 변환할 수 있으며 아래와 같은 화면처럼 입력값들을 지정하면 됩니다.

설치

아래의 설치 파일을 다운로드 받아 바로 설치하시면 됩니다.

Geocoder-Xr은 인터넷을 통해 지오코딩 기능을 수행하므로, 사용자의 IP와 함께 주소 데이터가 수집됩니다. 수집된 데이터는 더 나은 지오코딩 서비스 개선을 위해 활용됩니다. 이러한 사용자의 IP와 주소 데이터의 수집에 대해 동의할 경우에만 Geocoder-Xr을 이용하기 바랍니다.

사용에 있어 문의점이나 개선점에 대한 피드백은 댓글로 남겨 주시기 바랍니다.

#GWC UI Library : Tree

웹 UI 라이브러리인 GWC에서 제공하는 Tree 컴포넌트에 대한 예제 코드입니다.

먼저 DOM 구성은 다음과 같습니다.

그리고 CSS 구성은 다음과 같구요.

.center {
    display: flex;
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    gap: 1em;
}

gwc-vscrollview {
    width: 22em;
    height: 30em;
    background: rgba(0,0,0,0.3);
    border: 1px solid black;
}

gwc-tree {
    width: 100%;
    padding: 0.5em 0.5em;
}

.h-center {
    display: fex;
    justify-content: center;
    align-items: center;
}

js 코드는 다음과 같습니다.

window.onload = () => {
    // 최상위 폴더(root) 
    const rootFolder = tree.rootFolder;
    
    // 폴더 추가
    const gaFolder = rootFolder.addFolder("아카이브");
    const docFolder = rootFolder.addFolder("문서").open();

    rootFolder.addFolder("프로그램");
    const programFolder = rootFolder.getFolder("프로그램");

    const humanGeoFolder = gaFolder.addFolder("인문공간데이터");
    const adminGeoFolder = gaFolder.addFolder("행정경계");

    // 폴더에 파일 추가    
    humanGeoFolder.addFile("인구통계.zip", "url(../examples/images/icon7.png)");
    humanGeoFolder.addFile("유동인구.zip", "url(../examples/images/icon7.png)");
    humanGeoFolder.addFile("유아통계.zip", "url(../examples/images/icon7.png)");

    const koreaFolder = adminGeoFolder.addFolder("대한민국");
    koreaFolder.addFile("시도.zip", "url(../examples/images/icon3.png)");
    koreaFolder.addFile("시군구.zip", "url(../examples/images/icon3.png)");
    koreaFolder.addFile("읍면동.zip", "url(../examples/images/icon3.png)");

    adminGeoFolder.addFile("서울특별시.zip", "url(../examples/images/icon3.png)");
    adminGeoFolder.addFile("경기도.zip", "url(../examples/images/icon3.png)");

    docFolder.addFile("레포트1.pdf", "url(../examples/images/icon6.png)");
    docFolder.addFile("레포트2.pdf", "url(../examples/images/icon6.png)");
    docFolder.addFile("레포트3.pdf", "url(../examples/images/icon6.png)");

    programFolder.addFile("VisualSudio.Code.zip", "url(../examples/images/icon5.png)")
    programFolder.addFile("PhotoShop.zip", "url(../examples/images/icon5.png)")
    programFolder.addFile("리터널_PS5.zip", "url(../examples/images/icon5.png)")

    // 폴더 열고 닫기
    btnProgramOpenClose.addEventListener("click", () => {
        const folder = tree.rootFolder.getFolder("아카이브");
        if(folder.isOpen()) folder.close();
        else folder.open();
    });

    // 특정 폴더에 파일 추가
    btnAddFile.addEventListener("click", () => {
        const folder = tree.rootFolder.getFolder("문서");
        const file = folder.getFile("NEW 레포트.pdf");
        if(file) {
            file.remove();
        } else {
            const file = folder.addFile("NEW 레포트.pdf", "url(../examples/images/icon6.png)");

            // 파일 또는 폴더에 사용자 정의 데이터 추가
            file.setData("생성일자", "2022년 2월 10일");
            console.log(file.getData("생성일자"));
        }
    });

    // 파일(폴더+파일)에 대한 클릭 이벤트
    tree.addEventListener("fileClick", (event) => {
        const file = event.detail.file;
        
        const parentFolderName = file.parentFolder.name;
        const bFolder = file.isFolder();
        const bOpen = file.isOpen();

        label.content = `
            이름: ${file.name} 
            부모폴더: ${parentFolderName?parentFolderName:"없음"} 
            종류: ${bFolder?"폴더":"파일"} 
            ${bFolder?`상태: ${bOpen?"열림":"닫힘"}`:""}
        `;

        vscrollview.refresh(); // 폴더 열기로 인한 트리 컴포넌트 크기 변경에 따른 스크롤뷰 업데이트
    });

    // 트리 컴포넌트의 크기가 가변이므로 스크롤뷰를 업데이트 해줌
    vscrollview.refresh();

    GeoServiceWebComponentManager.instance.update();
};

실행 결과는 다음과 같습니다.

트리의 항목에 대한 팝업 메뉴 기능을 적용할 때에 대한 코드입니다.

// PopupMenu 생성 시작
const popupMenu = gwcCreatePopupMenu();
popupMenu.addMenuItem("menu1", {
    text: "생성",
    _icon: "images/icon1.png",
    onClick: (menuId) => { 
        gwcMessage(`폴더 생성(${menuId})`);
        popupMenu.hide();                
    }
});
popupMenu.addMenuItem("menu2", {
    text: "이름 변경",
    _icon: "images/icon2.png",
    onClick: (menuId) => { 
        gwcMessage(`${popupMenu.fileData.name} 이름 변경(${menuId})`);
        popupMenu.hide();                
    }
});
popupMenu.addMenuItem("menu3", {
    text: "삭제",
    _icon: "images/icon3.png",
    onClick: (menuId) => { 
        gwcMessage(`${popupMenu.fileData.name} 삭제(${menuId})`);
        popupMenu.hide();
    }
});
popupMenu.addMenuItem("menu4", {
    text: "잘라내기",
    _icon: "images/icon4.png",
    onClick: (menuId) => { 
        gwcMessage(`${popupMenu.fileData.name} 잘라내기(${menuId})`);
        popupMenu.hide();
    }
});
popupMenu.addMenuItem("menu5", {
    text: "붙여넣기",
    _icon: "images/icon5.png",
    onClick: (menuId) => { 
        gwcMessage(`${popupMenu.fileData.name}에 붙여넣기(${menuId})`);
        popupMenu.hide();
    }
});
// PopupMenu 생성 완료

// 파일(폴더+파일)에 대한 클릭 이벤트
tree.addEventListener("fileClick", (event) => {
    const file = event.detail.file;
    const contextMenu = event.detail.contextMenu;

    if(contextMenu)  {
        console.log(event.detail.originalEvent);
        popupMenu.fileData = file; // fileData는 임의로 부여한 속성으로 팝업 메뉴 실행 시에 참조됨
        popupMenu.show(event.detail.originalEvent.clientX, event.detail.originalEvent.clientY);
    }
});

위의 코드에 대한 실행 결과는 다음과 같습니다.

트리를 구성하는 항목에 대해서 선택된 항목이라는 피드백을 줄 수 있습니다. 다음 코드를 참고하기 바랍니다.

tree.clearSelection(); // 일단 기존의 선택된 항목에 대해 선택 해제
tree.rootFolder.getFolder("아카이브").selected = true; // 폴더를 얻고 해당 폴더를 선택된 상태로 표시

폴더 또는 파일 항목의 우측에 Tag 정보를 표시할 수 있습니다. 즉, getFolder 또는 getFile을 통해 얻은 item 객체의 tag 속성(get, set)을 설정하면 됩니다. 아래는 파일항목의 우측에 파일의 크기를 표시하는 예시입니다.

tree 컴포넌트는 동일한 계층에 동일한 이름을 가진 항목을 추가할 수 없습니다. 이때 label 속성을 이용해 이름은 다르지만 표시되는 제목만을 변경해 줄 수 있습니다. 코드 예시는 다음과 같습니다.

data.forEach(item => {
    const rootFolder = this.#tree.rootFolder;
    rootFolder.addFile(item.id, "url(../images/layers.svg)").label = item.title;
});

#GWC UI Library : Memo

웹 UI 라이브러리인 GWC에서 제공하는 Memo 컴포넌트에 대한 예제 코드입니다.

먼저 DOM 구성은 다음과 같습니다. gwc-resizable-panel 태그로 감싸서 크기 조정이 가능하도록 했습니다. 이는 옵션입니다.

그리고 CSS 구성은 다음과 같구요.


.center {
    display: flex;
    width: 100%;
    height: 100%;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    gap: 0.5em;
}

.vcenter {
    display: flex;
    align-items: center;
    gap: 0.3em;
}

.hcenter {
    flex-direction: column;
    display: flex;
    _align-items: center;
}

gwc-memo {
    width: 100%;
    height: 100%;
}

js 코드는 다음과 같습니다.

window.onload = () => {
    button1.onclick = () => {
        memo.value = "메모에 대한 내용은 코드로 변경할 수 있어요.\n줄 바꿈도 가능하답니다.";
    }

    button2.onclick = () => {
        gwcMessage(memo.value);
    }

    button3.onclick = () => {
        console.log(memo.disabled);
        memo.disabled = !memo.disabled;
    }

    button4.onclick = () => {
        console.log(memo.readonly);
        memo.readonly = !memo.readonly;
    }

    memo.addEventListener("change", (event) => {
        labelEvent.content = `${memo.value.length}자가 입력됨 (입력가능 문자수 ${memo.maxLength})`;
    })

    GeoServiceWebComponentManager.instance.update();
   
};

실행 결과는 다음과 같습니다.

gwc-memo 태그 선언을 통해 value 속성에 문자열을 지정할 때 문자열에 쌍따옴표가 있을 경우 변환이 필요합니다.

<gwc-memo value="${params.content.replaceAll("\"", "&quot;")}"></gwc-memo>