Mapkit - Get Altitude

Mapkit - Get altitude

MapKit doesn't provide the altitude at a given point. You can only know the current altitude of the camera of a MKMapView, or the altitude of the user position. For your purpose, you can afford on external web services like:

  • Google Maps Elevation API
  • MapQuest API

Altitude in MapKit from specific location

I am pretty sure if you are indoors you cannot get altitude readings(indoors device uses cell tower/WIFI location). Try testing your code on a real device outdoors. You need the GPS reading to get an altitude reading.

I had a similar problem with my code, drove me nuts(2-3 hours). Then I was outside for a fag, tried again on real device. Poof, it worked.

Does MapKit provide elevation data?

MapKit won't give you this, but you could use the free MapQuest API.

How to determine the correct altitude for an MKMapCamera focusing on an MKPolygon

In order to solve this problem, I ended up doing the following:

1) rotating the MKPolygon around it's center coordinate to eliminate heading/rotation issues when determining a bounding rectangle: asking an MKPolygon for it's 'boundingMapRect' without this would return whatever minimum rectangle fit around the entirety of the shape. If a long, skinny polygon happened to be oriented diagonally from north-east to south-west, the bounding rect would be nearly square. Performing the rotation allows for the heading of the polygon to be taken into account in determining it's aspect ratio.

2) fitting the polygon's heading-corrected bounding rectangle into the aspect ratio of the snapshot viewport: this ensures that a very 'tall' polygon will still fit properly in a wide-aspect viewport and vice-versa.

3)[Removed from my example code] Creating a polygon of the resulting aspect-corrected bounding rectangle and rotating it back to the original heading using the polygon's center coordinate: This would likely be needed if working with large regions, as the next step involves measurement between horizontal/vertical bounding distances. In my case, I am working with very small regions that should not be impacted enough by the curvature of the earth to make a real difference.

4) determining the total horizontal and vertical bounding region in meters

5) using the larger dimension (Dimension) of the two distances to form the base measurement of a triangle, where A = minimum coordinate location on axis, B = maximum coordinate location on axis, and C = camera location (center coordinate of the polygon)

At this point, I was a bit stumped as to how to solve the altitude of the resulting triangle without having at least 1 of the angles. In performing some tests using an MKMapView instance, it appears that the aperture of the MKMapCamera is about 30 degrees -- this is regardless of augmenting the aspect ratio of the viewport, aspect ratio of the polygon, or any other factor than the curvature of the earth. I may be wrong about this assertion.

5) Using the aperture angle observed in my tests, calculate the needed altitude using (dimension / 2) / tan(aperture_angle_in_radians / 2)

Seeing how much time I ended up spending on this, I've decided to post the question/answer combo on StackOverflow in hopes that it either:
1) helps someone else in the same situation
2) is corrected by someone way smarter than I am and leads to an even better solution

Thanks!

OH, and of course, the code:

+ (double)determineAltitudeForPolygon:(MKPolygon *)polygon withHeading:(double)heading andWithViewport:(CGSize)viewport {
// Get a bounding rectangle that encompasses the polygon and represents its
// true aspect ratio based on the understanding of its heading.
MKMapRect boundingRect = [[self rotatePolygon:polygon withCenter:MKMapPointForCoordinate(polygon.coordinate) byHeading:heading] boundingMapRect];

MKCoordinateRegion boundingRectRegion = MKCoordinateRegionForMapRect(boundingRect);

// Calculate a new bounding rectangle that is corrected for the aspect ratio
// of the viewport/camera -- this will be needed to ensure the resulting
// altitude actually fits the polygon in view for the observer.
CLLocationCoordinate2D upperLeftCoord = CLLocationCoordinate2DMake(boundingRectRegion.center.latitude + boundingRectRegion.span.latitudeDelta / 2, boundingRectRegion.center.longitude - boundingRectRegion.span.longitudeDelta / 2);
CLLocationCoordinate2D upperRightCoord = CLLocationCoordinate2DMake(boundingRectRegion.center.latitude + boundingRectRegion.span.latitudeDelta / 2, boundingRectRegion.center.longitude + boundingRectRegion.span.longitudeDelta / 2);
CLLocationCoordinate2D lowerLeftCoord = CLLocationCoordinate2DMake(boundingRectRegion.center.latitude - boundingRectRegion.span.latitudeDelta / 2, boundingRectRegion.center.longitude - boundingRectRegion.span.longitudeDelta / 2);

CLLocationDistance hDist = MKMetersBetweenMapPoints(MKMapPointForCoordinate(upperLeftCoord), MKMapPointForCoordinate(upperRightCoord));
CLLocationDistance vDist = MKMetersBetweenMapPoints(MKMapPointForCoordinate(upperLeftCoord), MKMapPointForCoordinate(lowerLeftCoord));

double adjacent;
double newHDist, newVDist;

if (boundingRect.size.height > boundingRect.size.width) {
newVDist = vDist;
newHDist = (viewport.width / viewport.height) * vDist;

adjacent = vDist / 2;
} else {
newVDist = (viewport.height / viewport.width) * hDist;
newHDist = hDist;

adjacent = hDist / 2;
}

double result = adjacent / tan(Deg_to_Rad(15));
return result;
}

+ (MKPolygon *)rotatePolygon:(MKPolygon *)polygon withCenter:(MKMapPoint)centerPoint byHeading:(double)heading {
MKMapPoint points[polygon.pointCount];
double rotation_angle = -Deg_to_Rad(heading);

for(int i = 0; i < polygon.pointCount; i++) {
MKMapPoint point = polygon.points[i];

// Translate each point by the coordinate to rotate around, use matrix
// algebra to perform the rotation, then translate back into the
// original coordinate space.
double newX = ((point.x - centerPoint.x) * cos(rotation_angle)) + ((centerPoint.y - point.y) * sin(rotation_angle)) + centerPoint.x;
double newY = ((point.x - centerPoint.x) * sin(rotation_angle)) - ((centerPoint.y - point.y) * cos(rotation_angle)) + centerPoint.y;

point.x = newX;
point.y = newY;

points[i] = point;
}

return [MKPolygon polygonWithPoints:points count:polygon.pointCount];
}

MKMapCamera doesn't zoom to correct altitude

I don't know for certain why this is happening but I have a theory. When you're using the flyover map types the minimum altitude of the camera is restricted by the tallest structure in the centre of the map.

If you go to the Maps app, set it to 3D Satellite view and go directly above a tall building (say the Empire State Building in New York) you can only pinch to zoom to a little above the height of the building. If you pan the camera away from the tall structure you can pinch to zoom in further. The map won't let you zoom through or inside the structure. If you zoom in to the entrance of a tall building and try to pan towards the building, the map will adjust the altitude upwards without you pinching to zoom out to prevent you passing through the building.

So before the map is fully loaded, it doesn't know what the tallest structure at the centre is going to be. To prevent you zooming inside a tall structure, the map limits the minimum height. After the map is fully loaded and it knows that there are no tall structures it lets you zoom in closer.

When you set a long duration on the animation, it's giving the map a chance to load before it gets to the lower altitude. The map knows that there are no tall structures and allows further zooming in. I would say that if you tried a longer duration animation but throttled the network bandwidth it would stop working again.

Note that the Satellite mode allows you to pass through tall structures.

As a workaround, try using mapViewDidFinishLoadingMap: or mapViewDidFinishRenderingMap:fullyRendered: to know when to zoom in more.

How is the altitude of MKMapCamera calculated?

I found your post when asking the same question. I then found this post:

How to determine the correct altitude for an MKMapCamera focusing on an MKPolygon

Condensing this into the answer to your question (and mine):

    double distance = MKMetersBetweenMapPoints(MKMapPointForCoordinate(pinCenter.coordinate),
MKMapPointForCoordinate(pinEye.coordinate));
double altitude = distance / tan(M_PI*(15/180.0));

MKMapCamera *camera = [MKMapCamera cameraLookingAtCenterCoordinate:pinCenter.coordinate
fromEyeCoordinate:pinEye.coordinate
eyeAltitude:altitude];


Related Topics



Leave a reply



Submit