Skip to content

iOS-Developer-KR/WhereIsTheRestroom

Repository files navigation

WhereIsTheRestroom

Finding RestRoom In Seoul Subway Restroom 1호선부터 8호선 역사 내 화장실 위치를 찾기 위한 앱입니다.

시작하기 전에

https://apple-document.tistory.com/156 에서 자세한 내용을 확인할 수 있습니다.

  • 반드시 API Key를 info.plist에 추가하거나 앱을 시작할 때 API Key를 사용해야 지도를 띄울 수 있습니다.

cocoapods

# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'

target 'DiseaseTrackerMap' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for DiseaseTrackerMap
  pod 'NMapsMap'

end

csv 파일 불러오기

좌표가 포함되어 있는 csv 파일을 불러옵니다. 하지만 csv를 xcode로 불러왔을때 정확하게 모두 가져오지 않는다는 것을 확인하였습니다. (outlier 존재)

if let path = Bundle.main.path(forResource: self.CSV_ASSET_NAME, ofType: "csv") {
  let contents = try String(contentsOfFile: path, encoding: .utf8)
  let lines = contents.components(separatedBy: .newlines)

클러스터링

원하는 데이터를 keyTagMap 프로퍼티에 담아 addAll을 사용하여 클러스터링을 진행할 수 있습니다.

@Published var keyTagMap = [ItemKey: ItemData]()
self.clusterer?.addAll(self.keyTagMap)

클러스터 마커의 이름또한 정할 수 있고 크기, 마커를 탭했을 때 동작도 정의할 수 있습니다.

func updateClusterMarker(_ info: NMCClusterMarkerInfo, _ marker: NMFMarker) {
    marker.captionText = String(info.size)
    marker.captionTextSize = 16
    marker.touchHandler = { (overlay: NMFOverlay) -> Bool in
        let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: info.position.lat, lng: info.position.lng))
        cameraUpdate.animation = .easeIn
        self.view.mapView.moveCamera(cameraUpdate)
        return true
    }
    
    if info.size < 3 && info.size > 1 {
        marker.iconImage = NMF_MARKER_IMAGE_CLUSTER_LOW_DENSITY
    } else if info.size < 10 {
        marker.iconImage = NMF_MARKER_IMAGE_CLUSTER_MEDIUM_DENSITY
    } else {
        marker.iconImage = NMF_MARKER_IMAGE_CLUSTER_HIGH_DENSITY
    }
}

일반 마커

일반 마커를 탭하였을 때 특정 뷰를 띄우기 위해 코드를 만들었습니다.

func updateLeafMarker(_ info: NMCLeafMarkerInfo, _ marker: NMFMarker) {
    let tag = info.tag as? ItemData
    marker.captionText = tag?.toiletName ?? ""
    marker.iconImage = NMF_MARKER_IMAGE_GREEN
    marker.touchHandler = { [weak self] (overlay: NMFOverlay) -> Bool in
        let cameraUpdate = NMFCameraUpdate(scrollTo: info.position)
        self?.view.mapView.moveCamera(cameraUpdate)
        self?.tappedMarkerInfo = info
        self?.tappedMarkerKey = info.key as? ItemKey
        self?.tappedMarkerTag = info.tag as? ItemData
        return true
    }
}

뷰에서 탭한 마커의 정보 확인

탭한 마커의 정보를 나타내기 위해서 코드를 만들었으며 반복되는 코드의 길이를 줄이기 위해서 extension에 HStackTextView를 만들었습니다.

if let tag = coordinator.tappedMarkerTag, let key = coordinator.tappedMarkerKey {
    HStackTextView(text: "위치:\(key.position.lat), \(key.position.lng)")
    HStackTextView(text: "거리:\(Double(Int(key.distance ?? 0.0))/1000)km")
    HStackTextView(text: "\(tag.line)호선")
    HStackTextView(text: "화장실이름: " + tag.toiletName)
    HStackTextView(text: "화장실 상세 위치: " + tag.toiletDetailedLocation)
    HStackTextView(text: "게이트 내외부: " + tag.toiletDetailedLocationInGate)
    HStackTextView(text: "운영시간: " + tag.openHours)
    HStackPhoneCallVeiw(text: "전화번호: ", phoneNumber: tag.phoneNumber)
    HStackTextView(text: "리모델링: " + tag.remodelingYearMonth)
    HStackTextView(text: "기저귀교환대설치유무-남자화장실: " + transformXY(value: tag.diaperChangingTableInstallationMaleToilet))
    HStackTextView(text: "기저귀교환대설치유무-여자화장실: " + transformXY(value: tag.diaperChangingTableInstallationFemaleToilet))
    HStackTextView(text: "기저귀교환대설치유무-남자장애인화장실: " + transformXY(value: tag.diaperChangingTableInstallationMaleDisabledToilet))
    HStackTextView(text: "기저귀교환대설치유무-여자장인화장실: " + transformXY(value: tag.diaperChangingTableInstallationFemaleDisabledToilet))
}
func HStackTextView(text: String) -> some View {
    HStack {
        Text(text)
        Spacer()
    }
    .padding(3)
}

거리 계산

현 위치의 좌표와 목적지의 좌표 사이의 거리를 수직으로 뻗었을 때의 거리를 의미하며 실제 경로의 거리와 차이가 날 수 있습니다.

func distance(to coordinate1: NMGLatLng, coordinate2: NMGLatLng) -> Double {
    let earthRadius: Double = 6371 // 지구의 반지름 (단위: km)

    // 라디안 단위로 변환
    let lat1Rad = coordinate1.lat * .pi / 180
    let lon1Rad = coordinate1.lng * .pi / 180
    let lat2Rad = coordinate2.lat * .pi / 180
    let lon2Rad = coordinate2.lng * .pi / 180

    // 위도와 경도의 차이
    let latDiff = lat2Rad - lat1Rad
    let lonDiff = lon2Rad - lon1Rad

    // 위도와 경도의 차이에 대한 Haversine 공식
    let a = sin(latDiff / 2) * sin(latDiff / 2) +
        cos(lat1Rad) * cos(lat2Rad) *
        sin(lonDiff / 2) * sin(lonDiff / 2)
    let c = 2 * atan2(sqrt(a), sqrt(1 - a))

    // 거리 계산
    let distance = earthRadius * c
    return distance
}

길찾기를 위한 기본 설정

네이버맵으로 길찾기

func NaverMap(lat: Double, lng: Double) {
    // URL Scheme을 사용하여 네이버맵 앱을 열고 자동차 경로를 생성합니다.
    guard let url = URL(string: "nmap://route/car?dlat=\(lat)&dlng=\(lng)&appname=kr.co.kepco.ElectricCar") else { return }
    // 앱 스토어 URL을 설정합니다.
    guard let appStoreURL = URL(string: "http://itunes.apple.com/app/id311867728?mt=8") else { return }
    
    if UIApplication.shared.canOpenURL(url) {
        // 네이버맵 앱이 설치되어 있는 경우 앱을 엽니다.
        UIApplication.shared.open(url)
    } else {
        // 네이버맵 앱이 설치되어 있지 않은 경우 앱 스토어로 이동합니다.
        UIApplication.shared.open(appStoreURL)
    }
}

카카오맵으로 길찾기

func KaKaoMap(lat: Double, lng: Double) {
    // URL Scheme을 사용하여 kakaomap 앱 열고 경로 생성합니다
    guard let url = URL(string: "kakaomap://route?ep=\(lat),\(lng)&by=CAR") else { return }
    
    // Kakaomap 앱의 App Store URL 생성
    guard let appStoreUrl = URL(string: "itms-apps://itunes.apple.com/app/id304608425") else { return }
    
    let urlString = "kakaomap://open"
    
    // Kakaomap 앱이 설치되어 있는지 확인하고 URL 열기
    if let appUrl = URL(string: urlString) {
        if UIApplication.shared.canOpenURL(appUrl) {
            UIApplication.shared.open(url)
        } else {
            // Kakaomap 앱이 설치되어 있지 않은 경우 App Store URL 열기
            print("안깔려있는데")
            UIApplication.shared.open(appStoreUrl)
        }
    }
}

티맵으로 길찾기

func TMap(lat:Double, lng:Double) {
    // URL Scheme을 사용하여 티맵 앱을 열고 자동차 경로를 생성합니다.
    let urlStr = "tmap://route?rGoName=목적지&rGoX=\(lng)&rGoY=\(lat)"
    
    // URL 문자열을 인코딩하여 올바른 형식으로 변환합니다.
    guard let encodedStr = urlStr.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return }
    
    // 인코딩된 URL 문자열을 URL 객체로 변환합니다.
    guard let url = URL(string: encodedStr) else { return }
    
    // TMap 앱이 설치되어 있는지 확인합니다.
    if UIApplication.shared.canOpenURL(url) {
        // TMap 앱을 엽니다.
        UIApplication.shared.open(url)
    } else {
        // TMap 앱이 설치되어 있지 않은 경우 앱 스토어로 이동합니다.
        guard let appStoreURL = URL(string: "http://itunes.apple.com/app/id431589174") else { return }
        UIApplication.shared.open(appStoreURL)
    }
}

About

Finding RestRoom In Seoul Subway Restroom

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published