MySQL에서 제공하는 st_centroid 함수에 대한 글은 저번글에서 작성을 했었습니다.

이번글에서는 st_centroid 함수와 추가로 st_x, st_y 함수를 이용했던 사례를 쓰고 싶었습니다.

지바이크의 사용자 앱인 지쿠터 앱을 열어서 서울 지역의 지도를 확대해보면 아래 이미지와 같은 팔각형의 주차금지 구역이 있음을 확인 할 수 있습니다.

서울시 정책에 의해서 견인구역 주차시 견인금 징수라는 개념이 생겼고 아래 이미지와 같은 상황에 주차를 할 경우 견인료를 징수하게 되는 상황이 발생하였습니다.

초기에는 해당 위치에 주차금지구역이 없었고 사용자가 해당 위치에 주차를 하여 견인이 된 경우 해당 사용자에게 견인료를 징수하는 정책이었으나 해당 위치를 주차금지구역으로 하여서 주차 자체를 불가능하게끔 하도록 정책이 바뀌었습니다.

횡단보도, 지하철 출입구 등 수많은 곳에 10m 이내로 주차금지 구역을 그렸어야 했는데 사람이 일일이 그리기에는 수가 너무나도 많았기에 지하철 출입구와 횡단보도 같은 경우는 서울시에서 제공하는 공공데이터를 엑셀로 받아다가 일괄로 밀어넣는 방법을 선택했고 그후 남은 구역을 직원분들이 직접 추가하는식으로 작업하였습니다.

제가 해당 상황에서 했던 일은..
세가지 정도로 말할 수 있을 것 같습니다.

마커를 팔각형의 견인구역으로 만들기

제가 처음에 해야 했던일은 관리자가 하나의 중점을 찍으면 그 중점을 기준으로 반경내 Nm 의 팔각형으로 만들어야 하는 일이었습니다.

네이버맵에서는 마커와 폴리곤 그리기는 제공하는데 하나의 점을 찍으면 다각형을 만들어주는 함수는 검색해도 나오지 않아서 어쩌지 하다가
그런 기능을 직접 만들자! 생각했고 방법은 대략 아래와 같습니다.

  • 견인구역 중점이 되는 위치에 마커를 찍는다.
  • 마커를 찍는 이벤트(markerAdded)가 발생하면 Listener에 의해서 읽어들여서 해당 중점을 통해 8개의 점을 구한다.
  • 8개의 점을 이용하여 하나의 폴리곤을 추가시킨다.
  • 초기에 찍었던 마커는 삭제 처리한다.

였습니다.

팔각형 만들기 구상

  • Latitude (1도) 1Km = 1 / 109.958489129649955 = 0.0091
  • Longitude (1도) 1Km = 1 / 88.74 = 0.0113
  • lat 1m = (1/1000/109.958489129649955) = 0.00000909
  • lng 1m = (1/1000/88.74) = 0.00001127

각 위도, 경도의 1m에 대한 수치는 위처럼 서로 다릅니다.
때문에 10m 이내로 하기위해선 반경을 5m씩 잡으면 되겠다고 생각하였고 각 5m에 대한 수치는 아래와 같습니다.

  • lat 5m = 0.00000909 * 5
  • lng 5m = 0.00001127 * 5

중점을 통해 하나의 사각형을 그리고 그 사각형을 가지고 위-아래, 왼쪽-오른쪽 으로 (반경/2) 만큼 움직이면 팔각형이 만들어지겠구나 라고 생각했고 로직을 구현했습니다.

위 이미지는 그때 로직을 작성하기 전에 그렸던 그림입니다.

대략적인 팔각형 그리기 로직은 아래와 같습니다.
(모든 코드를 공개할 수는 없을 것 같아서 제가 작성한 코드에 대해서만 올립니다.)

/**
 * marker 생성 시 폴리곤으로 만들어준다.
 */
drawingManager.addListener('markerAdded', function (overlay) {
    const lat = overlay.position['_lat'];
    const lng = overlay.position['_lng'];
    //10mX10m
    const lat_radius = 0.00000909 * 5; //반경 5m정도로 폴리곤 그려주기
    const lat_radius2 = lat_radius / 2; //2로 나눈 이유는 정사각형 모서리를 기준으로 두개의 꼭지점을 만들어서 팔각형을 만들기 위함!!
    const lng_radius = 0.00001127 * 5; 
    const lng_radius2 = lng_radius / 2;
    //하나의 사각형
    const markerPolygon = {
        'max_lat': lat + lat_radius,
        'max_lng': lng + lng_radius,
        'min_lng': lng - lng_radius,
        'min_lat': lat - lat_radius
    };

    let polygon = [];
    //사각형을 기준으로 정팔각형을 그린다.
    if (lat !== 0 || lng !== 0) {
        //min_lat , min_lng
        polygon.push({'lat': markerPolygon["min_lat"] + lat_radius2, 'lng': markerPolygon["min_lng"]});
        polygon.push({'lat': markerPolygon["min_lat"], 'lng': markerPolygon["min_lng"] + lng_radius2});

        polygon.push({'lat': markerPolygon["min_lat"], 'lng': markerPolygon["max_lng"] - lng_radius2});
        polygon.push({'lat': markerPolygon["min_lat"] + lat_radius2, 'lng': markerPolygon["max_lng"]});

        polygon.push({'lat': markerPolygon["max_lat"] - lat_radius2, 'lng': markerPolygon["max_lng"]});
        polygon.push({'lat': markerPolygon["max_lat"], 'lng': markerPolygon["max_lng"] - lng_radius2});

        polygon.push({'lat': markerPolygon["max_lat"], 'lng': markerPolygon["min_lng"] + lng_radius2});
        polygon.push({'lat': markerPolygon["max_lat"] - lat_radius2, 'lng': markerPolygon["min_lng"]});
    }
    
    //해당 8개의 점으로 폴리곤 그리는 함수 호출
    지바이크_폴리곤_그리기_함수(polygon);

    overlay.setMap(null); //초기 선택했던 좌표에 대한 마커는 삭제한다.
});

이제 한가지 일에 대한 작성이 끝났습니다.
해당 내용으로는 한개씩 마커를 계속 찍어야 하는 수밖에 없습니다.

다음은 두번째 작업 내용입니다.

서울시 공공데이터 팔각형으로 밀어넣기

서울시 횡단보도&지하철 출입구에 대한 좌표 엑셀파일은 이미 다른 직원분에 의해서 받은 상태였고 밀어넣게끔 작업을 해주면 됐습니다.

시나리오는 아래와 같습니다.

  • 엑셀 읽어오기
  • 반복문 돌려서 하나의 좌표를 팔각형으로 만들고 저장시킨다.

지바이크 > 저희 팀에서 현재 사용중인 개발언어는 PHP 였기에 위 JS로 작성했던 내용을 PHP 코드로 바꾸고 엑셀을 받아서 반복문을 돌려주는 것에 대한 것만 달라졌습니다.

로직자체는 위 JS 코드와 동일하고 개발 언어만 다르기때문에 추가하지 않겠습니다!

다음은 세번째 작업 내용입니다.

보나, 견인구역 크기를 키워주세요!

보나는 저희 회사에서의 제 영어 이름입니다🌝

이렇게 밀어넣은 견인구역이 앱상에서 작게 보이고 그밖에 몇몇 이유로 하여 기존 크기의 N%정도만 키워달라는 요청이 들어왔습니다.

이때 제가 했던 것은

  • Insert된 견인구역 Select
  • 각각의 견인구역에 대한 폴리곤으로 st_centroid, st_x, st_y 함수를 통해 중점 구하기
  • 구한 중점 좌표를 이용하여 기존로직에서 반경 수치만 변경하여 팔각형 만들고 update

입니다!

st_x, st_y, st_centroid

st_centroid는 저번글에서 말한 것처럼 폴리곤의 중점을 구하는 함수입니다.

  • st_x : longtitue(경도)
  • st_y : latitude(위도)
select 
	st_x(st_centroid(geo_col)) as lng,
    st_y(st_centroid(geo_col)) as lat
from
	test_tb
where 견인구역 조건;

위 SQL과 같은 형식으로 경도, 위도에 대한 값을 구할 수 있었고 해당 값을 사용하여 견인구역 팔각형에 대한 크기를 키울 수 있었습니다.

견인구역 키우기 요청이 들어왔을때 중점을 어떻게 구하지하면서 머리로 하려다가 검색을 해보니 st_centroid, st_x, st_y 라는 고마운 함수들이 있었고 그것을 사용해서 무사히 처리했던 것 같습니다.

그밖에도 자잘하게 처리했던 내용이 몇가지 있지만 위 세가지 내용선까지만 공개하는게 맞는 것 같습니다.🌝

길을 걷다가 가끔씩 주차를 하면 안되는 곳에 주차돼있거나 쓰러져 있는 지쿠터가 눈에 들어오면 마음이 아프곤 합니다.
모든 사람들이 예쁘게 주차하고 안전하게 운전하는 예쁜 킥보드 문화가 생겼으면 좋겠습니다.
이렇게 견인구역 관련해서 했던 제 작은 작업이 그런 문화를 만드는데 작은 보탬이 되었으면 좋겠습니다.

profile
백엔드 개발자

0개의 댓글