환경

iOS / Swift

Xcode ver 12.5

swift 5

 

배경

팀원들과 프로젝트를 진행하던 도중 사용자가 보고 있는 MapKit 화면의 크기에 따라 화면에 annotation을 표시할 개수 만큼만 요청해서 API로 받아와야 할 상황이 생겼다. HA..... 라는 반응이 나왔지만 어쨌든 책임감을 갖고 해내야 했기 때문에 여러 가지 자료를 조사해야 했는데, 너무 자료가 없는 것 같아서 직접 써서 남겨두려 한다. 

좀더 상황을 자세히 설명하자면 아래 그림처럼 친구들이 '사과' 위치에 있다고 하자.

 

 

그런데 만약 내게 친구가 전세계에 10000명이라서 10000명 전부를 지도에 annotation으로 추가해야 한다면 그만큼 비효율적인 일도 없을 것이다. 따라서 친구가 10000명이든 100000명이든 데이터를 필터링해서 내가 지금 보고 있는 map 의 "범위 안에" 있는 친구들만 표시할 필요가 있었다.

처음에는 도저히 어떻게 할지 떠오르질 않아서 한~참 검색을 하다가, mapKit의 span이라는 개념을 이해하고는 무릎을 탁 쳤다. 

이 span이라 하는 건 예를 들어 다음과 같은 화면이 우리가 보고 있는 mapKit View라고 할 때, 초록색 영역이 span이 된다.

 

출처: 하단 stackoverflow링크

이 때 longitude 한쪽 끝에서 반대쪽 끝을 span.longitudeDelta를 통해 얻어낼 수 있고,

latitude 한쪽 끝에서 반대쪽 끝을 span.latitudeDelta를 통해 얻어낼 수 있다.

 

이걸 어떻게 활용할 수 있냐면, 만약에 '나의 위치'가 center라는 전제만 갖는다면 나침반이 북쪽을 가리킨다고 생각했을 때

  • 위도(아래 위로, latitude)의 범위는: loc3 ~ loc4,
    즉 center.latitude + span.latitudeDelta * 0.5 이 되고,
  • 경도(왼쪽~오른쪽, longitude)의 범위는: loc1 ~ loc2,
    즉 center.longitude + span.longitudeDelta * 0.5가 된다.

신기한 건 화면을 돌리더라도 돌린 각도에 따라 Delta값도 알아서 바뀌기 때문에 저 계산식을 그대로 적용하면 된다는 것 !!! (쩌러!!!!!!!)

으하핳하하ㅏ하하ㅏㅎㅎ하ㅏ하

 

그래서 위 그림에서

왼쪽 끝을 farWest,

오른쪽 끝을 farEast,

맨 우에를 farNorth,

맨 아래를 farSouth

라고 한다면 아래와 같은 코드가 나올 것이다.

 

        let span = mapKitView.region.span
        let center = mapKitView.region.center
        
        let farSouth = CLLocation(latitude: center.latitude - span.latitudeDelta * 0.5, longitude: center.longitude)
        let farNorth = CLLocation(latitude: center.latitude + span.latitudeDelta * 0.5, longitude: center.longitude)
        let farEast = CLLocation(latitude: center.latitude, longitude: center.longitude + span.longitudeDelta * 0.5)
        let farWest = CLLocation(latitude: center.latitude, longitude: center.longitude - span.longitudeDelta * 0.5)
        
        let minimumLatitude: Double = farSouth.coordinate.latitude as Double
        let maximumLatitude: Double = farNorth.coordinate.latitude as Double
        let minimumlongtitude: Double = farWest.coordinate.longitude as Double
        let maximumLongitude: Double = farEast.coordinate.longitude as Double
            
        print ("minimumLatitude: \(minimumLatitude)")
        print ("maximumLatitude: \(maximumLatitude)")
        print ("minimumLongitude: \(minimumlongtitude)")
        print ("maximumLongitude: \(maximumLongitude)")
        

 

그리고 이걸 location didupdate 메서드와 함께 붙여준다면 위치가 업데이트 될 때마다(거의 알아서 5~10초에 한번꼴로 업데이트됨)

내가 지금 보고 있는 map 크기에 맞게 친구들 리스트를 필터링하여 지도에 띄워줄 수 있을 것이다.

참고로 위 코드를 실행하면 아래처럼 나온다:

 

minimumLatitude: 37.451172215117325
maximumLatitude: 37.51815171795566
minimumLongitude: 127.10296016783714
maximumLongitude: 127.19296016783714

 

p.s. 

만약에 이런 span을 제공하지 않는다면 내 위치의 좌표와 맵 끝의 어느지점까지의 meter 거리를 이용하는 방법도 있다.

위도 1도 차이와 경도 1도 차이는 다르지만 한국 내에서 서비스를 한다는 가정 하에서는 이들 둘을 그렇게 차별하지 않고 써도 크게 상관 없지 않을까 싶다. 나침반 돌아가면 삼각함수 이용해서 계산하는 게 연산량이 너무 많아질 수 있기 때문이다. 만약에 나침반이 고정되어 있다면 위도, 경도 따로 계산식을 써서 좌표를 필터링하면 된다. 

아무튼 이런 상황에서는 

  • 0.1도 차이면,  10km
  • 0.01도 차이면 1km 
  • 0.001도 차이면 100m
  • 0.0001도 차이면 10m
  • 0.00001도 차이면 1m 

이렇게 생각하고 쓰면 될듯 ! (맨아래 출처참고)

 

출처:

https://stackoverflow.com/questions/21273269/trying-to-get-the-span-size-in-meters-for-an-ios-mkcoordinatespan

https://m.cafe.daum.net/gpsyn/Pllz/530

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기
// custom