MapView regionThatFits set using horizontalAccuracy - iphone

I'm looking for a method to convert meters to a mapview region.
CLLocation's horizontalAccuracy is a double, representing the accuracy in meters. regionThatFits: takes an MKCoordinateRegion which has a span with longitudeDelta and latitudeDelta. How can I convert meters to a longitude/latitude span?

Found an answer. It seems that 1 degree of latitude is equal to around 111 kilometers, or 111120 meters
- (MKCoordinateRegion)regionForAccuracyOfLocation:(CLLocation *)location
{
CLLocationDegrees spanInDegrees = (CLLocationDegrees) (location.horizontalAccuracy / 222240);
MKCoordinateSpan span = MKCoordinateSpanMake(spanInDegrees, spanInDegrees) ;
CLLocationCoordinate2D coordinate = location.coordinate;
MKCoordinateRegion region = MKCoordinateRegionMake(coordinate, span);
return region;
}

Related

Is there way to set the current location to something other than the center in UIMap?

I want to show my current location on the left-hand side of the map.
CLLocationCoordinate2D theCoordinate;
theCoordinate.latitude = 26.926;
theCoordinate.longitude = 75.8235;
MKCoordinateRegion region;
region.center = theCoordinate;
MKCoordinateSpan span;
span.latitudeDelta = 0.005;
span.longitudeDelta = 0.005;
region.span=span;
[mapView setRegion:region animated:TRUE];
The above code shows my location in the center of the map because of this line:
region.center = theCoordinate;
Is there a way to change this on any specific position other than the center?
Thank you.
Nitesh
This is not specific to Objective-C. You need to calculate what the center would be, if the desired location is on the left of the map. ie, given a map of width X in pixels, and scale T, what is the long/lat of the point X/2 pixels east of my position. Once you have that, you can assign
region.center = theNewCoordinate;
You could add a kind of offset ?
CLLocationCoordinate2D coordinatesForCenter = [theCoordinate copy];
// Add an offset :
coordinatesForCenter.latitude+=5; // 5 for exemple (it's not significant)
coordinatesForCenter.longitude+=5;
// Center on your new coordinates :
region.center = coordinatesForCenter;
Hope it helps.

MKCoordinateRegionMakeWithDistance does not set the correct region on the MapView

I have this code :
- (void)viewDidLoad
{
[super viewDidLoad];
CLLocationCoordinate2D userLocation = CLLocationCoordinate2DMake(48.9793946200, 2.4726272850);
CLLocationDistance dist1 = 636.9887048804;
CLLocationDistance dist2 = 900.8380655203;
CLLocationDistance dist = dist1;
[self.myMapView setRegion:MKCoordinateRegionMakeWithDistance(userLocation, dist, dist) animated:YES];
// TEST
// ------------------------------------------------------------
MKCoordinateRegion region = self.myMapView.region;
CLLocationDegrees lat = region.center.latitude;
CLLocationDegrees lon = region.center.longitude - region.span.longitudeDelta/2;
CLLocation *west = [[[CLLocation alloc] initWithLatitude:lat longitude:lon] autorelease];
NSLog(#"User location: lat : %.10lf long : %.10lf", userLocation.latitude, userLocation.longitude);
NSLog(#"distance set: %.10lfm", dist);
NSLog(#"center: lat : %.8lf long : %.8lf", region.center.latitude, region.center.longitude);
CLLocation* centerRegion = [[[CLLocation alloc] initWithLatitude:region.center.latitude longitude:region.center.longitude] autorelease];
NSLog(#"distance to western boundary: %.2lfm", [centerRegion distanceFromLocation:west]);
lat = region.center.latitude - region.span.latitudeDelta/2 ;
lon = region.center.longitude;
CLLocation *north = [[[CLLocation alloc] initWithLatitude:lat longitude:lon] autorelease];
NSLog(#"distance to western boundary: %.2lfm", [centerRegion distanceFromLocation:north]);
// ------------------------------------------------------------
}
When setting dist = dist1, that gives :
User location: lat : 48.9793946200 long : 2.4726272850
distance set: 636.9887048804m
center: lat : 48.97937199 long : 2.47269630
distance to western boundary: 500.44m
distance to western boundary: 650.57m
When setting dist = dist2, that gives :
User location: lat : 48.9793946200 long : 2.4726272850
distance set: 900.8380655203m
center: lat : 48.97937199 long : 2.47269630
distance to western boundary: 500.44m
distance to western boundary: 650.57m
What's the problem here ? Why do I have the same display with 2 different distances ?
Final question : How can I be sure to display the wanted meters on the map, at minimum for horizontal and vertical visual (with or without animation of course) ?
If I understand it correctly, you want to tell the mapView "give me a map which is 636m across" or "give me a map which is 900m across". But the map gives you the same distance across for both of these.
When you set a map region, you generally don't get back exactly what you ask for, but instead a best fit. The map view looks at the region you requested, then creates a region which fits your region inside it. The problem is that the map view doesn't zoom exactly to your requested region, it finds the highest zoom level that allows all your region to be visible. It won't use an in-between zoom level. This is why when you use setRegion: the map always looks crisp. You can manually set an in-between zoom level by pinching in or out, but not (as far as I know) programatically.
Note too that the map view might change the actual region variable you pass to it. Here's the docs:
When setting a new region, the map may adjust the value in the region parameter so that it fits the visible area of the map precisely. This is normal and is done to ensure that the value in the region property always reflects the visible portion of the map. However, it does mean that if you get the value of that property right after calling this method, the returned value may not match the value you set. (You can use the regionThatFits: method to determine the region that will actually be set by the map.)
You can see the difference in regions by logging the region you give it, and the one the map view actually sets (although I didn't see the passed myRegion change):
MKCoordinateRegion myRegion = MKCoordinateRegionMakeWithDistance(userLocation, dist, dist);
NSLog(#"Passed: %f %f", myRegion.span.latitudeDelta, myRegion.span.longitudeDelta);
[self.mapView setRegion:myRegion animated:YES];
NSLog(#"Passed 2: %f %f", myRegion.span.latitudeDelta, myRegion.span.longitudeDelta);
NSLog(#"Set: %f %f", mapView.region.span.latitudeDelta, mapView.region.span.longitudeDelta);
> Passed: 0.005728 0.008702
> Passed 2: 0.005728 0.008702
> Set: 0.012957 0.013733
If you bump your distance up to 1200m, you'll be able to see the next zoom level.
BTW, there's a small error in your code:
NSLog(#"distance to western boundary: %.2lfm", [centerRegion distanceFromLocation:north]);
should be
NSLog(#"distance to northern boundary: %.2lfm", [centerRegion distanceFromLocation:north]);
One possible cause of this is that the region you are specifying has square aspect ratio while your MKMapView probably has a rectangular one.
When you set the region MKMapView will not use it exactly as it is, but will modify it so that:
its aspect ratio corresponds to that of the view
the new region contains the specified one
Thus if your view has a width:height aspect ratio of 2:1, then the west/east boundaries would be 200m from the center while north/south boundaries 100m from the center.
Something to try after setting the region as above:
MKCoordinateRegion region = self.mapView.region;
CLLocationDegrees lat = region.center.latitude;
CLLocationDegrees lon = region.center.longitude - region.span.longitudeDelta/2;
CLLocation *west = [[CLLocation alloc] initWithLatitude:lat longitude:lon];
NSLog(#"distance to western boundary: %.2lfm", [userLocation distanceFromLocation:west]);
lat = region.center.latitude + region.span.latitudeDelta/2
lon = region.center.longitude;
CLLocation *north = [[CLLocation alloc] initWithLatitude:lat longitude:lon];
NSLog(#"distance to northern boundary: %.2lfm", [userLocation distanceFromLocation:north]);
One of those should be 100m. If not, I would be interested to see what they are.
P.S. The code above has not been tested in anyway.

how to make mapview zoom to 5 mile radius of current location

I know its a very common issue but I am not getting the exact answer for this thing.
How to make MKMapView defaults to a zoom of a 5 mile radius of current location.
Thanks in advance.
Use the MKCoordinateRegionMakeWithDistance function from MapKit.
Use a conversion function to convert miles to meters since MapKit uses meters.
float MilesToMeters(float miles) {
// 1 mile is 1609.344 meters
// source: http://www.google.com/search?q=1+mile+in+meters
return 1609.344f * miles;
}
Then in your code set the map region as (thanks to #DylanBettermann for pointing out that to get a radius of 5 miles, the distance needs to be doubled)
mapView.region = MKCoordinateRegionMakeWithDistance(
centerCoordinate,
MilesToMeters(10.0f),
MilesToMeters(10.0f)
);
swift 4 version :
mapView.region = MKCoordinateRegion(
center: centerCoordinate,
latitudinalMeters: MilesToMeters(10.0f),
longitudinalMeters: MilesToMeters(10.0f)
)
Use the following code when ever you want to zoom to 5 miles radius:
double miles = 5.0;
double scalingFactor = ABS( (cos(2 * M_PI * newLocation.coordinate.latitude / 360.0) ));
MKCoordinateSpan span;
span.latitudeDelta = miles/69.0;
span.longitudeDelta = miles/(scalingFactor * 69.0);
MKCoordinateRegion region;
region.span = span;
region.center = newLocation.coordinate;
[mapView setRegion:region animated:YES];
[self.theMapView setRegion:MKCoordinateRegionMake(
[theConsumer.latLong.clLocation coordinate],
MKCoordinateSpanMake( MAP_SPAN, MAP_SPAN ))
animated:YES];
The parameters to MKCoordinateSpanMake are in degrees, and 1 degree is approx 69 miles at the equator. Thus MAP_SPAN would be 5.0/69.0

Calculate the radius in meters using mapkit

I need to know the distance (in kilometers) from center map to the other side of the screen, (and if the zoom change the distance will change).
I need to implement this feature in this function
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{
}
Any ideas how i can do this?
Thanks
MkMapView has properties named centerCoordinate(CLLocationCoordinate2D) and region (MKCoordinateRegion). Region is a struct that:
typedef struct {
CLLocationDegrees latitudeDelta;
CLLocationDegrees longitudeDelta;
}MKCoordinateSpan
You should be able to create another point, based on centerCoordinate, let's say, by adding latitudeDelta to you latitude property or centerCoordinate, and calculate distance using the method of CLLocation:
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location
Something like this
MkMapView * mapView; // init somewhere
MKCoordinateRegion region = mapView.region;
CLLocationCoordinate2D centerCoordinate = mapView.centerCoordinate;
CLLocation * newLocation = [[[CLLocation alloc] initWithLatitude:centerCoordinate.latitude+region.span.latitudeDelta longitude:centerCoordinate.longitude] autorelease];
CLLocation * centerLocation = [[[CLLocation alloc] initWithLatitude:centerCoordinate.latitude:longitude:centerCoordinate.longitude] autorelease];
CLLocationDistance distance = [centerLocation distanceFromLocation:newLocation]; // in meters
And just calculate each time a delegate fires a certain method (decide which you need: MKMapViewDelegate)
Because rectangles aren't circles you can't get a radius.
But you can still get the furthest distance from the center of a region.
Reusing the same idea as above but embeded in a swift extension.
extension MKCoordinateRegion {
func distanceMax() -> CLLocationDistance {
let furthest = CLLocation(latitude: center.latitude + (span.latitudeDelta/2),
longitude: center.longitude + (span.longitudeDelta/2))
let centerLoc = CLLocation(latitude: center.latitude, longitude: center.longitude)
return centerLoc.distanceFromLocation(furthest)
}
}
Usage
let radius = mapView.region.distanceMax()

How do I show the user's location, and additional points on an iPhone map?

Basically I want to show the users location plus a list of selected location on a map. It can even have the standard iphone annotations. But, I have no idea of the general steps I would take to achieve this. Would I use MKMapView, or Core Location, or both? Could someone give me a simple outline of steps to take, or a link to a good tutorial or sample code. Thanks
To expand, I was wondering if there are any examples anywhere on how to deal with arrays of locations. I'm guessing that I would need to identify the users location then set up a radius of how far I want to reference locations away from the user, then populate that radius with an array of location that fit within that radius. Are my thoughts on this correct? And are there any examples out there of how to do at least a part of this. I have seen a ton of examples on how to show a single location, but none dealing with multiple locations.
Here's something I'm using that may help you. It will give you an MKCoordinateRegion that fits an array of CLLocations. You can then use that region to pass it to MKMapView setRegion:animated:
// create a region that fill fit all the locations in it
+ (MKCoordinateRegion) getRegionThatFitsLocations:(NSArray *)locations {
// initialize to minimums, maximums
CLLocationDegrees minLatitude = 90;
CLLocationDegrees maxLatitude = -90;
CLLocationDegrees minLongitude = 180;
CLLocationDegrees maxLongitude = -180;
// establish the min and max latitude and longitude
// of all the locations in the array
for (CLLocation *location in locations) {
if (location.coordinate.latitude < minLatitude) {
minLatitude = location.coordinate.latitude;
}
if (location.coordinate.latitude > maxLatitude) {
maxLatitude = location.coordinate.latitude;
}
if (location.coordinate.longitude < minLongitude) {
minLongitude = location.coordinate.longitude;
}
if (location.coordinate.longitude > maxLongitude) {
maxLongitude = location.coordinate.longitude;
}
}
MKCoordinateSpan span;
CLLocationCoordinate2D center;
if ([locations count] > 1) {
// for more than one location, the span is the diff between
// min and max latitude and longitude
span = MKCoordinateSpanMake(maxLatitude - minLatitude, maxLongitude - minLongitude);
// and the center is the min + the span (width) / 2
center.latitude = minLatitude + span.latitudeDelta / 2;
center.longitude = minLongitude + span.longitudeDelta / 2;
} else {
// for a single location make a fixed size span (pretty close in zoom)
span = MKCoordinateSpanMake(0.01, 0.01);
// and the center equal to the coords of the single point
// which will be the coords of the min (or max) coords
center.latitude = minLatitude;
center.longitude = minLongitude;
}
// create a region from the center and span
return MKCoordinateRegionMake(center, span);
}
As you're probably already established, you'll need to use a MKMapView and Core Location to do what you want. In my app I know what locations I want to display, and then make the MKMapView big enough to fit them all in. The method above will help you do that. If however you want to get a list of locations that fit within a given map region, then you'll have to do more or less the reverse of what I'm doing above.
Here's three:
http://mithin.in/2009/06/22/using-iphone-sdk-mapkit-framework-a-tutorial/
http://blog.objectgraph.com/index.php/2009/04/02/iphone-sdk-30-playing-with-map-kit/
http://www.iphonedevsdk.com/forum/tutorial-discussion/39374-mkmapview-tutorial-using-latitude-longitude.html