Can anyone explain these inaccuracies with MKMapView::mapRectThatFits - ios

I am getting strange inaccuracies when using MKMapView::mapRectThatFits. If I pass in an MKMapRect that is wider than it is higher I would expect mapRectThatFits to return a new MapRect with the same horizontal span, but with increased vertical span to fit it into the mapView, however I am noticing MKMapView adding to the horizontal span as well as the vertical.
This inaccuracy seems to increase as the coordinate region increases in dimensions. At a few hundred meters the accuracy is negligible, but at a 1.5 kilometers, the difference is 0.0006 which is significant.
I have tried using mapRectThatFits:edgePadding with edge padding set to zero and regionThatFits but both result in the same inaccuracies.
MKCoordinateRegion combinedRegion = self.models.locationModelsCoordinator.coordinateRegion;
NSLog(#"Before %f", combinedRegion.center.longitude - (combinedRegion.span.longitudeDelta * 0.5)); // Logs -0.103473
MKMapRect combinedRect = [ELMapKitUtils mapRectForCoordinateRegion:combinedRegion];
// If I convert combinedRect back to an MKCoordinateRegion here I can verify it is unaltered, so there is no issue with my conversion code.
MKMapRect focusRect = [self.mapView mapRectThatFits:combinedRect];
MKCoordinateRegion regionFittedToMapView = MKCoordinateRegionForMapRect(focusRect);
NSLog(#"After %f", regionFittedToMapView.center.longitude - (regionFittedToMapView.span.longitudeDelta * 0.5)); // Logs -0.104107

It is because you have bitmap tiles. MapKit today adapts to the full size of the bitmap tiles, showing horizontally wider area because of that. If MapKit did use vector based tiles, which it might do in a future version, as demonstrated yesterday during the Keynote, it wouldn't be limited by that bitmap tiles issue.
There is no workaround today with MapKit. I didn't look at bing Maps or Routeme. You should.

Related

MKMap not requesting more than 3 longitudinal tiles from OverlayRenderer

I used apples breadcrumb sample, adapted it and got to a weird effect in my code.
- (void)drawMapRect:(MKMapRect)mapRect
zoomScale:(MKZoomScale)zoomScale
inContext:(CGContextRef)context
is called with as many tiles as necessary to cover north to south, but it never requests more than 3 tiles east to west. So it never covers wide overlays.
Everything inside the tiles is drawn correctly etc. its the map that is simply not calling any more requests even with
- (BOOL)intersectsMapRect:(MKMapRect)mapRect {
return YES;
}
Center coordinate is smack in the middle of the bounds.
// init of CrumbPath : NSObject <MKOverlay>
upper = CLLocationCoordinate2DMake(49.0, 10.0);
lower = CLLocationCoordinate2DMake(48.0, 5.0);
_coordinate = CLLocationCoordinate2DMake(48.5, 7.5);
MKMapPoint upperLeft = MKMapPointForCoordinate(upper);
MKMapPoint lowerRight = MKMapPointForCoordinate(lower);
_boundingMapRect = MKMapRectMake(upperLeft.x,
upperLeft.y,
lowerRight.x - upperLeft.x,
lowerRight.y - upperLeft.y);
screenshot at http://imgur.com/lc5KpTT
MapKit can NOT handle MKMapRect with negative sizes. All calculations from and to CGRects and drawing WORK but the map itself will not request the right MapRects if the size is negative as it sees them as size 0.
So with 'abs' it will work
_boundingMapRect = MKMapRectMake(upperLeft.x,
upperLeft.y,
fabs(lowerRight.x - upperLeft.x),
fabs(lowerRight.y - upperLeft.y));

Yet another MKMapView zoom level and Offline maps

I've been working with MKMapView in iOS 7 trying to set and get programatically the zoom level in order to download and reuse map tiles when I were offline.
As I can't download the whole map into my phone, I download just several tiles in an appropriated zoom level and, afterwards I fix that zoom level and use the tiles thought MKTileOverlay and MKTileOverlayRenderer.
I tried to use Troybrant's algorithm (http://troybrant.net/blog/2010/01/set-the-zoom-level-of-an-mkmapview) but it didn't worked well for me. It failed to establish the zoom level back properly.
So I created one of my own that works fine.
Some explanations about my own method:
At the maximum map zoom level (20), you would see every map point at 1:1 scale. The whole map would have 256*2^20 points.
In retina displays there is a 2.0 scale factor between map points and pixels.
Apple maps can vary zoom level from 3 to 19 (min & max)
Then there is a simple inverse rule:
At maximum zoom level our view will show as much points as pixels it has (with the point-pixel scale factor of 2.0 for retina displays)
If zoom level is decreased the amount of map points shown should increase (inverse rule)
With that information the idea is to set the MKMapView's visibleRect property:
visibleRect width points = 2.0 * mapView.bounds.size.width * 2^(20-zoom)
Using that formula I've been able to centre my maps and apply to them zoom levels properly.
As Troybrant did, I created a category with the following methods:
#interface MKMapView (ZoomLevel)
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
zoomLevel:(NSUInteger)zoomLevel
animated:(BOOL)animated;
#end
#implementation MKMapView (ZoomLevel)
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
zoomLevel:(NSUInteger)zoomLevel
animated:(BOOL)animated
{
MKMapPoint centrePoint = MKMapPointForCoordinate(centreCoord);
CGFloat rectWidth = 2.0 * self.bounds.size.width * pow(2, 20-zoomLevel);
CGFloat rectHeight = 2.0 * self.bounds.size.height * pow(2, 20-zoomLevel);
MKMapRect visibleRect = MKMapRectMake(centrePoint.x-rectWidth/2, centrePoint.y-rectHeight/2, rectWidth, rectHeight);
[self setVisibleMapRect:visibleRect animated:animated];
}
#end
I hope this code can help you all.

MKMapView setting zoom limit without bouncing

There is a lot of questions and answers on stackoverflow about max zoom for MKMapView, for exmaple:
Is there way to limit MKMapView maximum zoom level?
Unfortunately non of the answers actually worked for me (or I just did not implement correctly). I was not able to simulate the behavior that native map has, that would be - restricting zoom level without 'bouncing' or resuming back.
This is my code that used:
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
if( mapView.region.span.latitudeDelta<0.001 || mapView.region.span.longitudeDelta<0.001){
MKCoordinateRegion region =mapView.region;
region.span = MKCoordinateSpanMake(0.001, 0.001);
mapView.region = region;
[mapView setRegion:mapView.region animated:NO];
}
}
When a user pinch and zoom map and the map reaches the max zoom (set by me), the map should not zoom in and then bounce back. Is it possible?
With my knowledge, I don't think it's actually possible to set max zoom or min zoom without making it yourself. But I know that if you use the Google Map SDK, you can actually set a max and a min zoom for the map: https://developers.google.com/maps/documentation/ios/reference/interface_g_m_s_map_view

MKMapView Region

I have a question regarding setting the region on my MKMapView.
I need to set the mapview to display a specific region when my view first loads.
The north east and south west latitude and longitude of this region is:
North East Coordinate Lat:59.623724 Long:2.911587
South West Coordinate Lat:49.004833 Long:-11.361825
Further to this, I would like to 'lock' the mapview to this region. Ideally the lock will be transparent, i.e: the coordinates above represent the maximum extent of the MKMapView. However if it is simply a case of checking the northeast and southwest coordinates within
- (void)mapView:(MKMapView *)aMapView regionDidChangeAnimated:(BOOL)animated
and resetting the view if they exceed my maximum range, that would be acceptable to me also.
Many thanks for any pointers on this matter.
EDIT:
Regarding the first part of my question, I have figured out I can set the initial region on the MKMapView using the following code:
CLLocationCoordinate2D neCoord;
neCoord.latitude = 59.787643;
neCoord.longitude = 3.025857;
CLLocationCoordinate2D swCoord;
swCoord.latitude = 49.394171;
swCoord.longitude = -11.036642;
MKCoordinateRegion region;
region.center.latitude = neCoord.latitude - (neCoord.latitude - swCoord.latitude) * 0.5;
region.center.longitude = neCoord.longitude + (swCoord.longitude - neCoord.longitude) * 0.5;
region.span.latitudeDelta = fabs(neCoord.latitude - swCoord.latitude); // Add a little extra space on the sides
region.span.longitudeDelta = fabs(swCoord.longitude - neCoord.longitude); // Add a little extra space on the sides
region = [self.mapView regionThatFits:region];
[self.mapView setRegion:region animated:YES];
First, you'll need to make sure you set the region on the map view after the view has been displayed. If you set it before the map has loaded, it probably won't center on that region. Once you've done that, just set self.mapView.zoomEnabled = NO; and self.mapView.scrollEnabled = NO; and it will prevent the user from moving the map around.
If you want to lock the maximum bounds the user can view but still allow scrolling and zooming, you will have to use -mapView:regionDidChangeAnimated: and 'bump' the user back inside your bounds if they leave it. Note that the user experience for this will probably suck - they'll pan around, let go, and then the map will suddenly move back to the region you defined. You could try using -mapView:regionWillChangeAnimated: and modify the map region if they left your boundaries, that could be a little less jarring.

Zooming MKMapview to bound annotations makes annotation right on edges of mapview

Hi have MKMapView and I make it to zoom depending on the annotations added to mapview, but sometimes I see map zoomed to some level in which annotations fall on the edges and half visible. Below is the code i'm using to set the map region.
MKPolygon *poly = [MKPolygon polygonWithCoordinates:points count:annotationCount];
MKCoordinateRegion region=MKCoordinateRegionForMapRect([poly boundingMapRect]);
Please provide some solution, Thanks.
So your region is too small, have you considered making it bigger?
Your MKCoordinateRegion has a CLLocationCoordinate2D (center) and a MKCoordinateSpan (span). That MKCoordinateSpan has a latitudeDelta (consider this the height) and a longitudeDelta (consider this the width). What you want to do is a make a slightly larger region. So my first guess is
region.span.latitudeDelta = region.span.latitudeDelta * 1.01;
Then set your mapview to that region

Resources