🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

2769閲覧

MapKitを使って二点間の距離を計算したい

kasshi0827

総合スコア1

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2021/02/27 11:49

前提・実現したいこと

ここに質問の内容を詳しく書いてください。
Swift初心者です。
MapKitを使い、ロングタップをした場所から一番近いお城を特定して、ロングタップした地点から一番ちかいお城を特定し、タップ地点とお城の距離を計測するアプリを作っています。

発生している問題・エラーメッセージ

distanceメソッドを使って、二点間の距離を計算する際、計算結果(下記ソースコード中の変数distance)が0.0になってしまいます。

該当のソースコード

swift

1import UIKit 2import MapKit 3import CoreLocation 4 5class ViewController: UIViewController,CLLocationManagerDelegate,UIGestureRecognizerDelegate { 6 7 let castleNames = ["根室半島チャシ跡群", "五稜郭", "松前城", "弘前城", "根城", "盛岡城", "多賀城", "仙台城", "久保田城", "山形城", "二本松城", "会津若松城", "白河小峰城", "水戸城", "足利氏館", "箕輪城", "金山城", "鉢形城", "川越城", "佐倉城", "江戸城", "八王子城", "小田原城", "武田氏館", "甲府城", "松代城", "上田城", "小諸城", "松本城", "高遠城", "新発田城", "春日山城", "高岡城", "七尾城", "金沢城", "丸岡城", "一乗谷城", "岩村城", "岐阜城", "山中城", "駿府城", "掛川城", "犬山城", "名古屋城", "岡崎城", "長篠城", "伊賀上野城", "松阪城", "小谷城", "彦根城", "安土城", "観音寺城", "二条城", "大阪城", "千早城", "竹田城", "篠山城", "明石城", "姫路城", "赤穂城", "高取城", "和歌山城", "鳥取城", "松江城", "月山富田城", "津和野城", "津山城", "備中松山城", "鬼ノ城", "岡山城", "福山城", "郡山城", "広島城", "岩国城", "萩城", "徳島城", "高松城", "丸亀城", "今治城", "湯築城", "松山城", "大洲城", "宇和島城", "高知城", "福岡城", "大野城", "名護屋城", "吉野ヶ里遺跡", "佐賀城", "平戸城", "島原城", "熊本城", "人吉城", "大分府内城", "岡城", "飫肥城", "鹿児島城", "今帰仁城", "中城城", "首里城"] 8 9 let latDictionary = ["根室半島チャシ跡群": 43.386969, "五稜郭": 41.7969948, "松前城": 41.4297918, "弘前城": 40.6079291, "根城": 40.5046173, "盛岡城": 39.7001247, "多賀城": 38.2938132, "仙台城": 38.25247745, "久保田城": 33.255675, "山形城": 38.25561485, "二本松城": 37.5996777, "会津若松城": 37.5085404, "白河小峰城": 37.1327408, "水戸城": 36.3730791, "足利氏館": 36.3401914, "箕輪城": 36.40438828527544, "金山城": 33.301123, "鉢形城": 36.1092846, "川越城": 35.9251145, "佐倉城": 35.7220927, "江戸城": 35.6883735, "八王子城": 35.6528939, "小田原城": 35.263676, "武田氏館": 36.3822232, "甲府城": 35.66460627265863, "松代城": 37.125089, "上田城": 36.4021192, "小諸城": 36.3268255, "松本城": 36.2385977, "高遠城": 35.83337088302108, "新発田城": 37.9548019, "春日山城": 37.1467958, "高岡城": 36.7362147, "七尾城": 37.0092850257371, "金沢城": 36.565600450000005, "丸岡城": 36.152398, "一乗谷城": 36.0137208, "岩村城": 35.359713, "岐阜城": 35.4094858, "山中城": 35.1564091, "駿府城": 34.9776719, "掛川城": 34.793469, "犬山城": 35.3883394, "名古屋城": 35.1851045, "岡崎城": 34.95632355, "長篠城": 34.926122, "伊賀上野城": 34.7889966, "松阪城": 34.5868422, "小谷城": 35.46266475866277, "彦根城": 35.27700325, "安土城": 35.1488134, "観音寺城": 34.1284693, "二条城": 35.012714, "大阪城": 34.687348, "千早城": 34.417064, "竹田城": 35.30031445, "篠山城": 35.6180129, "明石城": 34.6525687, "姫路城": 34.83935085, "赤穂城": 34.7458591, "高取城": 34.42951625, "和歌山城": 34.2276472, "鳥取城": 35.4943948, "松江城": 35.47576875, "月山富田城": 35.3611788, "津和野城": 34.4600199, "津山城": 33.301123, "備中松山城": 34.8086278, "鬼ノ城": 34.7275739, "岡山城": 34.665176349999996, "福山城": 34.491079, "郡山城": 34.5020563, "広島城": 34.4027148, "岩国城": 34.1753464, "萩城": 34.4188734402295, "徳島城": 34.0698307, "高松城": 34.350111749999996, "丸亀城": 34.2888128, "今治城": 34.063381, "湯築城": 33.8480881, "松山城": 33.8456715, "大洲城": 33.5095498, "宇和島城": 33.2194562, "高知城": 33.560691, "福岡城": 32.5292788, "大野城": 33.547399, "名護屋城": 33.53059763430767, "吉野ヶ里遺跡": 33.324934923806865, "佐賀城": 33.2639134, "平戸城": 33.368559, "島原城": 32.788084, "熊本城": 32.805269100000004, "人吉城": 32.2111134, "大分府内城": 33.240356399999996, "岡城": 32.9692029, "飫肥城": 31.6290146, "鹿児島城": 31.5988311, "今帰仁城": 26.69088935, "中城城": 26.2856449, "首里城": 26.2170014] 10 11 let lngDictionary = ["根室半島チャシ跡群": 145.7521176, "五稜郭": 140.7571649, "松前城": 140.1083792, "弘前城": 140.46366052462452, "根城": 141.4555508, "盛岡城": 141.1502054, "多賀城": 141.0042642, "仙台城": 140.85580809599725, "久保田城": 130.2288558, "山形城": 140.32830209037357, "二本松城": 140.4281599, "会津若松城": 139.9300318, "白河小峰城": 140.2138267, "水戸城": 140.4827879, "足利氏館": 139.4497731, "箕輪城": 138.95201603115916, "金山城": 132.5971628, "鉢形城": 139.1959612, "川越城": 139.4856927, "佐倉城": 140.2158745, "江戸城": 139.754407, "八王子城": 139.2521542, "小田原城": 139.150229, "武田氏館": 140.5173145, "甲府城": 138.57029005070953, "松代城": 138.6138681, "上田城": 138.2490506, "小諸城": 138.417924, "松本城": 137.9688837, "高遠城": 138.06304171480048, "新発田城": 139.3255546, "春日山城": 138.2057514, "高岡城": 137.0187306, "七尾城": 136.9840749505195, "金沢城": 136.6595753027176, "丸岡城": 136.2720774, "一乗谷城": 136.29745, "岩村城": 137.4512955, "岐阜城": 136.7569769, "山中城": 138.9924006, "駿府城": 138.3852717, "掛川城": 138.018733, "犬山城": 136.9392801, "名古屋城": 136.8998438, "岡崎城": 137.15880700000002, "長篠城": 137.565176, "伊賀上野城": 136.1235104, "松阪城": 136.5412491, "小谷城": 136.27854449402926, "彦根城": 136.25174669997847, "安土城": 136.1479609, "観音寺城": 133.6628679, "二条城": 135.7509321, "大阪城": 135.5258512960221, "千早城": 135.6512936, "竹田城": 134.82928507499997, "篠山城": 138.65645486739976, "明石城": 134.9910964, "姫路城": 134.6940046, "赤穂城": 134.3892983, "高取城": 135.82696123777612, "和歌山城": 135.1715062, "鳥取城": 134.2219282, "松江城": 133.05063800335296, "月山富田城": 133.1846605, "津和野城": 131.7641695, "津山城": 132.5971628, "備中松山城": 133.6220616683833, "鬼ノ城": 133.7678973, "岡山城": 133.93604457863012, "福山城": 133.361087, "郡山城": 135.6243217, "広島城": 132.459033, "岩国城": 132.1743246, "萩城": 131.38361102138134, "徳島城": 134.5550353, "高松城": 134.05101339421776, "丸亀城": 133.7982421, "今治城": 133.0067549, "湯築城": 132.7867746, "松山城": 132.7656893, "大洲城": 132.5411299, "宇和島城": 132.5652384, "高知城": 133.53145880847023, "福岡城": 130.0316318, "大野城": 130.488786, "名護屋城": 129.86916502254334, "吉野ヶ里遺跡": 130.386111, "佐賀城": 130.3008378, "平戸城": 129.557552, "島原城": 130.3705411, "熊本城": 130.7054642204054, "人吉城": 130.7665342, "大分府内城": 131.61141599909516, "岡城": 131.4078196, "飫肥城": 131.3503762, "鹿児島城": 130.5551261, "今帰仁城": 127.92973343860277, "中城城": 127.80329519247289, "首里城": 127.7193727] 12 13 @IBOutlet weak var mapView: MKMapView! 14 var locManager:CLLocationManager! 15 @IBOutlet var longPressGesRec: UILongPressGestureRecognizer! 16 @IBOutlet weak var castleNameLabel: UILabel! 17 @IBOutlet weak var distanceLabel: UILabel! 18 var tapPointLatitude:CLLocationDegrees = 0.0 19 var tapPointLongitude:CLLocationDegrees = 0.0 20 21 override func viewDidLoad() { 22 super.viewDidLoad() 23 // ロケーションマネージャーのセットアップ 24 locManager = CLLocationManager() 25 locManager.delegate = self 26 locManager.requestWhenInUseAuthorization() 27 // 城のピンを立てる 28 makeCastlesPin() 29 } 30 31 // UILongPressGestureRecognizerのdelegate:ロングタップを検出する 32 @IBAction func mapViewDidLongPress(_ sender: UILongPressGestureRecognizer) { 33 // ロングタップ開始 34 if sender.state == .began { 35 } 36 // ロングタップ終了(手を離した) 37 else if sender.state == .ended { 38 // タップした位置(CGPoint)を指定してMkMapView上の緯度経度を取得する 39 let tapPoint = sender.location(in: view) 40 let center = mapView.convert(tapPoint, toCoordinateFrom: mapView) 41 42 let latStr = center.latitude.description 43 let lonStr = center.longitude.description 44// tapPointLatitude = CLLocationDegrees(latStr)! 45// tapPointLongitude = CLLocationDegrees(lonStr)! 46 tapPointLatitude = Double(latStr)! 47 tapPointLongitude = Double(lonStr)! 48 let nearCastleInfoArray = identifyNearCastle(tapPointLatitude: tapPointLatitude, tapPointLongitude: tapPointLongitude) 49 castleNameLabel.text = nearCastleInfoArray[0] 50 let distanceNum = round(Double(nearCastleInfoArray[1])!/1000) 51 distanceLabel.text = String(distanceNum) + "km" 52 } 53 } 54 //MAP上にピンを立てるメソッド 55 func makeMapPoint(latitude: CLLocationDegrees,longitude:CLLocationDegrees,title:String,subTitle:String){ 56 //ピンを立てる 57 //ピンを立てたい緯度、経度をセット 58 let coordinate = CLLocationCoordinate2DMake(latitude,longitude) 59 //ピンを生成 60 let pin = MKPointAnnotation() 61 // ピンのタイトル・サブタイトルをセット 62 pin.title = title 63 pin.subtitle = subTitle 64 //ピンに一番上で作った位置情報をセット 65 pin.coordinate = coordinate 66 //mapにピンを表示する 67 mapView.addAnnotation(pin) 68 } 69 70 // 日本百名城の位置情報が入った辞書をもとにマップ上にピンを立てるメソッド 71 func makeCastlesPin(){ 72 for castleName in castleNames{ 73 makeMapPoint(latitude: latDictionary[castleName]!, longitude: lngDictionary[castleName]!, title: castleName, subTitle: "") 74 } 75 } 76 77 // 2点間の距離を計算するメソッド 78 func calcDistance(latitude1:Double,latitude2:Double,longitude1:Double,longitude2:Double)->Double{ 79 80 let loc1:CLLocation = CLLocation(latitude: latitude1, longitude: longitude1) 81 let loc2:CLLocation = CLLocation(latitude: latitude2, longitude: longitude2) 82 // 二点間の距離を測定する(単位はメートル) 83 let distance = loc1.distance(from: loc2) 84 print(distance) 85 return distance 86 } 87 88 // タップ任意の位置情報から一番近い城の名前と距離を配列で返す 89 func identifyNearCastle(tapPointLatitude:Double,tapPointLongitude:Double) -> Array<String>{ 90 // 一度タップした地点と、百名城の距離をすべて計算する ->リストに入れる 91 var distanceList = [Double]() 92 for castleName in castleNames{ 93 let targetCastleLat = latDictionary[castleName] 94 let targetCastleLng = lngDictionary[castleName] 95 let distance = calcDistance(latitude1: tapPointLatitude, latitude2: tapPointLongitude, longitude1: targetCastleLat!, longitude2: targetCastleLng!) 96 // 城との距離をリストに追加 97 distanceList.append(distance) 98 } 99 100 // リストの中で一番小さい値を見つける 101 let sortedDistanceList = distanceList.sorted() 102 // 一番小さい値 103 let min = sortedDistanceList[0] 104 // distanceList中における一番小さい値のインデックス番号を取得 105 let indexNumber = distanceList.firstIndex(of: min) 106 // 一番小さい値で辞書からその城を特定する 107 let targetCastleName = castleNames[indexNumber!] 108 let nearCastleInfo:[String] = [targetCastleName,String(min)] 109 return nearCastleInfo 110 } 111} 112 113

試したこと

calcDistance,identifyNearCastleで使用する変数(tapPointLatitude,tapPointLongitudeなど)の型を

tapPointLatitude = CLLocationDegrees(latStr)! tapPointLongitude = CLLocationDegrees(lonStr)!

のようにしてみたのですが、結果は同じでした。

補足情報(FW/ツールのバージョンなど)

Xcodeのバージョン 12.4
Swiftのバージョン 5.3.2

初歩的な質問で申し訳ありませんが、何卒宜しくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

楽しいアプリですね。

さて、calcDistance が受け取る引数は (緯度1, 緯度2, 経度1, 経度2) という順番ですが、identifyNearCastle で calcDistance を呼び出す時は (緯度1, 経度1, 緯度2, 経度2) になってます。
呼び出す時の順番の方が自然だと思いますので、calcDistance の 2 番目と 3 番目の引数を入れ替えると良いでしょう。

(または、calcDistance の引数を (緯度1, 緯度2, 経度1, 経度2) ではなく、CLLocation を 2 つにして、identifyNearCastle の引数も CLLocation にするともっと良いと思います。)


修正内容をちゃんと書かなくて失礼しました。以下の 2 箇所です。

swift

1 func calcDistance(latitude1: Double, longitude1: Double, latitude2: Double, longitude2: Double) -> Double {

swift

1 let distance = calcDistance(latitude1: tapPointLatitude, longitude1: tapPointLongitude, latitude2: targetCastleLat!, longitude2: targetCastleLng!)

これで distance もたぶん正常な値が取れるようになるはず。
(緯度は -90 〜 90 の範囲なので、135 度とかだと不正な値ってことで distance が 0 になるのでは…。)


おまけ。余計なお世話かとは思いますが、データの持ち方は構造体の配列にするのが勧めです。

swift

1struct Castle { 2 let name: String 3 let lat: Double 4 let lng: Double 5} 6 7let castles = [ 8 Castle(name: "根室半島チャシ跡群", lat: 43.386969, lng: 145.7521176), 9 Castle(name: "五稜郭", lat: 41.7969948, lng: 140.7571649), 10 Castle(name: "松前城", lat: 41.4297918, lng: 140.1083792), 11 // 以下略

swift

1 // 日本百名城の位置情報が入った辞書をもとにマップ上にピンを立てるメソッド 2 func makeCastlesPin(){ 3 for castle in castles { 4 makeMapPoint(latitude: castle.lat, longitude: castle.lng, title: castle.name, subTitle: "") 5 } 6 }

投稿2021/02/27 12:58

編集2021/02/28 02:34
hoshi-takanori

総合スコア7899

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kasshi0827

2021/02/27 17:06

>>楽しいアプリですね。 ありがとうございます。そつない物ですが褒められるとやはりうれしいです。 また、引数の扱いについてもアドバイスありがとうございます。 参考になります。 しかし、引数の順番を変えても、問題は解決いたしませんでした。 もしよろしければ、こちらの >>発生している問題・エラーメッセージ distanceメソッドを使って、二点間の距離を計算する際、計算結果(下記ソースコード中の変数distance)が0.0になってしまいます。 についてもご教授いただけますと幸いです。 何卒よろしくお願いいたします。
kasshi0827

2021/02/28 09:05

修正点についてのご回答を頂きありがとうございます。 お陰様で正常に動作するようになりました! また、データの取り扱いに構造体を使うと良い、というアドバイスも非常に勉強になりました。 構造体を用いて一部書き直してみたところ、非常に見やすく、書きやすいコードになり感動しました! ほんとうにありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問