Calculate coordinates of map square from map center - ios

I want to get the coordinates of the corners of the rectangle. Or to find the coordinate of the north-westest most point, 50 km from the map centre.
Does anyone know how I can do that?
The point is when I move around the map, I want to always have a rectangle(the rectangle does not need to drew, I just need its coordinates for a backend request), with it's corners always at 50 km from the current centre of the map.
I'm thinking of using somehow the distance function from CLLocation, but in this case I have the distance, but not one of the coordinates.
50km = mapCenterLocation.distance(from: coordinatesUnknown)

Not really sure what do you mean, but maybe this can help
func getNewTargetCoordinate(position: CLLocationCoordinate2D, userBearing: Float, distance: Float)-> CLLocationCoordinate2D{
//haversine formula
//r is earth radius
let r = 6378140.0
let latitude1 = position.latitude * (Double.pi/180);
let longitude1 = position.longitude * (Double.pi/180);
//bearing for user heading in degree
let brng = Double(userBearing) * (Double.pi/180);
//calculating user new position based on user distance and bearing can be seen at haversine formula
var latitude2 = asin(sin(latitude1)*cos(Double(distance)/r) + cos(latitude1)*sin(Double(distance)/r)*cos(brng));
var longitude2 = longitude1 + atan2(sin(brng)*sin(Double(distance)/r)*cos(latitude1),cos(Double(distance)/r)-sin(latitude1)*sin(latitude2));
//converting latitude as degree
latitude2 = latitude2 * (180/Double.pi)
longitude2 = longitude2 * (180/Double.pi)
// return location of user
return CLLocationCoordinate2DMake(latitude2, longitude2)
}
This work for NE direction and distance in meters
for the north-west direction, I think you can just put 135 for the degree and 5000 for distance.
For the position, you need to put map center location.
edit:
For custom rectangle., you can first check for the diagonal degree
func getDiagonalDegree(x: Float, y:Float) -> Float{
return atan2(y,x)*(180/Double.pi)
}
So now you can get that returned diagonal degree to and put it in getNewTargetCoordinate. New bearing is 270+diagonalDegree.

Not sure if I understand you correctly, but I think this could help, or at least point you on some direction
CLLocationCoordinate2D northWest;
northWest = [mapView convertPoint:CGPointMake(0, 0) toCoordinateFromView:mapView];
With this you will get the coordinates for the top left corner of the map, I think you just need to adjust this to set a point 50 km of your center and get the coordinate with this same logic.

Related

Calculate bearing in MKMapView gives wrong value while crossing 180 meridian

I need to draw lines to demonstrate transportation of goods on apple maps. To clarify start- and end-point, I draw a little arrowhead on the destination side.The arrowhead is drawn separately but it is reversed in one case.
>-->-->-->-
instead of
<--<--<--<-
I am using MKMapView and MKPolyline to draw lines. I am using MKOverlay to add direction arrows. The steps I follow are,
calculate bearing of
Source : CLLocationCoordinate2D(latitude: -33.8392932, longitude: 151.21519799999999)
Destination: CLLocationCoordinate2D(latitude: 39.645516999999998, longitude: -104.598724)
using the following function
open static func getDirectionOf( _ supplyLineWithCoordinates: [CLLocationCoordinate2D]) -> CGFloat {
guard let sourceCoordniate = supplyLineWithCoordinates.first,
let destinationCoordniate = supplyLineWithCoordinates.last else {
fatalError("Coordinates of supply line not found")
}
let sourcePoint: MKMapPoint = MKMapPointForCoordinate(sourceCoordniate)
let destinationPoint: MKMapPoint = MKMapPointForCoordinate(destinationCoordniate)
let x: Double = destinationPoint.x - sourcePoint.x
let y: Double = destinationPoint.y - sourcePoint.y
var arrowDirection = CGFloat(fmod(atan2(y, x), 360.0))
if arrowDirection < 0.0 {
arrowDirection += 2 * .pi
}
return arrowDirection
}
Rotate the arrow image and add it as the map overlay. The directions are calculated correctly in most of the cases, however, when I select the line shown below the direction is displayed 180 opposite. It starts from Sydney, Australia and ends in Denver, US
When trying to display the region with this two locations in mapView.setVisibleMapRect these region is not displayed, mapview tries to display region starting from Sydney (Australia) to Denver(US) through Asia and Europe, while it should display the map area I have attached above. If you have suggestions for optimisation, feel free to mention it.
I think this might be the reason, the direction should be calculated along the red line but it being calculated along the green line. Both lines are drawn by connecting same location coordinates in map. Any known workaround for this?
I solved it in a dirty way by converting coordinate to CGPoint and then calculating bearing between Points.
let destinationPoint = mapView.convert(destination, toPointTo: nil)
let sourcePoint = mapView.convert(source, toPointTo: nil)
let bearing = atan2(sourcePoint.y - destinationPoint.y, sourcePoint.x - destinationPoint.x) - .pi
Caution: This calculation will go wrong when map is rotated

MKMapView - how to calculate radius consistent with set-up

I draw a circle on an MKMapView. But how do I calculate the radius of this circle such that it equals the value of the radius used to draw the circle in the first place?
func setArea(_ center:CLLocationCoordinate2D, radius:CLLocationDistance) {
let area = MKCircle(center:center, radius:radius / LocationViewController.kRadiusInset)
mapView.setVisibleMapRect(mapView.mapRectThatFits(area.boundingMapRect), animated:false)
}
I've tried the following but it returns a value very slightly larger than radius passed to setArea. It calculates the distance from the center of the map to the left-hand edge.
// getRadius
let distance = middle.distance(from: edge)
let middle = CLLocation(latitude: mapView.region.center.latitude, longitude: mapView.region.center.longitude)
let edge = CLLocation(latitude: mapView.region.center.latitude, longitude: mapView.centerCoordinate.longitude - (mapView.region.span.longitudeDelta * 0.5))
let distance = middle.distance(from: edge)
If I pass in '4000' metres to setArea() and then afterwards calculate the map's radius (say, within mapViewDidFinishRenderingMap() I get 4010.61219348448
Why the discrepancy?
I replaced the use of a MKCircle with the following:
let region = MKCoordinateRegionMakeWithDistance(center, radius * 2, radius * 2)
mapView.region = region
This is less inaccurate. With radius == 4000 fed into this I get 4000.00048143541 after I set my MKMapView's region. This difference will creep into the user-interface but not nearly as quickly as my first approach.
Can anyone get closer?

Request mapkit annotations to the server by position and zoom level

I have this question:
I have an API server to request points nearest a position (lat & long) and distance (km)
I would to call this API when the user make a pan on the map..so I would to calculate the distance parameter in function of zoom level..
How can I obtain this?
At this moment I have this MapKit delegate method:
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
MKZoomScale currentZoomScale = mapView.bounds.size.width / mapView.visibleMapRect.size.width;
NSLog(#"currentZoom:%f", currentZoomScale);
[self.dataReader sendServerRequestWithCoordinate:mapView.region.center andDistance:[self getDistanceByZoomLevel:currentZoomScale];
}
- (float) getDistanceByZoomLevel:(MKZoomScale) zoomLevel {
/// ?????? ////
}
You don't need to calculate the distance based on a "zoom level" or "zoom scale".
Map Kit and Core Location have methods and functions to calculate distance given coordinates or map points.
Assume you want to use the diagonal distance (from top-left to bottom-right) covered by the currently visible map.
The corner coordinates (CLLocationCoordinate2Ds) can be obtained from the map view's region property and then you can use the distanceFromLocation method to calculate the distance between the two coordinates.
The corner map points (MKMapPoints) can be obtained from the map view's visibleMapRect property and then you can use the MKMetersBetweenMapPoints function to get the distance between them.
Here's an example using the map points:
MKMapRect vmr = mapView.visibleMapRect;
//vmr.origin is the top-left corner MKMapPoint
MKMapPoint bottomRightMapPoint =
MKMapPointMake(vmr.origin.x + vmr.size.width,
vmr.origin.y + vmr.size.height);
CLLocationDistance distanceMeters =
MKMetersBetweenMapPoints(vmr.origin, bottomRightMapPoint);
Of course, if you want kilometers instead of meters, divide distanceMeters by 1000.0.
Also, if by "distance" you actually want the radius (distance from the center point), then also divide distanceMeters by 2.0.

MKMapView How-to convert from longitude latitude back to cm?

1) I am using MKMapView to display a custom Image (for example width=350 cm,height =230 cm) in a MKOverlayView.
2) The center of the Image is now at longitude=0 and latitude = 0 and covers the whole world
4) I place a MKPointAnnotation at longitude = 60.749995 and latitude =56.091651
Now I want convert this Point(Longitude,Latitude) back to x,y in cm.
So that I can create a JPG on the server with the annotation on top of the Image.
So how do I calculate the x,y values?
Thanx Craig
so something like:
CLLocationCoordinate2D coordinateOrigin = CLLocationCoordinate2DMake(90, -180);
CLLocationCoordinate2D coordinateMax = CLLocationCoordinate2DMake(-90, 180);
MKMapPoint maxMap=MKMapPointForCoordinate(coordinateMax);
MKMapPoint minMap=MKMapPointForCoordinate(coordinateOrigin);
double width = maxMap.x-minMap.x;
double height = maxMap.y-minMap.y;
MKMapPoint p = MKMapPointForCoordinate(wanted_coord);//wanted_coord is the one needed
double pixel_x=p.x/width;
double pixel_y=p.y/height;
1) You're not really dealing with cm, you're dealing with pixels. An image has a certain number of pixels in each direction, the physical measurement of cm depends on how big your screen/printer's pixels are.
2) to convert from lat long to pixels use MKMapPoints via the MKMapPointForCoordinate function. That will give you a x/y coordinate and you'll need to scale those values to fit your custom image, therefore you need to work out what MKMapPoints it covers. For example if your image covered the entire world you could find the minimum values for MKMapPoint by using MKCoordinateForMapPoint with (-180,-90) and the maximum values with (180,90). Now you'll have the max/min for MKMapPoint's x and y, you know the max/min for your image, so it's trivial to scale from one to the other.

Best practice for using lat/long within a UIView (not MKMapView)

Basically i have a list of POI's (name,lat,long) and i want to draw them on the UIView, relative to my current lat/long. I'm looking for some best practice for mapping these POI (lat/long) to a UIView.
I don't want to use MKMapView (no need for displaying map-data).
I was reading:
http://developer.apple.com/library/ios/#documentation/general/conceptual/Devpedia-CocoaApp/CoordinateSystem.html
But I'm clueless how i get from a CLLocation to a (x,y) on my UIView. I only want to draw those POI's around my current location. So, for example if my screen would represent a 20 by 30 KM region, how do i map my POI's to their corresponding (x,y) coordinates?
Thanks.
What you're doing is a little strange, but you can convert latitude/longitude to a CGPoint-like struct called an MKMapPoint. An MKMapPoint has an x and y value which correspond to points on a map. Imagine if you laid out a flat map of the world, and 0,0 was the top left. MKMapPoint is a point on that map using that coordinate system.
Use the function MKMapPointForCoordinate() to convert a CLLocationCoordinate2D to an MKMapPoint
MKMapPoint myMapPoint = MKMapPointForCoordinate(myLocationCoordinate);
When you get the list of points, you'll have to do something like finding the max and min x and y values, then fitting all the points into your view using those values, otherwise you'll end up with a load of very close points in one place in your view.
My guess is that, for a 20KM by 30KM region, you can consider the earth to be flat and there fore linearly extrapolate the coordinates. I am sure you can google and find out as to how much distance is a difference in 0.00001 in latitude and longitude.
So if you have 20Km to be represented on X axis, and your current location is 30.1234567 in latitude, and 0.0000001 is 1 km then you can put your coordinate in the center of the screen and 30.1234557 as the left most X coordinate and so on.
I am not trying to provide an answer here, but just trying to think out loud, because I wanted to do some thing similar as well and did it as an Internet based app (without display though), where given two coordinates, I had to find the distance between them.
There are many (many) different approaches to modelling the planet and translating 3D coordinates onto a 2D surface, and the errors introduced by the various methods vary depending on what part of the globe you are. This question seems to cover most of what you are after though:
Converting Longitude & Latitude to X Y on a map with Calibration points
I think its best way (correctly work for Mercator projection map):
extension UIView
{
func addLocation(coordinate: CLLocationCoordinate2D)
{
// max MKMapPoint values
let maxY = Double(267995781)
let maxX = Double(268435456)
let mapPoint = MKMapPointForCoordinate(coordinate)
let normalizatePointX = CGFloat(mapPoint.x / maxX)
let normalizatePointY = CGFloat(mapPoint.y / maxY)
let pointView = UIView(frame: CGRectMake(0, 0, 5, 5))
pointView.center = CGPointMake(normalizatePointX * frame.width, normalizatePointY * frame.height)
pointView.backgroundColor = UIColor.blueColor()
addSubview(pointView)
}
}
My simple project for adding coordinate on UIView: https://github.com/Glechik/MapCoordinateDrawer

Resources