MKCoordinateSpan in Meters? - iphone

I need to create a MKCoordinateSpan that is about 500 meters.
How do I calculate the values to pass into the MKCoordinateSpan constructor?
Answers in any programming (Obj-C, .Net) language are fine.

Another alternative is to use MapKit's MKCoordinateRegionMakeWithDistance function:
MKCoordinateRegion rgn = MKCoordinateRegionMakeWithDistance(
CLLocationCoordinate2DMake(someLatitude, someLongitude), 500, 500);
The MKCoordinateSpan will be in rgn.span.

Unless you need great accuracy you can make it much easier with approximation. The first problem is to find the fraction of a degree of latitude representing 500 meters. Easy since a degree of latitude is a constant in any location, roughly 111 km. So 500 meters is .0045 degrees latitude.
Then it gets harder because length of a degree of longitude varies depending on where you are. It can be approximated with
where alpha is earth's equatorial radius, 6,378,137km, b/a is 0.99664719 (a constant in use for the WGC84 spheroid model in use by all GPS devices) and where phi is the degree of latitude.
Imagine for a second you're lucky enough to be in Melbourne with a longitude of 37.783 degrees S. North or South doesn't matter here. beta works out to be 37.6899 and the rest of it solves to give a longitudinal degree a length of 88km. So 500 meters is .0057 of a degree.
Result for Melbourne - MKCoordinateSpan melbourne500MeterSpan = MKCoordinateSpanMake(.0045, .0057);
You can check your answers and your code with this online calculator
The wiki article on longitude has a lot more detail on this (and it the source of the images here)
Code:
#define EARTH_EQUATORIAL_RADIUS (6378137.0)
#define WGS84_CONSTANT (0.99664719)
#define degreesToRadians(x) (M_PI * (x) / 180.0)
// accepts decimal degrees. Convert from HMS first if that's what you have
double spanOfMetersAtDegreeLongitude(double degrees, double meters) {
double tanDegrees = tanf(degreesToRadians(degrees));
double beta = tanDegrees * WGS84_CONSTANT;
double lengthOfDegree = cos(atan(beta)) * EARTH_EQUATORIAL_RADIUS * M_PI / 180.0;
double measuresInDegreeLength = lengthOfDegree / meters;
return 1.0 / measuresInDegreeLength;
}

In MonoTouch, then using this solution you can use this helper method:
public static void ZoomToCoordinateAndCenter (MKMapView mapView, CLLocationCoordinate2D coordinate, double meters, bool showUserLocationToo, bool animate)
{
if (!coordinate.IsValid ())
return;
mapView.SetCenterCoordinate (coordinate, animate);
mapView.SetRegion (MKCoordinateRegion.FromDistance (coordinate, meters, meters), animate);
}

Related

Find Minimum/Maximum latitude and Longitude

My Question is how can i find minimum and maximum latitude and longitude of specific area (500 meter) from current location.
In my case, Such like i need to get X and Y CLLocation (latitude and longitude) from 500meter of area
See my image (sorry for this may be bad drawing )
I also have to tried to googling and i got link such like
How can i get minimum and maximum latitude and longitude using current location and radius?
But i don't know how it implement in my case.
Pleas help me in this issue.
NOTE : I do not want to use CLLocationDistance distance = [currentLocation distanceFromLocation:newLocation]; because it is not helpful in my case so..
If you don't need a really precise value, then use the approximation that 1 degree is 111 km. Based on this, you need to add and remove 0.0025 degrees to the current coordinates to get corners of the area you are looking for.
rectanglesidelengthmeters = 500
degreedeltalat = 0.001 * (rectanglesidelengthmeters / 2.0) * cos(current.lon)
degreedeltalon = 0.001 * (rectanglesidelengthmeters / 2.0) * cos(current.lat)
minlat = current.lat - degreedeltalat
maxlat = current.lat + degreedeltalat
minlon = current.lon - degreedeltalon
maxlon = current.lon + degreedeltalon
You may need to correct the result a little for staying in the -90 .. 90 range for latitude and -180 .. 180 range for longitude values but I think CLClocation will handle that for you too.
You have to do some radius calculation from current location in km.
double kilometers = 0.5;
double curve = ABS( (cos(2 * M_PI * location.coordinate.latitude / 360.0) ));
MKCoordinateSpan span;
span.latitudeDelta = kilometers/111; //like allprog said.
span.longitudeDelta = kilometers/(curve * 111);
MKCoordinateRegion region;
region.span = span;
region.center = location.coordinate;
[self.mapView setRegion:region animated:YES];
This way i set mapView to show distance region to 0.5 km.
EDIT:
Whoa, i digging in my old 'liked' question to show you some original answer, but found a better one below accepted one:
how to make mapview zoom to 5 mile radius of current location
Look at #Anurag answer
To get precise value you should try with
minLattitude = currentLattitude - (RadiusInKm/111.12);
maxLattitude = currentLattitude + (RadiusInKm/111.12);
Thus in your case RadiusInKm = 0.5
For finding in & max longitude data you need to follow the same thing but but you have to multiply the result with cosine function of latitude
I would do this way.
double accuracy = 0.1;//How accurate do you want. Smaller value, slower perform
double distance = 500;//Distance you want
Create infinite loop.
In the loop check whether distance is bigger than 500. If yes, break. If not, add 0.1 value to latitude or longitude.
Do above way to get Max longitude, max latitude, min longitude and min latitude.
Compare your DB, if CLLocation is inside of the value, then return.
I cannot say this is the best way to solve your problem. Because we are guessing value...If you know how to convert CLLocation from given distance, that is better!
This should be correct (in php)
https://www.movable-type.co.uk/scripts/latlong-db.html
$R = 6371; // earth's mean radius, km
$rad = 0.5
// first-cut bounding box (in degrees)
$maxLat = $lat + rad2deg($rad/$R);
$minLat = $lat - rad2deg($rad/$R);
$maxLon = $lon + rad2deg(asin($rad/$R) / cos(deg2rad($lat)));
$minLon = $lon - rad2deg(asin($rad/$R) / cos(deg2rad($lat)));

Calculate Latitude and longitude more between Latitude/Longitude points?

Latitude: 22.744812,
Longitude: 75.892578
The above would be considered my center point.
And now I need to determine the latitude and longitude points from center point 1000 meter outward to each NSWE corners. So I would have a central long/lat, N, S, E and W long/lat..
So I would end up with 4 additional lat/long pairs.
What I am trying to resolve is a formula, preferably that can be done on a standard calculator to determine these 4 NSWE points based on the central point.
You could use MapKit for that:
- (CLLocationCoordinate2D *) calculateSquareCoordinates:(CLLocation*)center withRadius:(float)radius{
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(center.coordinate, radius*2, radius*2);
CLLocationCoordinate2D points[4];
points[0] = CLLocationCoordinate2DMake(region.center.latitude - region.span.latitudeDelta/2, region.center.longitude - region.span.longitudeDelta/2);
points[1] = CLLocationCoordinate2DMake(region.center.latitude + region.span.latitudeDelta/2, region.center.longitude - region.span.longitudeDelta/2);
points[2] = CLLocationCoordinate2DMake(region.center.latitude + region.span.latitudeDelta/2, region.center.longitude + region.span.longitudeDelta/2);
points[3] = CLLocationCoordinate2DMake(region.center.latitude - region.span.latitudeDelta/2, region.center.longitude + region.span.longitudeDelta/2);
return points;
}
and just call
CLLocationCoordinate2D *fourPoints = [self calculateSquareCoordinates:center withRadius:1000];
on your code.
you will have to use the Haversine formula to calculate the Lat/Long based on distance from a starting Lat/Long. have a look at this Link
The average radius of the earth is around 6371000 metres. This means that
1 degree of lattitude is equivalent to 6371000 * PI / 180 metres
(NB: PI = 3.14159... etc). However, 1 degree of longitude depends on the lattitude that you are. At the equator, one degree of longitude corresponds to the same distance in metres as 1 degree of lattitude. However, at the north and south poles, all longitude values are the same point (i.e. the pole itself), so 1 degree of longitude at the poles is zero metres. The formula for longitude is
1 degree of longitude is equivalent to 637100 * PI / 180 * COS(Lattitude)
where COS is the trigonometric cosine function. If you make these conversions, then you can do the calculation on a standard calculator. However, be aware that these are approximations that work well over short distances (e.g. less than a few hundred kilometers), but over long distances (e.g. thousands of kilometers) they become more and more inaccurate.

Latitude / Longitude Distance Calculation

A quick question about a Lat / Long calculation.
I want to take a value set e.g. Lat: 55.123456 Long -6.123456 and work out the four points that are an arbitrary distance away.
As the given square, I want to work out the value for Latitude on the left and right side. Thus the red lines are 1.5km from the start point. Likewise for the longitude, the blue lines will be 1.5km from the start point. The output will be 4 points, all distances in kilometres.
In short: Latitude + Y = Latitude Value X kilometers away
Working with iPhone at the moment and its for a very rough database calculation.
EDIT: Just to clarify, the distance is so short that curvature (And hence accuracy) is not an issue.
In OBJ-C this should be a decent solution:
float r_earth = 6378 * 1000; //Work in meters for everything
float dy = 3000; //A point 3km away
float dx = 3000; //A point 3km away
float new_latitude = latitude + (dy / r_earth) * (180 / M_PI);
float new_longitude = longitude + (dx / r_earth) * (180 / M_PI) / cos(latitude * 180/M_PI);
Well, for rough calculation with relatively small distances (less than 100km) you may assume that there is 40_000_000/360=111 111 meters per degree of latitude and 111 111*cos(latitude) meters per degree of longitude. This is because a meter was defined as 1/40_000_000 part of the Paris meridian;).
Otherwise you should use great circle distances, as noted in the comments. For high precision you also need to take into account that Earth is slightly oblate spheroid rather than a sphere.
// parameter: offset in meters
float offsetM = 1500; // 1.5km
// degrees / earth circumfence
float degreesPerMeter = 360.0 / 40 000 000;
float toRad = 180 / M_PI;
float latOffsetMeters = offsetM * degreesPerMeter;
float lonOffsetMeters = offsetM * degreesPerMeter * cos (centerLatitude * toRad);
Now simply add +/- latOffsetMeters and +/- lonOffsetMeters to your centerLatitude/ centerLongitude.
Formula is usefull up to hundred kilometers.

Calculate new coordinate x meters and y degree away from one coordinate

I must be missing somthing out in the docs, I thought this should be easy...
If I have one coordinate and want to get a new coordinate x meters away, in some direction. How do I do this?
I am looking for something like
-(CLLocationCoordinate2D) translateCoordinate:(CLLocationCoordinate2D)coordinate
translateMeters:(int)meters
translateDegrees:(double)degrees;
Thanks!
Unfortunately, there's no such function provided in the API, so you'll have to write your own.
This site gives several calculations involving latitude/longitude and sample JavaScript code. Specifically, the section titled "Destination point given distance and bearing from start point" shows how to calculate what you're asking.
The JavaScript code is at the bottom of that page and here's one possible way to convert it to Objective-C:
- (double)radiansFromDegrees:(double)degrees
{
return degrees * (M_PI/180.0);
}
- (double)degreesFromRadians:(double)radians
{
return radians * (180.0/M_PI);
}
- (CLLocationCoordinate2D)coordinateFromCoord:
(CLLocationCoordinate2D)fromCoord
atDistanceKm:(double)distanceKm
atBearingDegrees:(double)bearingDegrees
{
double distanceRadians = distanceKm / 6371.0;
//6,371 = Earth's radius in km
double bearingRadians = [self radiansFromDegrees:bearingDegrees];
double fromLatRadians = [self radiansFromDegrees:fromCoord.latitude];
double fromLonRadians = [self radiansFromDegrees:fromCoord.longitude];
double toLatRadians = asin( sin(fromLatRadians) * cos(distanceRadians)
+ cos(fromLatRadians) * sin(distanceRadians) * cos(bearingRadians) );
double toLonRadians = fromLonRadians + atan2(sin(bearingRadians)
* sin(distanceRadians) * cos(fromLatRadians), cos(distanceRadians)
- sin(fromLatRadians) * sin(toLatRadians));
// adjust toLonRadians to be in the range -180 to +180...
toLonRadians = fmod((toLonRadians + 3*M_PI), (2*M_PI)) - M_PI;
CLLocationCoordinate2D result;
result.latitude = [self degreesFromRadians:toLatRadians];
result.longitude = [self degreesFromRadians:toLonRadians];
return result;
}
In the JS code, it contains this link which shows a more accurate calculation for distances greater than 1/4 of the Earth's circumference.
Also note the above code accepts distance in km so be sure to divide meters by 1000.0 before passing.
I found one way of doing it, had to dig to find the correct structs and functions. I ended up not using degrees but meters for lat and long instead.
Here's how I did it:
-(CLLocationCoordinate2D)translateCoord:(CLLocationCoordinate2D)coord MetersLat:(double)metersLat MetersLong:(double)metersLong{
CLLocationCoordinate2D tempCoord;
MKCoordinateRegion tempRegion = MKCoordinateRegionMakeWithDistance(coord, metersLat, metersLong);
MKCoordinateSpan tempSpan = tempRegion.span;
tempCoord.latitude = coord.latitude + tempSpan.latitudeDelta;
tempCoord.longitude = coord.longitude + tempSpan.longitudeDelta;
return tempCoord;
}
And of course, if I really need to use degrees in the future, it's pretty easy (I think...) to do some changes to above to get it to work like I actually asked for.
Using an MKCoordinateRegion has some issues—the returned region can be adjusted to fit since the two deltas may not exactly map to the projection at that latitude, if you want zero delta for one of the axes you are out of luck, etc.
This function uses MKMapPoint to perform coordinate translations which allows you to move points around in the map projection's coordinate space and then extract a coordinate from that.
CLLocationCoordinate2D MKCoordinateOffsetFromCoordinate(CLLocationCoordinate2D coordinate, CLLocationDistance offsetLatMeters, CLLocationDistance offsetLongMeters) {
MKMapPoint offsetPoint = MKMapPointForCoordinate(coordinate);
CLLocationDistance metersPerPoint = MKMetersPerMapPointAtLatitude(coordinate.latitude);
double latPoints = offsetLatMeters / metersPerPoint;
offsetPoint.y += latPoints;
double longPoints = offsetLongMeters / metersPerPoint;
offsetPoint.x += longPoints;
CLLocationCoordinate2D offsetCoordinate = MKCoordinateForMapPoint(offsetPoint);
return offsetCoordinate;
}
Nicsoft's answer is fantastic and exactly what I needed. I've created a Swift 3-y version which is a little more concise and can be called directly on a CLLocationCoordinate2D instance:
public extension CLLocationCoordinate2D {
public func transform(using latitudinalMeters: CLLocationDistance, longitudinalMeters: CLLocationDistance) -> CLLocationCoordinate2D {
let region = MKCoordinateRegionMakeWithDistance(self, latitudinalMeters, longitudinalMeters)
return CLLocationCoordinate2D(latitude: latitude + region.span.latitudeDelta, longitude: longitude + region.span.longitudeDelta)
}
}

How to get meters in pixel in mapkit?

I wanted to test the mapKit and wanted to make my own overlay to display the accuracy of my position.
If i have a zoom factor of for example .005 which radius does my circle around me has to have(If my accuracy is for example 500m)?
Would be great to get some help :)
Thanks a lot.
Look at the documentation for MKCoordinateSpan, which is part of the map's region property. One degree of latitude is always approx. 111 km, so converting the latitudeDelta to meters and then getting to the meters per pixel should be easy. For longitudinal values it is not quite so easy as the distance covered by one degree of longitude varies between 111 km (at the equator) and 0 km (at the poles).
My way to get meters per pixel:
MKMapView *mapView = ...;
CLLocationCoordinate2D coordinate = ...;
MKMapRect mapRect = mapView.visibleMapRect;
CLLocationDistance metersPerMapPoint = MKMetersPerMapPointAtLatitude(coordinate.latitude);
CGFloat metersPerPixel = metersPerMapPoint * mapRect.size.width / mapView.bounds.size.width;
To add to another answer, a difference of one minute of latitude corresponds to one nautical mile: that's how the nautical mile was defined. So, converting to statute miles, 1 nautical mile = 1.1508 statue miles, or 6076.1 ft. or 1852 meters.
When you go to longitude, the size of the longitude circles around the Earth shrink as latitude increases, as was noted on the previous answer. The correct factor is that
1 minute of longitude = (1852 meters)*cos(theta),
where theta is the latitude.
Of course, the Earth is not a perfect sphere, but the simple calculation above would never be off by more than 1%.