Today
-
Yesterday
-
Total
-
  • 01. Grid로 세계 지도 나타내기
    Projects/세계 여행 지도 2021. 6. 3. 17:37

    팀 해체 후 첫 번째 토이 프로젝트

    지우님과 팀 해체를 하고 다시 만났다. 단순히 구현하는 것이 목적이 아닌 공부를 목적으로 하는 첫 번째 토이 프로젝트를 진행하기로 했다.

    내가 예전 부터 만들어보고 싶었던 앱을 하나 제안했는데,

    바로 내가 방문해 본 국가들을 세계 지도에 색칠을 해 나갈 수 있는 간단한 웹 어플리케이션이었다.

    지우님은 나라를 검색하고 방문 여부에 관한 데이터를 관리하는 역할을 맡았고 나는 세계 지도를 렌더링하고 컨트롤하는 역할을 맡았다.

    우선, 세계 지도를 이미지로 나타내고 싶었지만 일러스트 파일을 찾기가 쉽지 않아서 고민끝에 grid로 한번 나타 내보기로 했다..!

    이런 느낌으로다가

    node로 매크로 앱 만들기

    세계 지도를 픽셀로 나타내기 위해 특정 픽셀 별 위치가 담긴 json 파일이 필요했는데,
    이를 만들기 위해 node로 간단한 매크로 앱을 만들었다. 

    1. [-180, 180] x [-90, 90] (경도 x 위도) 크기의 배열을 만든다.
        (처음에는 360 x 180의 크기로 만들었는데 grid의 크기가 너무 작아서 180 x 90으로 줄였다.)
    2. 해당 index의 좌표를 Google API를 활용해 GeoCoding한다. (Geocoding: 위도 경도를 해당 주소로 변환)
    3. GeoCoding이 된 문자열(주소)를 배열의 해당하는 위치에 저장한다.
    4. JSON파일로 변환해서 저장한다. (한 위도의 데이터를 모두 불러올 때 마다)

    여기서 문제는 Google API를 사용할 때 비동기로 Geocoding이 되는데 동시에 굉장히 많은 데이터를 요청할 수 없다는 것이었다. 때문에 비동기 처리를 하는 API를 동기적으로 요청을 하도록 만들었다... (해당 콜백 함수가 불려지고 그 다음 좌표의 Geocoding을 요청하도록) 덕분에 모든 데이터를 요청하고 저장하는데 시간이 조금 걸렸다. (2시간 정도 걸렸던 것 같다)

    const map = require('./map.json'); //결과물 저장
    const fetch = require ('node-fetch'); //api 요청을 위해 설치한 간단한 라이브러리
    
    const API_KEY = 'GOOGLE API KEY';
    const MAX_LON = 180; //경도
    const MAX_LAT = 90; //위도
    
    const fs = require('fs');
    const scale = 2;
    
    const Map = () => {
      const longitude = map[map.length - 1].length * scale - MAX_LON;
      const latitude = MAX_LAT - (map.length * scale);
    
      if (longitude === MAX_LON) {
        if (latitude > -MAX_LAT) {
          map.push([]);
          Map();
          return;
        }
      }
    
      const url = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&key=${API_KEY}`;
    
      fetch(url)
      .then(res => res.json())
      .then(data =>
        {
          //가끔 지도에 zero_results라는 결과가 나옴..! (바다)
          if (data.status === 'ZERO_RESULTS') {
            map[map.length - 1].push('sea');
            console.log(`${latitude}, ${longitude}: sea`);
    
            if(longitude === MAX_LON) {
              //fs.writeFile('map.json', JSON.stringify(map), 'utf8', () => {});
            }
            fs.writeFile('map.json', JSON.stringify(map), 'utf8', () => {});
            Map();
          } else if(data.results && data.results[0] && data.results[0].formatted_address){
            map[map.length - 1].push(data.results[0].formatted_address);
            console.log(`${latitude}, ${longitude}: ${data.results[0].formatted_address}`);
    
            if(longitude === MAX_LON) {
              map.push([]);
              //fs.writeFile('map.json', JSON.stringify(map), 'utf8', () => {});
            }
            fs.writeFile('map.json', JSON.stringify(map), 'utf8', () => {});
            Map();
          } else {
            console.log(data)
          }
        }
      )
      .catch(err => {
        console.error(err);
      });
    }
    
    Map();

    데이터 파일도 만들었겠다, 이제는 React를 이용해서 해당 데이터를 띄워보기로 했다. 우선 가장 간단하게 구현해 볼 수 있는 방법인 이중 배열을 map으로 돌려서 각 위도마다 flexbox에 한 줄씩 넣었다. 

    꽤 많은 데이터에서 "Sea", "Ocean", "Bay" 등 육지가 아닌 곳들은 모두 색을 투명하게 만들고, 육지인 부분은 background color를 입혀서 한번 띄워보니 굉장히 그럴싸한 세계지도가 나온 것이다!

    데이터 입력 전과 후 (픽셀의 수와 크기를 좀 변경했다)

     

    이제 마우스가 가리키는 나라에 해당하는 Grid들의 색을 변경시켜 하이라이트 시켜보자..!

    댓글