In my app I use satellite imagery in MapKit. I use a 2D satellite map most of the time using MKMapCamera with a pitch of 0. The user can change to a 3D map with a pitch of 80 and a much lower distance setting since I don't need to see too far away in 3D.
This works beautifully here in the Chicago metropolitan area, but not at all in more rural areas where I've tried it (Alabama, West Texas and a little ways outside of Nashville and Atlanta). In these areas it seems that the pitch change in the map is extremely limited so the end result is that the camera just dives straight down into the ground as the distance is reduced rather than moving down and panning out for the expected beautiful 3D view. As I said, this works great in and around the Chicagoland area.
I suspect that this is because the information used for more extreme (80 degree) 3D views is limited in many areas (perhaps only available for those areas with flyover availability as in https://www.apple.com/ios/feature-availability/#maps-flyover ?).
Is there a way to programmatically check whether this type of 3D view is available in a given location?
I'd like to be able to disable the 3D controls in locations where the 3D view isn't available. The "isPitchEnabled" property simply looks to see if pitch adjustment is enabled, NOT if the requested pitch change actually works in a specific location.
Here are snippets of the code I use, which works beautifully in some locations:
Setting the MKMapCamera -
var mapCamera: MKMapCamera {
// MARK: This sets the camera perspective and position. It may be generally a 2D view showing the map from directly above or it may be a 3D view looking down the line
return MKMapCamera(
lookingAtCenter: cameraCenter(),
fromDistance: cameraDistanceUsed * locations.cameraZoom,
pitch: locations.cameraAngle,
heading: locations.bearing()
)
}
func cameraCenter() -> CLLocationCoordinate2D {
// MARK: This sets the center of the map
if locations.cameraZoom == CameraZoom.birdsEye.rawValue {
// In the 2D (birds-eye) view, set the camera center to be the midpoint of the scene so that the entire scene is visible.
return locations.midpoint.scene
} else {
// In the 3D view, set the camera center to be the midpoint between the user and the target so that the map is aligned with the direction of the user
return locations.midpoint.target
}
}
Making the MapView -
func makeUIView(context: Context) -> MKMapView {
// MARK: This creates the satellite map
let mapView = MKMapView()
// Sets the map type to Satellite view
mapView.preferredConfiguration = MKImageryMapConfiguration(elevationStyle: .realistic)
// Shows the user's location on the map
mapView.showsUserLocation = true
// Sets the map camera and the coordinator
mapView.delegate = context.coordinator
mapView.camera = mapCamera
mapView.isPitchEnabled = true
//
DispatchQueue.main.async {
self.courseMap = mapView
}
return mapView
}
Enums for changing the 2D/3D values -
enum CameraAngle: Double {
// MARK: This sets the value for the camera angle on the map.
// Degrees of "0" means the camera is directly above the map, in a 2D "birds-eye" view. Increasing the degrees of results in a more 3D view looking down the target line.
case birdsEye = 0
case threeD = 80
}
enum CameraZoom: Double {
// MARK: This sets the value for the camera zoom on the map.
// Zoom factor of "2.2" is used for the 2D "birds-eye" view. A lower zoom factor is used for 3D view looking down the target line since you don't need to see too far in the distance.
case birdsEye = 2.2
case threeD = 0.5
}
Switching between 2D and 3D -
Button {
// Change the camera angle and distance from the ground to create the 3D view where available
locations.cameraAngle = CameraAngle.threeD.rawValue
locations.cameraZoom = CameraZoom.threeD.rawValue
} label: {
Image(systemName: "chevron.up")
.font(.title)
}
Button {
// Raise the camera and set the angle to 0º to create the 2D "bird's eye" view of the map
locations.cameraAngle = CameraAngle.birdsEye.rawValue
locations.cameraZoom = CameraZoom.birdsEye.rawValue
} label: {
Image(systemName: "chevron.down")
.font(.title)
}
Any ideas on how I can check for 3D availability so I can disable the buttons in locations where needed?