[iOS] NaverMap API - Reverse Geocoding

Reverse Geocoding이란?

 

특정 위도와 경도를 이용해서 해당 위치에 대한 주소를 구하는 것을 말한다.

네이버 맵을 사용할 경우 네이버 API를 사용해 쉽게 구현할 수 있다.

 

 

 

 

Reverse Geocoding API

 

Reverse Geocoding 역시 get 메서드로 요청을 하며

Header클라이언트 ID시크릿 키를 넣어줘야 한다.

 

Reverse Geocoding은 좌표를 주소로 변환하는 API이기에

저번 글에서 만든 마커를 가운데에 고정시킨 지도 객체를 활용해 좌표값을 받아보겠다.

 

 

[iOS] NaverMap - 화면 가운데 고정된 마커 설정 및 좌표 반환

이번 글은 프로젝트를 진행하며 내가 알아내고 구현한 것 위주로 서술하였다. 다른 더 좋은 방법이 있을 수도 있다!   화면 가운데 마커 설정하기택시 호출 앱 등을 살펴보면 화면 정 가운데

d0ngurrrrrrr.tistory.com

 

 

 

 

 

코드

우선 네트워크 통신 메서드부터 만들어보자면

    func callRequest(coords: String, completion: @escaping (Data) -> Void) {
        
        guard var urlComponents = URLComponents(string: "https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc") else {
            print("URL Components Error")
            return
        }
        
        let queryItemArray = [
            URLQueryItem(name: "coords", value: coords),
            URLQueryItem(name: "orders", value: "roadaddr"),
            URLQueryItem(name: "output", value: "json")
        ]
        urlComponents.queryItems = queryItemArray
        
        guard let url = urlComponents.url else {
            print("URL Error")
            return
        }
        
        var urlRequest = URLRequest(url: url)
        
        urlRequest.addValue("클라이언트 Id", forHTTPHeaderField: "X-NCP-APIGW-API-KEY-ID")
        urlRequest.addValue("Secret 키", forHTTPHeaderField: "X-NCP-APIGW-API-KEY")
        
        URLSession.shared.dataTask(with: urlRequest) { data, response, error in
            
            guard let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                print("status code error")
                return
            }
            completion(data)
        }.resume()
    }

 

 

Geocoding과 비슷하게 만들 수 있다.

이때 차이점은 쿼리에 output과 coords, 즉 좌표값을 줘야 한다는 것이다.

output을 json으로 주지 않으면 xml로 오기 때문에 xml로 파싱을 할 것이 아니라면 json으로 할 것을 추천한다.

 

 

    // 딜리게이트 메서드
    func mapViewCameraIdle(_ mapView: NMFMapView) {
        print(mapView.cameraPosition.target)
        let coords = "\(mapView.cameraPosition.target.lng)" + "," + "\(mapView.cameraPosition.target.lat)"
        callRequest(coords: coords) { data in
            do {
                let decodeData = try JSONDecoder().decode(ReverseGeocodingModel.self, from: data)
                print(decodeData)
            } catch {
                print(error)
            }
        }
    }
    
//모델
struct ReverseGeocodingModel: Decodable {
    let results: [Address]
}
 
struct Address: Decodable {
    let region: Region
    let land: Land
}
 
struct Region: Decodable {
    let area1: Area1
    let area2, area3, area4: Area
}
 
struct Area1: Decodable {
    let name, alias: String
}
 
struct Area: Decodable {
    let name: String
}
 
struct Land: Decodable {
    let number1, number2: String
    let addition0: Addition0
    let name: String?
}
 
struct Addition0: Decodable {
    let type, value: String
}

 

 

이제 callRequest를 호출해야 하는데, 언제 이 메서드를 호출하느냐가 중요하다.

나는 mapViewCameraIdle 메서드에서 호출을 했다.

 

그 이유는 간단하다.

카메라 이동의 모든 움직임이 종료되었을 때, 호출을 해서 call 수를 줄일 수 있기 때문이다.

mapViewCameraIdle이 아니라 cameraIsChangingByReason, cameraDidChangeByReason에서 호출을 하게 되면

드래그를 하는 동안에도 여러 번 호출이 될 수 있다.

이 부분은 주의해서 호출해줘야 한다.

 

 

전체코드 보기

더보기
import UIKit
import NMapsMap
 
class CenterMarkerViewController: UIViewController {
    
    let centerMarker = NMFMarker()
    
    override func viewDidLoad() {
        super.viewDidLoad()
 
        let naverMap = NMFNaverMapView(frame: view.frame)
        view.addSubview(naverMap)
        
        let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: 37.479132, lng: 127.011770))
        naverMap.mapView.moveCamera(cameraUpdate)
        
        centerMarker.position = naverMap.mapView.cameraPosition.target
        centerMarker.captionText = "마커"
        centerMarker.captionAligns = [NMFAlignType.top]
        centerMarker.mapView = naverMap.mapView
        
        naverMap.mapView.addCameraDelegate(delegate: self)
    }
 
    func callRequest(coords: String, completion: @escaping (Data) -> Void) {
        
        guard var urlComponents = URLComponents(string: "https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc") else {
            print("URL Components Error")
            return
        }
        
        let queryItemArray = [
            URLQueryItem(name: "coords", value: coords),
            URLQueryItem(name: "orders", value: "roadaddr"),
            URLQueryItem(name: "output", value: "json")
        ]
        urlComponents.queryItems = queryItemArray
        
        guard let url = urlComponents.url else {
            print("URL Error")
            return
        }
        
        var urlRequest = URLRequest(url: url)
        
        urlRequest.addValue("클라이언트 Id", forHTTPHeaderField: "X-NCP-APIGW-API-KEY-ID")
        urlRequest.addValue("Secret 키", forHTTPHeaderField: "X-NCP-APIGW-API-KEY")
        
        URLSession.shared.dataTask(with: urlRequest) { data, response, error in
            
            guard let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                print("status code error")
                return
            }
            completion(data)
        }.resume()
    }
    
}
 
extension CenterMarkerViewController: NMFMapViewCameraDelegate {
    
    func mapView(_ mapView: NMFMapView, cameraIsChangingByReason reason: Int) {
        centerMarker.position = mapView.cameraPosition.target
    }
    
    func mapView(_ mapView: NMFMapView, cameraDidChangeByReason reason: Int, animated: Bool) {
        centerMarker.position = mapView.cameraPosition.target
    }
    
    func mapViewCameraIdle(_ mapView: NMFMapView) {
        print(mapView.cameraPosition.target)
        let coords = "\(mapView.cameraPosition.target.lng)" + "," + "\(mapView.cameraPosition.target.lat)"
        callRequest(coords: coords) { data in
            do {
                let decodeData = try JSONDecoder().decode(ReverseGeocodingModel.self, from: data)
                print(decodeData)
            } catch {
                print(error)
            }
        }
    }
    
}
 
struct ReverseGeocodingModel: Decodable {
    let results: [Address]
}
 
struct Address: Decodable {
    let region: Region
    let land: Land
}
 
struct Region: Decodable {
    let area1: Area1
    let area2, area3, area4: Area
}
 
struct Area1: Decodable {
    let name, alias: String
}
 
struct Area: Decodable {
    let name: String
}
 
struct Land: Decodable {
    let number1, number2: String
    let addition0: Addition0
    let name: String?
}
 
struct Addition0: Decodable {
    let type, value: String
}

 

지도를 드래그해서 중앙마커를 움직이고, 움직임을 멈출 때마다

ReverseGeocodingModel 형태로 데이터가 받아지는 것을 알 수 있다.

마지막 데이터를 보자면 '서울특별시 서초구 서초동 219 대법원 청사'로 주소값을 얻을 수 있다.

(데이터가 많기 때문에 본인이 필요한 데이터에 맞는 모델을 만들어야 한다.)

도큐먼트 보러 가기