iOS MapKit get actual visible area of MKMapView when mapView is 3D - ios

I've tried the following to get the actual visible area of my MKMapView after a region change. None produce the desired result after the user rotates the map.
use mapView.bounds and mapView.convertPoint to get NE and SW CLLocationCoordinate2D.
use mapView.visibleRect to create NE and SW MKMapPoints and convert those points to NE and SW CLLocationCoordinate2D.
use mapView.centerCoodinate and mapView.region.span latitude and longitude delta to calculate NE and SW latitude and longitude, which are then used for new NE and SW CLLocationCoordinate2D.
#1 and #2 come from this post, and all 3 work well enough until the user rotates the map, which brings the mapView.camera into play by changing its heading. Once this happens, the mapView.visibleRect does not match the actual visible area. I'm sure changing altitude and pitch will have similar issues. I understand why properties on MKMapView don't make sense once it goes 3D, but I don't know how to account for the mapView.camera. There is mention of this in a comment on one of the proposed answers in this post, but no solution provided.
My question is, how can I get the area that's actually visible to the user, through the mapView.camera, accounting for heading, altitude and pitch?

I was looking for an answer to the similar situation, and found this. For my project I resolved like the following. Hope this helps.
let northWestCoordinate = self.mapView.convert(CGPoint(x: 0, y: 0), toCoordinateFrom: self.mapView)
let southEastCoordinate = self.mapView.convert(CGPoint(x: self.mapView.frame.size.width, y: self.mapView.frame.size.height), toCoordinateFrom: self.mapView)

Related

How To Detect A User Is Within Range Of An Annotation

So I am working on a project that includes many uses placing annotations all around a map. The annotation, (which is a custom image with a much larger circular range) appears on the screen and, ideally, I would like for a user to be:
Notified if they are within the range of a annotation
and
Not be allowed to place another annotation within the range of another one if the circular pins overlap by, say, more than 25%
I think this is a pretty unique question and should be fun for somebody to help out with, so have fun! Thanks everybody!
You can check the distance from each annotation using
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location
This method measures the distance between the two locations by tracing
a line between them that follows the curvature of the Earth. The
resulting arc is a smooth curve and does not take into account
specific altitude changes between the two locations.
For more details refer Link
Try this:
let location = CLLocation(latitude: 1, longitude: 1)//Or user's location
let distance = location.distance(from: anotherLocation)
Edit:
As mentioned in the comments, you wanted to create an equidistant point. I suggest manually doing that:
Subtract the annotation's location from he user's location. Then add your distance back to the original one. For example:
The user's location = (1, 1)
The annotation's location = (3, 2)
Vertical difference would be 2
Horizontal difference would be 1
Then:
(3 + 2, 2 + 1)
Your result: (5, 3)
Now you would have two points (the one you just created and the user's location) at each end with a center point (original annotation)

(Swift 2.1) Load annotations for visible region in MKMapView

I am calling API and getting latitude, longitude, title and many other data and storing them into custom data structure. I get 20 results per request.
So i need to show only some of those 20 results into visible area of MKMapView. When user change the region of map, API call should be done and my custom data structure will be filled and again only in visible area annotation should be drawn.
I am calling API again into mapView:regionDidChangeAnimated method but i am not able to show annotation into visible Map area only.
Can anyone help me to figure out how I can show annotation into visible are only?
Note: This is a same question but for Objective-C and I don't understand the answer of #Shmidt, and how I can use that code.
MKMapView has attribute visibleMapRect, then you can use function MKMapRectContainsPoint
let point = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
let mapContainsPoint = MKMapRectContainsPoint(map.visibleMapRect, MKMapPointForCoordinate(point));`

Check if coordinate rectangle contains CLLocationCoordinate2D

I am using a special Map SDK for iOS and I am adding a custom shape to the map. The shape is always a different size and it could be a circle, square, star etc. the point being it is always dynamic whenever the app is run.
After adding this shape to the map, I can access it's property called overlayBounds which is described as: This property contains the smallest rectangle that completely encompasses the overlay.
The overlay is my shape that I'm adding to the map.
Whenever a location update is generated by CLLocationManager, I want to check and see if the most recent coordinate is inside of that overlayBounds property of the shape.
When accessing overlayBounds, it has an ne property and a sw property. Both of these are just CLLocationCoordinate2D's
So, if the overlayBounds is made up of two CLLocationCoordinate2D's and the CLLocationManager is always updating the user's location and giving me the most recent coordinate(CLLocationCoordinate2D), how can I check if that most recent coordinate is within the overlayBounds?
After doing a lot of research I have only found one potential solution to go off of which is this: https://stackoverflow.com/a/30434618/3344977
But that answer assumes that my overlayBounds property has 4 coordinates(CLLocationCoordinate2D's), when I only have 2.
Your description seems much harder then the actual question. So if I am getting this correctly your question is only to check if the point is inside the rectangle described in overlayBounds.
You have only 2 points as it is enough to define a rectangle. So NE and SW are the two points where the other two are received as (NE.x, SE.y) and (SE.x, NE.y). With this you may use the answer you linked or you may simply construct a MKMapRect where origin is NE and size is SE-NE. So in this case you may simply use MKMapRectMake and then use MKMapRectContainsPoint. BUT watch out when computing size as SE-NE might produce negative results in which cases you need to add degrees to the size. That is 180 to x (latitude) and 360 to y (longitude)...
MKMapRect rect = MKMapRectMake(NE.latitude, NE.longitude, SE.latitude-NE.latitude, SE.longitude-NE.longitude);
if(rect.width < .0) rect.width += 180.0;
if(rect.height < .0) rect.height += 360.0;
BOOL pointInside = MKMapRectContainsPoint(rect, pointOnMap);
Something like this should do the trick.
Now if you are trying to check if the point is inside the shape itself it really depends on how your shape is defined. If this is some form of analytic representation you might find some method already made for you to return the value but if not then your best shot would most likely be drawing the shape to some canvas and checking the color of canvas at the location you need to check. In any case the bigger problem here is converting the point and the rect to a Cartesian coordinate system. If that is the case then just add a comment and I will try to help you on that...

MKMapview zoom level within Kilometer

i have a application which has 3 things in UIViewController
MapView
Search-control - (Ability to Search any Location in Map view)
Slider - (minimum value 0km - Maximum value 50Km)
Now i have Searched one city Lets Say Barcelona . it will search using Geocode method and i got the latitude and Longitude of the Barcelona and i have added one annotation of red Pin which show title and sub locality in annotation click..
i want to calculate distance of Zoom in zoom out with in the Radius of that annotation in Kilometers. what are the best possible way to achieve this ?
For e.g. i want to zoom around 30 km within that Radius
Any help will be greatly appreciated
Calculate a MKCoordinateRegion with the searched point as the center and 30km(or whatever distance) as the radius,
see this to do it. Set the map region to the calculated value using this method
setVisibleMapRect:animated:

Understanding MKCoordinateFromMapPoint behaviour

In a location based app we use MKMapPoints to store locations, for example the current user location.
When we try use this location on a MKMapView, to set the region that is initially displayed (zoomed in on the user) we convert this to a CLLocationCoordinate2D
There's a convernience method for that: namenly: MKCoordinateForMapPoint, but during testing this gives strange results.
MKMapPoint mapPoint = MKMapPointMake(51.96, 6.3); // My area ;)
CLLocationCoordinate2D automagicCoordinate = MKCoordinateForMapPoint(mapPoint);
CLLocationCoordinate2D manualCoordinate = CLLocationCoordinate2DMake(mapPoint.x, mapPoint.y);
I would expect both the automagicCoordinate and the manualCoordinate to be exactply the same.
but when I inspect it in the debugger I get the following result:
automagicCoordinate.latitude = (CLLocationDegrees) 85.05
automagicCoordinate.longitude = (CLLocationDegrees) -179.99
manualCoordinate.latitude = (CLLocationDegrees) 51.96
manualCoordinate.longitude = (CLLocationDegrees) 6.3
How come the coordinate created with the method is incorrect?
An MKMapPoint is not a latitude and longitude. If it was, you wouldn't need a function to "convert" it to coordinates.
As the Location Awareness Programming Guide explains in the Understanding Map Geometry section:
A map point is an x and y value on the Mercator map projection. Map points are used for many map-related calculations instead of map coordinates because they simplify the mathematics involved in the calculations.
The documentation for MKMapPoint is clearer:
If you project the curved surface of the globe onto a flat surface,
what you get is a two-dimensional version of a map where longitude
lines appear to be parallel. ...
The actual units of a map point are tied to the underlying units used
to draw the contents of an MKMapView, but you should never need to
worry about these units directly. ...
When saving map-related data to a file, you should always save
coordinate values (latitude and longitude) and not map points.
The map point 51.96, 6.3 corresponds to a coordinate at the top-left of the map projection. If you want to work with coordinates (latitude, longitude), use a CLLocationCoordinate2D to avoid confusion.
(You can technically use an MKMapPoint struct to store your coordinate values but then they don't need to be converted to coordinates and the wrong type usage will just lead to confusion.)

Resources