I am using Google maps for iOS. Everytime the user pans left or right I reload some data based on new position using this method
- (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position {
Now I am trying to find a way to calculate that if the user moved a little bit in the same view, and the change was not drastic and it does not warrant another data load, then do not load it. To that End I created a variable called mostRecentCoord that I maintain on load that holds initial location
- (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position
{
//Here mostRecentCoord is self descriptive, latest entry before this move occured.
if(position.target.latitude == mostRecentCoord.latitude && position.target.longitude ==
mostRecentCoord.longitude) {
return;
}
I thought that I can put a limit on the change in lngtitude or latitude , and based on that decide whether to load new data or not, but then I have to take into account zoom level, scope of coverage.
Anyone has ideas on this? would appreciate it
Here's one idea:
When you do your data fetch, pad the bounds of the visible region on all sides by some amount (eg by 20%).
Then when the view moves, if the new visible region is wholly contained inside your padded region, you don't need to reload the data. If some of the new region is outside, you need to reload.
idleAtCameraPosition is called at every time the camera has ended it's view changes.
lat, and lng is your movement until the user stops to pan (..changing the camera). You can convert that to pixels and vice versa using: CGPoint point = [map.projection pointForCoordinate:coordinates];
GMSCameraPosition *lastCameraPosition;
- (void)mapView:(GMSMapView *)pMapView didChangeCameraPosition:(GMSCameraPosition *)position {
if (lastCameraPosition == nil) lastCameraPosition = position;
// Algebra :) substract coordinates with the difference of camera changes
double lat = position.target.latitude - lastCameraPosition.target.latitude;
double lng = position.target.longitude - lastCameraPosition.target.longitude;
}
dont forget to:
- (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position {
lastCameraPosition = nil;
}
Related
I am using a MKUserTrackingBarButtonItem button to allow the user to automatically track their location on a map. The problem is that when they tap this button, it is zoomed too far out. I want it to start at a specified zoom level (i.e. span). How can I achieve this?
When the user taps the button to change to MKUserTrackingModeFollow, it seems to use the same zoom level that the user last manually changed to (i.e. using gestures on the map). Attempting to specify a different zoom level via setRegion or setVisibleMapRect does not affect what zoom level will be used when the mode is changed to MKUserTrackingModeFollow.
Attempting to override mapView:didChangeUserTrackingMode: to set the region causes the mode to be changed back to MKUserTrackingModeNone. Example:
- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated {
if (mode == MKUserTrackingModeFollow) {
CLLocationCoordinate2D center = mapView.userLocation.location.coordinate;
MKCoordinateSpan span = MKCoordinateSpanMake(0.002306, 0.001717);
[mapView setRegion:MKCoordinateRegionMake(center, span) animated:YES];
// [mapView setUserTrackingMode:MKUserTrackingModeFollow animated:NO];
}
}
If I attempt to reset the mode immediately after setting the region, it works fine if the user is stationary, but zooms back out if the user is moving.
The simplest solution would be if there was a way to simply specify something like a zoom level for MKUserTraking by sending it my span value. However, since that doesn't seem to exist, what else can I do?
I had the same issue and used a different approach to fix it. You can use the MapCamera function for this instead of that button.
On each new location do this:
MKMapCamera *newCamera = [MKMapCamera cameraLookingAtCenterCoordinate:[newLocation coordinate]
fromEyeCoordinate:[oldLocation coordinate]
eyeAltitude:2000];
[mapView setCamera:newCamera animated:TRUE];
And play with the eyeAltitude.
If the user manually zooms in or out you can read the altitude value from mapview.camera.altitude also don't update the camera when the user is manually using the map.
According to apple documentation used here
https://developer.apple.com/reference/mapkit/mkmapview/1616208-usertrackingmode
Setting the tracking mode to follow or followWithHeading causes the map view to center the map on that location and begin tracking the user’s location. If the map is zoomed out, the map view automatically zooms in on the user’s location, effectively changing the current visible region.
Here changing the region does not effect your visible region due to that reason.
- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated {
if (mode == MKUserTrackingModeFollow) {
CLLocationCoordinate2D center = mapView.userLocation.location.coordinate;
MKCoordinateSpan span = MKCoordinateSpanMake(0.002306, 0.001717);
[mapView setRegion:MKCoordinateRegionMake(center, span) animated:YES];
// [mapView setUserTrackingMode:MKUserTrackingModeFollow animated:NO];
}
}
So you just need to change center coordinate on didChangeUserTrackingMode instead of changing the whole region
- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated {
if (mode == MKUserTrackingModeFollow) {
[self.mapView setCenterCoordinate:mapView.userLocation.location.coordinate animated:YES];
}
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
[self.mapView setCenterCoordinate:mapViewuserLocation.location.coordinate animated:YES];
}
on click of MKUserTrackingBarButtonItem change the zoom level
CLLocationCoordinate2D center = mapView.userLocation.location.coordinate;
MKCoordinateSpan span = MKCoordinateSpanMake(0.002306, 0.001717);
[mapView setRegion:MKCoordinateRegionMake(center, span) animated:YES];
I'm desperately looking for a workaround to the well documented bug in MapKit in iOS6 that makes MKUserTrackingModeFollowWithHeading effectively unusable at higher magnifications:
There is a very simple example project [here].(https://dl.dropboxusercontent.com/u/316978/MapKitBug.zip)
Steps to reproduce:
Tap the MKUserTrackingModeButton to zoom in to your location.
Tap to zoom in closer 2 or 3 times.
Tap MKUserTrackingModeButton to select MKUserTrackingModeFollowWithHeading
Issues:
This mode will not 'stick' and almost always exits after a matter of seconds (1-20).
The Blue 'Your Location' Annotation 'vibrates' and seems to move slightly from its central position whilst it's in MKUserTrackingModeFollowWithHeading.
Note that this is increasingly a problem as you zoom in. At the default magnification (which you are taken to when first tapping the MKUserTrackingModeButton Button, it is not so much of a problem.
An obvious workaround would be to limit zoom level, but unfortunately I need full zoom in my application.
I , too, got frustrated with this extremely annoying bug.
I have a deadline just around the corner, so I can't spend a lot of time trying to implement a workaround.
I had managed to get it to stay in MKUserTrackingModeFollowWithHeading at max zoom, but the User Location annotation "pin" still jittered around quite heavily, and in some edge cases, it was still canceling back into MKUserTrackingModeFollow.
What I did, initially, was to force a correction using BOOL flags in the regionDidChangeAnimated: delegate method, like so:
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
NSLog(#"regionWillChangeAnimated:");
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
NSLog(#"regionDidChangeAnimated:");
[self forceCorrectUserTrackingMode];
}
- (void)forceCorrectUserTrackingMode {
if (shouldFollowWithHeading == YES && ([mapView userTrackingMode] != MKUserTrackingModeFollowWithHeading) ) {
NSLog(#"FORCE shouldFollowWithHeading! - setUserTrackingMode:MKUserTrackingModeFollowWithHeading");
[self.mapView setUserTrackingMode:MKUserTrackingModeFollowWithHeading animated:YES];
} else if (shouldFollowWithHeading == NO && ([mapView userTrackingMode] != MKUserTrackingModeNone) ) {
NSLog(#"FORCE should NOT FollowWithHeading - setUserTrackingMode:MKUserTrackingModeNone");
[self.mapView setUserTrackingMode:MKUserTrackingModeNone animated:YES];
}
}
This actually got me pretty close, but it wasn't reliable enough, and like I said, I had to think about prioritizing other features for my deadline, so this is what I ended up doing:
First, I grabbed this code for a zoom category on MKMapKit: http://troybrant.net/blog/2010/01/set-the-zoom-level-of-an-mkmapview/
Next, I included this method that a visitor provided in that blog's comments:
- (int) getZoomLevel {
return 21 – round(log2(mapView.region.span.longitudeDelta * MERCATOR_RADIUS * M_PI / (180.0 * mapView.bounds.size.width)));
}
Finally, a little trial and error testing the zoom levels vs. the occurrence of the bug led me to the following "workaround":
CLLocationCoordinate2D userLocation_CenterCoordinate = CLLocationCoordinate2DMake([locationManager location].coordinate.latitude, [locationManager location].coordinate.longitude);
int currentZoomLevel = [MKMapView getZoomLevelForMapView:mapView];
int newZoomLevel = 17;
if (currentZoomLevel > 17) {
NSLog(#"Adjusting mapView's zoomLevel from [%d] to [%d], also centering on user's location", currentZoomLevel, newZoomLevel);
[mapView setCenterCoordinate:userLocation_CenterCoordinate zoomLevel:newZoomLevel animated:NO];
} else {
NSLog(#"Centering on user's location, not adjusting zoom.");
[mapView setCenterCoordinate:userLocation_CenterCoordinate animated:NO];
}
Opened a TSI, and Apple confirmed that there is no workaround. I wonder if they'll have fixed it in 7 …
I have been working on the same app for a number of months, and this is a new problem. I am wondering if there has been a change in the server side of the Apple Map data. Here's the issue:
My app (at times) wants to set an MKMapView region to the most fully zoomed in value possible around a particular location. To do this, I do something like:
self.map.mapType = MKMapTypeHybrid;
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(CLLocationCoordinate2DMake(item.lat, item.lng), 1.0, 1.0);
[self.map setRegion:region animated:NO];
Regardless of where item's coordinate is, I get the gridded "no satellite images" background. This does not seem to be related to available satellite imagery, as it behaves consistently across many areas of the US.
I am aware that setRegion:animated: may adjust the region after the fact. And I am aware the a 1m square is an unreasonably small area to attempt to show on a fairly large map. So, I've tried
[self.map setRegion:[self.map regionsThatFits:region] animated:NO];
Setting animated:YES does seem to prevent this from occurring, but I do not want to animate these changes.
A few more observations:
If I zoom out just 1 or 2 pixels, the map imagery appears.
Attempting to implement the map delegate method: – mapViewDidFailLoadingMap:withError: does not help. It never is called.
This seems to be new. The working version I have of the app in the app store, now exhibits similar issues.
I have seen this happen in other popular apps recently.
Any thoughts on a solution to this, or confirmation that it is a systemic problem?
//fix for ios6
if (region.span.latitudeDelta < .0005f)
region.span.latitudeDelta = .0005f;
if (!region.span.longitudeDelta < .0005f)
region.span.longitudeDelta = .0005f;
Make sure your region span for lat/lon isn't set too low and it will clear up.
I ended up subclassing MKMapView and overriding setRegion:. I've created a sample app in Github if anyone is interested in seeing the issue in action, or my solution:
https://github.com/DeepFriedTwinkie/iOS6MapZoomIssue
My setRegion: method looks like this:
- (void) setRegion:(MKCoordinateRegion)region animated:(BOOL)animated {
#try {
// Get the zoom level for the proposed region
double zoomLevel = [self getFineZoomLevelForRegion:region];
// Check to see if any corrections are needed:
// - Zoom level is too big (a very small region)
// - We are looking at satellite imagery (Where the issue occurs)
// - We have turned on the zoom level protection
if (zoomLevel >= (MAX_GOOGLE_LEVELS-1) && self.mapType != MKMapTypeStandard && self.protectZoomLevel) {
NSLog(#"setRegion: Entered Protected Zoom Level");
// Force the zoom level to be 19 (20 causes the issue)
MKCoordinateRegion protectedRegion = [self coordinateRegionForZoomLevel:MAX_GOOGLE_LEVELS-1.0 atCoordinate:region.center];
[super setRegion:protectedRegion animated:animated];
} else {
[super setRegion:region animated:animated];
}
}
#catch (NSException *exception) {
[self setCenterCoordinate:region.center];
}
}
I want my mapView to display at the initial size (zoom level I determine) with phone location being centred in the map. Once this is done I want the user to be able to change zoom levels and pan to their hearts content. Next time they come into the app I want to reinitialise the map the same as the previous time.
Problem is when I come in it seems to set the map size before it has got a valid location fix.
Can anyone point me at an example that does best practice initialisation?
I solve this problem in next way. I think code is self-explained
- (void)mapView:(MKMapView *)mapView
didUpdateUserLocation:(MKUserLocation *)userLocation
{
if (!_userLocated &&
userLocation.coordinate.latitude != 0.0 &&
userLocation.coordinate.longitude != 0.0)
{
MKCoordinateRegion mapRegion;
mapRegion.center = mapView.userLocation.coordinate;
mapRegion.span = MKCoordinateSpanMake(0.04, 0.04);
[mapView setRegion:mapRegion animated: YES];
_userLocated = YES;
}
}
And dont forget to set UIMapView delegate and Shows user location.
In my application there is an MKMapView and I am trying to get the center coordinates of the map region that is currently visible. I am using following method so that if user moves the visible region I'll get new center coordinates.
- (void)mapView:(MKMapView *)mapView1 regionDidChangeAnimated:(BOOL)animated
{
CLLocationCoordinate2D centre = [mapView centerCoordinate];
NSLog(#"MAP CENTER = %f,%f",centre.latitude,centre.longitude);
}
the problem is that when I switch to the UIViewController that contains MKMapView it gives MAP CENTER = 0.000000,0.000000 for two times then gives the actual coordinates MAP CENTER = 55.755786,37.617633. I want the actual coordinates as soon as I switch to that UIViewController.
Is the coordinates (55.755786,37.617633) your current location ?
MKMapView takes some time to get a lock on GPS to fetch the coordinates for your current location. Until then centerCoordinate might return (0,0)
Try this this may help you.
self.mapView.centerCoordinate = self.mapView.userLocation.location.coordinate;