I am using react native maps in typescript in an iOS app. My initial region is pretty small, with latitudeDelta = 0.0000922 and longitudeDelta=0.0000922*(screenwidth/screenheight). My code uses useRef to save a reference to the MapView and when I get the first location (first latitude and longitude) from my server, I use mapRef.current.animateToRegion to animate to the region:
const INITIAL_LATITUDE_DELTA = 0.0000922;
const INITIAL_LONGITUDE_DELTA = INITIAL_LATITUDE_DELTA*(screenwidth/screenheight);
const server_callback = (serverResult) => {
...
mapRef.current.animateToRegion({
latitude: serverResult.latitude,
longitude: serverResult.longitude,
latitudeDelta: INITIAL_LATITUDE_DELTA,
longitudeDelta: INITIAL_LONGITUDE_DELTA
});
}
This works fine and shows my little region (about the size of a suburban backyard, a little blurry but fine). But I also want to allow the user to zoom in and out using hand gestures (pinch and expand) so I implement onRegionChangeComplete:
const onRegionChangeComplete = (newRegion: Region) => {
setRegion(newRegion);
}
The problem is that as soon as I do this, the size of the region expands to (what appears to be) the minimum size allowed by setRegion for the map. It seems like as soon as the user uses a gesture to change the region, or even uses a swipe to move it, the region size (the latitudeDelta and longitudeDelta) suddenly increase in value so that what I get in the region newRegion inside onRegionChangeComplete is this larger region. I can't zoom in any closer than this.
My MapView is defined like this:
<MapView mapType="satellite" style={styles.map} initialRegion={region} onRegionChangeComplete={onRegionChangeComplete} ref={mapRef}>
....
</MapView>
So, is it the case that animateToRegion allows me to zoom more than manual region changes allow? I need one of two things. Either I need to find a way to allow the manual gestures to keep this high zoom level (low deltas), or I need to know what the minimum values for the deltas are so that I can uses these as my initial/default values. How would I do either of these?
Related
I am displaying an MKMapView with another custom view overlaid on top (the 'radar' seen below). The radar is simply a static view that always stays centered and doesn't resize - it's not a map annotation. The idea is that a user can scroll and zoom the map until the radar covers a region they are interested in.
What's the best way to get the MKCoordinateRegion covered by the radar? I've tried convert:toRegionFromView: but I'm not seeing the results I would expect:
print("map region: \(mainView.map.region)")
// region: MKCoordinateRegion(center: __C.CLLocationCoordinate2D(latitude: 37.132839999999987, longitude: -95.785579999999968), span: __C.MKCoordinateSpan(latitudeDelta: 74.933954323122975, longitudeDelta: 61.276015749875469))
let radarRegion = mainView.map.convert(mainView.radar.frame, toRegionFrom: mainView.map)
print("radar region: \(radarRegion)")
// radar region: MKCoordinateRegion(center: __C.CLLocationCoordinate2D(latitude: 32.559875544717606, longitude: -95.760911716686806), span: __C.MKCoordinateSpan(latitudeDelta: 21.752004541359227, longitudeDelta: 25.901697478812096))
I would expect the radarRegion to have the same center coordinate, and a span roughly half, of those of map.region. Neither are true.
Here's a small project that replicates my setup.
According to the Apple documentation, convert:toRegionFrom: method:
Converts a rectangle in the specified view’s coordinate system to a
map region.
Parameters
rect
The rectangle you want to convert.
view The view that serves as the reference coordinate system for the
rect parameter.
So you really need to specify the view for which you provide the coordinates as the second parameter, not the map view :)
So, I believe if you pass in the frame for the radar as the first parameter, you should pass the radar view's parent as the second parameter and that should (theoretically) work.
Basically, if view is the parent view of which map (the MKMapView instance) and radar (the radar view) are children, I tried the following code:
let frame = radar.frame
let radarRegion = map.convert(frame, toRegionFrom:view)
map.setRegion(radarRegion, animated:true)
Before the above code was executed, this is what the screen looked like:
And after the above code runs:
The basic region is correct though there is a little bit of shifting up.
If this is not what you were expecting with regards to the region selected by the radar view, could you please explain what I might be missing?
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)
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));`
I'm trying to develop a delivery system for a restaurant, but I'm not sure how to approach this problem. The restaurant has five locations; four of them are in one state and the other is in a different state.
They only do deliveries for each location depending on how far it is; they also have setup certain limitations for each location.
My idea will be:
Fetch user's location on iOS (Accomplished)
Check if user location is inside of any Restaurant delivery radius. If so, set that location as the store, if not, just show a message that we don't delivery in their area.
Where I'm stuck
How can I define in Apple Maps the limits of Location 1, 2, 3, etc. (meaning what area will they be doing delivery to)?
Try setting the deliverable radius around the location of the restaurant. You can even draw an MKCircle to be fancy.
CLLocation * _storeLocation = [[CLLocation alloc] initWithLatitude:30.270135 longitude:-97.731270];
double deliverableRadius = 3 * 1609.34; // 3 miles (or the area they will deliver to)
MKCircle * circle = [MKCircle circleWithCenterCoordinate:storeLocation.coordinate radius:deliverableRadius];
[_mapView addOverlay:circle];
Once you have established the deliverable area, you can check to see if the see if the users location is within this area.
So I think a simple approach might be to simply check if the user is within a certain distance of a restaurant (ie "If I am less than 25 miles from a restaurant, then they will deliver to me").
First convert the address of the restaurant locations into gps coordinates. See Converting Place Names Into Coordinates for more info.
Next, calculate the distance between the user's location and the location of each restaurant and check if that distance less than the maximum delivery distance.
A formula for calcualating the distance between two points can be found here.
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)