MapKit+E.swift 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. //
  2. // MapKit+E.swift
  3. // Pods
  4. //
  5. // Created by Groot on 2025/5/14.
  6. //
  7. import MapKit
  8. import Foundation
  9. extension MKMapView {
  10. // 按比例 按padding适应annotation
  11. public func fitsAllPoints(
  12. points: [MKMapPoint],
  13. padding: UIEdgeInsets,
  14. aspectRatio: Double? = nil,
  15. animated: Bool = true
  16. ) {
  17. var zoomRect: MKMapRect = .null
  18. for point in points {
  19. let pointRect = MKMapRect(x: point.x, y: point.y, width: 0, height: 0)
  20. if zoomRect.isNull {
  21. zoomRect = pointRect
  22. } else {
  23. zoomRect = zoomRect.union(pointRect)
  24. }
  25. }
  26. // Calculate the desired size based on the aspect ratio
  27. let aspectRatio = aspectRatio ?? 1.0
  28. let width = zoomRect.size.width
  29. let height = zoomRect.size.height
  30. let desiredWidth = max(width, height * aspectRatio)
  31. let desiredHeight = max(height, width / aspectRatio)
  32. // Calculate the center of the annotations
  33. let center = MKMapPoint(x: zoomRect.midX, y: zoomRect.midY)
  34. // Create a new MKMapRect with the desired aspect ratio and centered on the annotations
  35. let newMapRect = MKMapRect(
  36. x: center.x - desiredWidth / 2,
  37. y: center.y - desiredHeight / 2,
  38. width: desiredWidth,
  39. height: desiredHeight
  40. )
  41. setVisibleMapRect(newMapRect, edgePadding: padding, animated: animated)
  42. }
  43. func calculateMapRect(points: [MKMapPoint], padding: UIEdgeInsets = .zero) -> MKMapRect {
  44. var zoomRect: MKMapRect = .null
  45. for point in points {
  46. let pointRect = MKMapRect(x: point.x, y: point.y, width: 0, height: 0)
  47. if zoomRect.isNull {
  48. zoomRect = pointRect
  49. } else {
  50. zoomRect = zoomRect.union(pointRect)
  51. }
  52. }
  53. return mapRectThatFits(zoomRect, edgePadding: padding)
  54. }
  55. func takeSnapShot(option: MKMapSnapshotter.Options, completion: @escaping (MKMapSnapshotter.Snapshot?) -> Void) {
  56. let shotter = MKMapSnapshotter(options: option)
  57. shotter.start(with: .global()) { shot, _ in
  58. completion(shot)
  59. }
  60. }
  61. ///移动至多个点的位置,并提供设置padding距离
  62. func moveToSuitableLocation(points: [CLLocationCoordinate2D], padding: ATMapPadding? = nil) {
  63. // 检查是否有足够的点
  64. guard points.count > 0 else {
  65. print("Error: No points provided to calculate suitable location")
  66. return
  67. }
  68. // 1. 计算所有点的边界
  69. var minLat = points[0].latitude
  70. var maxLat = points[0].latitude
  71. var minLng = points[0].longitude
  72. var maxLng = points[0].longitude
  73. for point in points {
  74. minLat = min(minLat, point.latitude)
  75. maxLat = max(maxLat, point.latitude)
  76. minLng = min(minLng, point.longitude)
  77. maxLng = max(maxLng, point.longitude)
  78. }
  79. // 2. 创建包含所有点的区域
  80. let centerLat = (minLat + maxLat) / 2
  81. let centerLng = (minLng + maxLng) / 2
  82. let center = CLLocationCoordinate2D(latitude: centerLat, longitude: centerLng)
  83. // 计算区域跨度,添加一些额外空间
  84. let spanLat = (maxLat - minLat) * 1.1 // 增加10%的边距
  85. let spanLng = (maxLng - minLng) * 1.1
  86. // 确保跨度不为0且不过大
  87. let safeSpanLat = max(min(spanLat, 100.0), 0.001) // 设置合理的最大跨度
  88. let safeSpanLng = max(min(spanLng, 100.0), 0.001)
  89. // 创建初始区域
  90. let span = MKCoordinateSpan(latitudeDelta: safeSpanLat, longitudeDelta: safeSpanLng)
  91. print("初始区域: center=\(center), span=\(span)")
  92. // 3. 创建区域并应用padding
  93. var region = MKCoordinateRegion(center: center, span: span)
  94. // 应用padding(如果有)
  95. if let padding = padding {
  96. // 检查地图视图尺寸是否有效
  97. let mapViewWidth = max(self.bounds.width, 1.0) // 确保宽度至少为1
  98. let mapViewHeight = max(self.bounds.height, 1.0) // 确保高度至少为1
  99. // 基于地图视图尺寸和padding计算经纬度调整量
  100. let latPadding = span.latitudeDelta * (padding.top + padding.bottom) / mapViewHeight
  101. let lngPadding = span.longitudeDelta * (padding.left + padding.right) / mapViewWidth
  102. // 调整区域跨度以应用padding,并确保不会导致无穷大
  103. region.span.latitudeDelta = min(region.span.latitudeDelta + latPadding, 180.0)
  104. region.span.longitudeDelta = min(region.span.longitudeDelta + lngPadding, 360.0)
  105. print("应用padding后的区域: span=\(region.span)")
  106. }
  107. // 4. 验证最终区域是否有效
  108. if !isValidRegion(region) {
  109. //print("无效区域,使用默认区域")
  110. region = defaultRegion()
  111. }
  112. print("最终使用的区域: \(region)")
  113. // 设置地图区域
  114. self.setRegion(self.regionThatFits(region), animated: true)
  115. }
  116. // 辅助方法:检查区域是否有效
  117. private func isValidRegion(_ region: MKCoordinateRegion) -> Bool {
  118. let center = region.center
  119. let span = region.span
  120. return !center.latitude.isNaN && !center.longitude.isNaN &&
  121. !span.latitudeDelta.isNaN && !span.longitudeDelta.isNaN &&
  122. !span.latitudeDelta.isInfinite && !span.longitudeDelta.isInfinite &&
  123. isValidCoordinate(center) &&
  124. span.latitudeDelta > 0 && span.latitudeDelta <= 180 &&
  125. span.longitudeDelta > 0 && span.longitudeDelta <= 360
  126. }
  127. // 辅助方法:提供默认有效区域
  128. private func defaultRegion() -> MKCoordinateRegion {
  129. return MKCoordinateRegion(
  130. center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
  131. span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
  132. )
  133. }
  134. // 辅助方法:检查坐标是否有效
  135. private func isValidCoordinate(_ coordinate: CLLocationCoordinate2D) -> Bool {
  136. return coordinate.latitude >= -90 && coordinate.latitude <= 90 &&
  137. coordinate.longitude >= -180 && coordinate.longitude <= 180
  138. }
  139. ///是否隐藏地图图标
  140. func mapLogoVisible(isHiddenLogo : Bool) {
  141. for subview in self.subviews {
  142. if let logo = subview as? UIImageView,
  143. logo.bounds.size.width <= 100,
  144. logo.bounds.size.height <= 20 {
  145. logo.isHidden = isHiddenLogo
  146. }
  147. }
  148. }
  149. }