본문 바로가기

Function

서울 36개 업종 증감 인터랙티브 지도 (2006-2014)


2006년~2014년의 8년간 한식 증감지도. 

붉은색은 8년간 한식 음식점이 증가한 곳, 푸른색은 감소한 곳이다. 진한 곳은 많이 증가(혹은 감소)한 곳



36개 업종의 증감 지도. [이 곳]에서 직접 클릭을 하여 원하는 업종의 증감, 혹은 조건 간 교집합을 볼 수 있다. 


링크를 눌러 나오는 화면에서 디스플레이 창 오른쪽 밑의 open 버튼을 누르면 새 창에서 좀 더 큰 화면으로 볼 수 있다. 휴대폰에서는 약간 다루기가 까다로울 것이며, 마우스를 이용한 PC에서 좀 더 잘 작동한다. 세로 해상도 1020 픽셀 이상의 모니터에서 36개 업종 버튼이 모두 다 보일 것이다. 처음 로딩되는데는 약간의 시간(5초 정도?)이 걸린다. 크롬 브라우져에서 좀 더 빠르게 반응한다.(사파리는 비교하지 못했음)


아래에서는 

1. 만든 과정과 

2. 간단한 사용법을 설명한 후, 

3. 몇 장의 지도를 만들어 내용을 들여다보도록 하겠다.


 







만든 과정



개요


사용한 자료는 2006년과 2014년 두 해의 36개 사업체 자료다.


각 업체마다 [업종, 경도, 위도, 연도] 이런 자료가 있다.

예를 들면 ["한식", 126.980093, 37.570553, 2006] 과 같다. 2006년에 어느 한식 업체가 126.980093,37.570553의 위치에 있다는 뜻이다.


위치를 알고 있으므로, 서울 전체를 200m x 200m 의 작은 격자들로 분할한다면 그 격자 안에 몇 개의 업체가 있는지 셀 수 있다.

예를 들어 2006년에 한식이 그 격자 안에 25개 있고, 2014년에 30개 있으면 증감은 +5가 된다. 전반적으로 이러한 계산을 거쳤다.



격자 안 업종 수 세기


특정 해 격자 안의 업종 수를 세는 방법은 대략 두 가지 정도가 있을 듯하다.


각 격자마다 모든 업체의 위치를 비교하여 그 범위 안에 있으면 숫자를 증가 시키는 방법이 있고,

한 업체의 경위도를 적당히 빼고 나누어서 격자 중 어느 곳으로 보내는 방법이 있다.


후자가 계산 시간이 빠르므로 후자의 방법으로 계산했다.



좀 더 구체적으로는, 

우리나라 부근에서 위도 1도는 약 110,000m 이고 경도 1도는 약 88,740m에 해당한다.

그러므로 가로 방향(경도)으로 200미터는 다음과 같이 계산한다. 코드는 Java의 문법이다.


float spacingR = 200 * (1 / 88740f);

세로 방향(위도)도 마찬가지로 계산한다.

float spacingC = 200 * (1 / 110000f);


서울의 경위도 경계를 넉넉히 설정하여 다음과 같이 둔다.


float latmin = 37.4249f;

float latmax = 37.704f;

float lngmin = 126.76f;

float lngmax = 127.1855f;


그러면 위도 방향의 격자 수와 경도 방향의 최대 격자 수는 다음과 같이 구할 수 있다.  소숫점 이하는 버린다.


int c1 = (int)((latmax - latmin) / spacingC); //위도 셀 개수 (세로)

int r1 = (int)((lngmax - lngmin) / spacingR); //경도 셀 개수 (가로)


위의 숫자로 계산하면 세로 방향으로 236개, 가로 방향으로 197개의 격자가 나온다. 총 46,492개의 격자다.


그렇다면 각 업체의 경위도는 다음과 같은 계산을 거쳐 몇 번째 방에 해당하는지 알 수 있다.


int c2 = (int)(((업체 위도) - latmin) / spacingC);

int r2 = (int)(((업체 경도) - lngmin) / spacingR);


앞서 가로 r1개, 세로 c1개의 2차원 배열의 값들을 0으로 초기화시켰다면,

이제 계산된 [r2,c2] 번째 방의 값들을 1씩 증가시키면 된다.


약간 복잡해 보일 지 모르지만, 사실 간단한 산수다.




Javascript에서 그리기


그리하여 36개 업종이 하나도 없는 셀들을 제외하고 표시하면 아래와 같은 그림이 나온다. 몇 개인지는 세어보지 않았다.




Java 에서 Javascript로 자료를 넘겨 구글 지도 OPEN API를 이용하여 웹브라우저에서 돌아가도록 할 계획이다.

따라서 Javascript에서는 계산을 최소화 하기 위해, Java에서 각 셀들의 폴리곤 좌표와 그 좌표의 업종 증감수만 배열로 종합하여 보낸다.


이를테면, 도형(각 셀, 정사각형)의 좌표는 아래와 같이 보내고,

[[127.041725,37.683083],[127.043976,37.683083],[127.043976,37.684902],[127.041725,37.684902],[127.041725,37.683083]],


그 셀에 해당하는 업종 증감은 다음과 같이 보낸다. 36개의 원소로 이루어진 배열이다.

[0,1,"x","x","x",1,1,"x",-1,"x",0,0,0,1,-2,0,0,"x",0,"x",0,0,3,0,0,0,-1,0,-1,1,-1,-1,1,"x","x","x"],

0은 증감이 0인 것. "x"는 해당 업종이 2006년과 2014년에 전혀 없었다는 것을 말한다.


Javascript에서는 자료를 읽어 색상을 계산한 후 입력받은 좌표의 사각형을 그리고 그 색을 입히도록 했다.



색상 표현


시각화 된 결과물은 한 눈에 보았을 때 직관적으로 인지되어야 하기 때문에 색상 표현은 매우 중요하다.

여기서는 진하기 표현에 신경을 썼다.


아래의 설명이 좀 장황할텐데, 간단히 요약하면 '증감치가 아주 쎄게 나오는 부분은 확 찐하게 표현했다' 만 기억하고 밑의 지도 API 설명으로 넘어가면 된다.




처음에는 각 업종마다 감소의 최대치와 증가의 최대치를 다음과 같이 두었다. 푸른색이 감소, 붉은색이 증가다.




그런데 일부 업종의 분포가 직관적으로 보이지 않았다. 

두 가지 문제가 있었는데, 

첫번째는 대부분의 분포를 넘어서는 돌출값들이 잘 표현되지 않는다는 것,

두번째는 -10~300의 분포 혹은 -300~10의 분포처럼 최대값과 최소값의 절대값이 크게 다를 경우 잘 표현되지 않는다는 점이었다.


첫번째의 경우, 예를 들어 의류업 같은 경우 대부분의 격자에서 10개 안팎의 증감을 보이는데 남대문시장 근처의 어떤 격자는 8년동안 무려 335개의 증가를 보인다. 이렇게 돌출되는 값을 드러내는 방법을 고려해야만 했다.


시행착오를 거쳐 다음과 같은 방법을 택했다.


특정 업종의 전체 증감 숫자들의 배열에서 상위 혹은 하위 2% 를 넘는 값들은, 기존의 색 경계에서 확장시켜 양 끝단에 새로 추가한 색들로 표시될 수 있도록 했다. (아래 색상 그라데이션 참고)




예를 들어 전체 증감의 분포가 -10 ~ 300 개일 경우, 최소값의 절대값(10)보다 최대값의 절대값(300)이 크므로 0~300을 d3.quantile 함수(자바스크립트 d3.js 라이브러리)에 넣고 상위 2%를 넘는 값부터 기존의 경계에서 확장된 색을 사용한다. 이 경우 감소를 나타내는 푸른색도 양의 척도를 뒤집어 같은 척도(-300~0)를 적용시키는데, 그렇게 되면 최소값인 -10의 감소는 거의 하얀 색에 가까운 푸른색으로 표시된다.


즉, 증가와 감소 중 절대값의 최대값이 더 큰 쪽의 척도를 양쪽에 적용시킴으로써 두번째 문제를 해결하였고, d3.quantile 2%를 적용시켜 첫번째 문제를 해결하였다.


구체적으로 해당 부분의 Javascript 코드를 보자면 다음과 같다. 코드는 위의 링크에 모두 공개되어 있다.

var maxAbs;
var biggerQuantile;

if (Math.abs(d3.min(valueArray[0])) > Math.abs(d3.max(valueArray[2]))) {
    maxAbs = Math.abs(d3.min(valueArray[0]));
    biggerQuantile = Math.abs(d3.quantile(valueArray[0], 0.02));
} else {
    maxAbs = Math.abs(d3.max(valueArray[2]));
    biggerQuantile = Math.abs(d3.quantile(valueArray[2], 0.98));
}
           
var selectColor = d3.scaleLinear()
                    .domain([(maxAbs * -1), (biggerQuantile * -1), 0, biggerQuantile, maxAbs])
                    .range(["#00334f", "#0086cf", "white", "#d80074", "#5c0031"]);


사실, 모든 경우에 명쾌하게 인지되지는 않지만 여러번의 시행착오를 거쳐 결정한 최선의 방법이다. 처음에는 평균과 표준편차를 이용하려고 하였으나 잘 되지 않았고, 상하위 몇%를 제외하고 하려고 해보았지만 기준이 모호하여 고심하던 차, 아는 범위 내에서는 d3.quantile 함수의 계산법이 가장 직관적인 결과를 가져다주었다.



지도 API


지도 API에 대해 말하자면,

구글 지도 API가 특정한 개인 고유 OPEN API key 입력 없이 사용할 수 있으므로 구글 지도를 바탕에 깔았고,

구글 지도는 색상 커스터마이징 기능을 제공하지만 국내지도 부분은 호환되지 않아(이것이 바로 지도 국외반출 문제와 연관된 문제다) 푸른색 붉은색이 잘 보이도록 반투명한 흰색 사각형을 깔고 그 위에 작업했다. zIndex 기능을 사용하여 각 셀들보다 아래쪽에 둘 수 있다.






인터페이스 구현


한 업종의 증감을 한번에 보기, 합집합 보기, 교집합 보기, 일시적으로 투명하게 하기, 초기화시키기 등의 옵션을 주었는데 하나하나 추가하다가 코드가 좀 지저분해진 것 같다. 구체적인 내용은 문장으로 표현할 필요가 없을 듯하여 별도로 적지 않겠다.












간단한 사용법

 


지도를 띄우면 기본적으로 아래 화면이 나온다. 




상단의 [선택된 업종의 교집합] 은 누를때마다 [선택된 업종의 합집합], [OOO 증감을 한번에] 로 변경된다.





일단 현재 상태에서 [한식+] 버튼을 눌러보자




서울 전체에서 한식이 증가된 격자들만 보여준다.





여기서 다시 [호프 및 간이주점+]를 눌러보자.




이것은 서울 전체에서 한식업도 늘고 주점도 동시에 늘어난 곳을 보여준다.





여기서 [까페-] 버튼을 눌러보자.



그러면 서울 전체에서 한식과 주점은 늘고 카페는 줄어든 곳들만 보여준다.

늘어난 곳과 줄어든 곳을 동시에 선택할 경우 보라색 계통으로 보이도록 하였다.






강남의 한 곳 중 진한 곳을 확대해보자





도산대로 근처인 것 같다. 잘 안보이므로 키보드의 e 키를 누른다. 대소문자 관계없다.



일시적으로 색상표현이 모두 사라져, 어디인지 좀 더 잘 보인다.





다시 e키를 눌러 색상이 표현된 상태로 되돌아 오고 이번에는 마우스를 해당 셀 안에 올려보자.



작은 창이 뜨며, 업종의 증감을 숫자로 확인할 수 있다. 한식은 9개소 늘고 호프 및 간이주점은 4개소 늘었으며, 카페는 5개소 줄어들었다. '아재이피케이션(?)' 되는 지역인 것 같다.....





이번에는 상단의 [선택된 업종의 교집합]을 눌러 [선택된 업종의 합집합]으로 변경시켜보자.



한식이 증가한 지역 + 호프및간이주점이 증가한 지역 + 카페가 감소한 지역을 모두 겹쳐 보여준다. 감소한 지역을 같이 섞어 놓으니 의미가 약간 헷갈리는데, 보통 증가한 지역끼리의 합집합, 감소한 지역끼리의 합집합은 가끔 쓰일 수도 있겠다는 생각이 든다.





[선택된 업종의 합집합]을 한번 더 눌러보자.



[한식 증감을 한번에] 보여주는 지도로 바뀐다.


우측에 선택된 버튼이 여러가지일 경우에는 최상단에 선택된 업종에 대해 보여주게 된다.





스페이스 바를 눌러보자.



선택된 내용을 모두 해제시켜 초기화 시켜준다. 되돌릴 수 없다.


더 이상의 자세한 설명은 생략한다.

이 정도면 모든 사용법을 충분히 설명한 것 같다.


마우스를 그냥 움직였는데 지도가 성가시게 따라올 경우 오른쪽 마우스 버튼을 누르면 안 따라오게 된다.

화면이 너무 작아서 설명 창이 충분하게 표시되지 못하는 경우에도 마우스 이동에 반응이 매우 성가시게 된다.











몇 장의 지도 이야기



아래는 몇 장의 지도를 만들어보고 내용을 들여다보겠다. 버튼에 따라 매우 많은 조합의 지도가 나오므로 궁금한 것들은 직접 만들어 볼 수 있다.


교집합의 경우만 다음과 같은 지도의 수가 나온다.(짐작보다 더 많이 많은 것 같은데 제대로 한 것인지 모르겠다)

합집합도 마찬가지, 증감지도 한번에 보는것은 36가지이므로, 총 300,189,270,593,998,278 개의 지도를 만들어 볼 수 있다.

무려 30경 개의 지도라서 1초에 하나씩 한 사람이 만든다 해도 3조4천억년이 걸린다.




8년간 카페 증감 지도.


서울 전체적으로 많이 늘고 홍대 부근에 집중적으로 늘어난 것이 보인다.

상암동 쪽이 급증한 것으로 나오는 이유는 2006년~2014년 사이에 그 지역에 새 단지와 상업시설이 들어섰기 때문이고, 8호선 장지역 근처가 급증한 이유는 가든파이브 개장 때문이다. 이렇게 8년간의 증감에는 '점진적 변화'와 '이벤트'가 섞여 있다. 오독을 피하려면 돌출값들이 나오는 지역에 대한 실제 변화를 파악해야 한다.


종로3가 부근에 카페가 줄어든 이유는 '다방'의 감소와 연관된다.








8년간 세탁소의 증감 지도


세탁소는 전반적으로 줄어들었는데, 잠실 어느 곳은 한 격자에 7개소나 늘어났다. 그 사이에 잠실엘스와 잠실리센츠 아파트에 사람들이 새로이 입주했다. 이 역시 '이벤트'에 해당한다.







8년간 휴대폰점의 증감 지도


2006년경에는 저렴한 폰을 사러 용산이나 강변 테크노마트에 갔다.

언제부터인가 동네 주변이나 온라인에서도 저렴한 폰과 사은품을 챙겨 살 수 있게 되었다.(사실 요새는 단통법때문에 다 비슷하지만.) 그래서 용산 부근의 한 셀에서는 55개소나 줄어들었다.

어떤 공산품의 유통방식이나 소비 방식이 실제 도시의 점포 밀도와 관계를 맺는 경우다.







8년간 PC방의 증감


PC방은 서울 전반적으로 줄어들었는데 신림동 고시촌에서 제일 많이 줄어들었다.

이 변화는 제도의 변경이라는 사회적 사건과 특정 그룹 사람들의 행위라는 두 가지 요소가 관련된 듯하다.

사법고시 폐지를 예고하면서 신림동 고시촌이 서서히 쇠락해갔고, 고시생들은 그 지난한 과정의 외로움을 PC방에서 달랬던 것 같다.







8년간 펜션의 증감 지도


펜션은 게스트하우스를 포함한다. 남산 기슭, 북촌, 홍대에 외국인 게스트하우스가 많이 늘어났다.

감소한 곳은 거의 없다. 2006년에는 서울 전체에 거의 없었으니까.








8년간 중식의 증감지도


서울에 소위 '차이나 타운'이 형성된 곳들을 보여준다.

건대입구역 한 켜 뒤에도 양꼬치 골목이 있다.








8년간 노래방의 증감지도


서울 도심은 전반적으로 줄어든 가운데 몇몇 지역들에서 늘어났다.

가락시장 부근에 집중적으로 늘어났는데 로드뷰로 찾아보니 유흥가다.








이번에는 교집합을 보자


기타외국식, 서양식, 카페, 일식이 증가하고 동시에 세탁소가 감소한 곳은?

흔히 말하는 젠트리피케이션 지역들이 나온다.

홍대, 서촌, 삼청동, 이태원, 경리단길, 동부이촌동, 가로수길 등등이다.




숫자로만 결론을 내는 것은 위험하다. 그리고 구체적인 지역의 사정은 그 지역에 대해 꾸준히 연구한 사람의 복합적이고 유기적 지식이 더 유용할 수 있다.

그러나 전체 범위가 너무 넓어서 한 사람이 한 번에 파악하기에는 어려울 때, 위와 같은 접근 방식은 모니터링이라는 측면에서 좋은 출발점이 될 수 있지 않을까.






앞에서도 언급했지만 [이 곳]에서 직접 클릭 하여 지도들을 조합해볼 수 있다.



업종의 조합을 통하여 의미있는 결과를 도출한 후 해석을 어딘가에 포스팅하였을 경우, 아래에 댓글로 주소를 달아주시면 매우 감사하겠습니다!

혹은 조합된 지도와 그 해석을 댓글에 써 주시는 것도 적극 환영합니다.