Problem Refreshing iPhone MapView - iphone

Hey guys, I am having trouble getting overlays in my map view to refresh via the setNeedsDisplayInMapRect: function. Here is the relevant code:
ParkingMapViewController.m:
for (ParkingRegionOverlay *overlay in mapView.overlays) {
[overlay setNeedsDisplayInMapRect:self.mapView.visibleMapRect];
}
//...
- (MKOverlayView *)mapView:(MKMapView *)mapView
viewForOverlay:(id <MKOverlay>)overlay
{
NSLog(#"ParkingMapViewController.m mapView:viewForOverlay");
//...
}
//...
ParkingRegionOverlay.h:
#interface ParkingRegionOverlay : MKOverlayView <MKOverlay> {
MKPolygon *polygon;
MKMapRect boundingRect;
CLLocationCoordinate2D centerCoord;
//...
}
//...
And I am not getting the "ParkingMapViewController.m mapView:viewForOverlay" output to console I am expecting. I have walked through he debugger and have ensured that the for loop is being reached and executed, however mapView:viewForOverlay: isn't being called for some reason. Anyone know what I am doing wrong? Thanks in advance!
EDIT 1:
I believe I have set the delegate, coordinates, and bounding rect properly, but please take a look...
ParkingMapViewController.h
#interface ParkingMapViewController : UIViewController <MKMapViewDelegate> {
MKMapView *mapView;
//...
ParkingMapViewController.m:
//...
- (void)viewDidLoad {
[super viewDidLoad];
mapView.delegate = self;
//...
ParkingRegionOverlay.m:
//...
//initializes polygon and calculates bounding rect as well as its center coordinate
-(id)initWithPoints:(NSArray *)pointsArray andTitle:(NSString *)overlayTitle{
MKMapPoint points[[pointsArray count]];
double maxX = MIN_COORD_VAL;
double minX = MAX_COORD_VAL;
double maxY = MIN_COORD_VAL;
double minY = MAX_COORD_VAL;
double tempX = 0;
double tempY = 0;
if (self = [super init]) {
int i = 0;
//determine min/max extrema to help calculate the bounding rect
for (id coordDict in pointsArray){
tempX = [[coordDict objectForKey:#"latitude"] doubleValue];
tempY = [[coordDict objectForKey:#"longitude"] doubleValue];
maxX = fmax(tempX, maxX);
minX = fmin(tempX, minX);
maxY = fmax(tempY, maxY);
minY = fmin(tempY, minY);
CLLocationCoordinate2D coord = {tempX,tempY};
points[i] = MKMapPointForCoordinate(coord);
i++;
}//for
CLLocationCoordinate2D northWestCorner = CLLocationCoordinate2DMake(maxX, minY);
CLLocationCoordinate2D southEastCorner = CLLocationCoordinate2DMake(minX, maxY);
MKMapPoint northWestPoint = MKMapPointForCoordinate(northWestCorner);
MKMapPoint southEastPoint = MKMapPointForCoordinate(southEastCorner);
boundingRect = MKMapRectMake(northWestPoint.x, northWestPoint.y,
(southEastPoint.x-northWestPoint.x),
(southEastPoint.y-northWestPoint.y));
centerCoord = CLLocationCoordinate2DMake((maxX-minX)/2,(maxY-minY)/2);
polygon = [MKPolygon polygonWithPoints:points count:[pointsArray count]];
polygon.title = overlayTitle;
[self initAcceptedPermitsBasedOnTitle:overlayTitle];
}//if
return self;
}
//...
Thanks.
EDIT 2:
An alternate method I have tried, to no avail:
ParkingMapViewController.m
NSArray *overlayArray = [[NSArray alloc] initWithArray:[mapView overlays]];
[self.mapView removeOverlays:mapView.overlays];
[self.mapView addOverlays:overlayArray];
Removing and re-adding all overlays ain't working too well for me. It merely crashes when that third line is executed. Any ideas?
EDIT 3:
So I changed the previously posted code to the following:
NSArray *overlayArray = [mapView overlays];
[self.mapView removeOverlays:overlayArray];
[self.mapView addOverlays:overlayArray];
And am now seeing this in the console:
2011-05-05 14:24:54.145 Parking[68501:207] -[NSCFNumber boundingMapRect]: unrecognized selector sent to instance 0xa9afae0
2011-05-05 14:24:54.147 Parking[68501:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFNumber boundingMapRect]: unrecognized selector sent to instance 0xa9afae0'

Chances are you haven't set up the coordinate or boundingMapRect properties on the MKOverlay correctly. The MapView will only ask for the view if it thinks there is a possibility that it is visible, if its visible rect doesn't intersect the boundMapRect, it won't.
Also make sure your delegate for the mapView is set properly.

So I figured it out. Not necessarily the most efficient method, but it works for me. This is what I did:
[self.mapView removeOverlays:[mapView overlays]];
[self loadOverlaysAndAnnotations];
And here is loadOverlaysAndAnnotations:
- (void)loadOverlaysAndAnnotations {
NSError *error;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
CoreDataSingleton *coreDataSingleton = [CoreDataSingleton sharedManager];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"ParkingLot" inManagedObjectContext:[coreDataSingleton managedObjectContext]];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [[coreDataSingleton managedObjectContext] executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *overlayEntity in fetchedObjects) {
NSArray *pointsArray = [NSArray arrayWithArray:[overlayEntity valueForKey:#"overlayCoordPoints"]];
ParkingRegionOverlay *regionPolygon = [[ParkingRegionOverlay alloc] initWithPoints:pointsArray andTitle:[overlayEntity valueForKey:#"lotName"]];
[mapView addOverlay:regionPolygon];
[regionPolygon release];
NSSet *annotationsSet = [NSSet setWithSet:[overlayEntity valueForKey:#"parkingAnnotations"]];
NSArray *allAnnotations = [NSArray arrayWithArray:[annotationsSet allObjects]];
CLLocationCoordinate2D workingCoordinate;
for (ParkingAnnotations *annotation in allAnnotations) {
ParkingAnnotation *parkingAnnot = [[ParkingAnnotation alloc] init];
workingCoordinate.latitude = [[annotation latitude] doubleValue];
workingCoordinate.longitude = [[annotation longitude] doubleValue];
[parkingAnnot setCoordinate:workingCoordinate];
[parkingAnnot setTitle:[overlayEntity valueForKey:#"lotName"]];
if ([[overlayEntity valueForKey:#"lotName"] isEqualToString:#"VP 1"]) {
[parkingAnnot setLot:lot1];
}
[mapView addAnnotation:parkingAnnot];
[parkingAnnot release];
}
}
[fetchRequest release];
}//loadOverlaysAndAnnotations
In short, I didn't have to create a new function but merely call the function I used to load overlays into the map view and that works fine! Hope this helps anyone else stuck in a similar situation.
EDIT:
Important to note that I am reloading BOTH annotations and overlays, and, if done without first removing both annotations and overlays, can lead to crashing of your app if the reload function is called too many times. This is what I am currently experiencing. Just something to be aware of. To fix this I am going to have separate load functions, one for overlays, and one for annotations which will be called appropriately.

Related

Passing location to different view with segue

very very new to iOS programming, so I appreciate any help with this.
All I actually want to do is pass the variable of my current coordinates to a different view when a button is pressed. I can't work out how to do this using my current work - I'm getting in a total muddle. My project is essentially made from lots of chunks of code from various sources. I'll share with you the parts that I think are relevant, and hopefully somebody can at least point me in the right direction!
This is from my MainViewController.m:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
if (currentPosition == nil) {
MKMapPoint point = MKMapPointForCoordinate(newLocation.coordinate);
double pointsPerMeter = MKMapPointsPerMeterAtLatitude(newLocation.coordinate.latitude);
double visibleDistance = pointsPerMeter * 500.0;
MKMapRect rect = MKMapRectMake(
point.x - visibleDistance, point.y - visibleDistance,
visibleDistance * 2, visibleDistance * 2);
[self.mapView setVisibleMapRect:rect animated:YES];
NSURL *url = [NSURL URLWithString:#"https://url.url/json.json"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
NSArray *array = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&error];
if (error != nil)
{
}
CLLocationCoordinate2D location;
NSMutableArray *newAnnotations = [NSMutableArray array];
MKPointAnnotation *newAnnotation;
for (NSDictionary *dictionary in array)
{
location.latitude = [dictionary[#"lat"] doubleValue];
location.longitude = [dictionary[#"lon"] doubleValue];
newAnnotation = [[MKPointAnnotation alloc] init];
newAnnotation.coordinate = location;
HUWMapAnnotation *annotation = [[HUWMapAnnotation alloc] initWithCoordinate:newAnnotation.coordinate];
annotation.messagetitle = dictionary[#"name"];
annotation.email = dictionary[#"message"];
annotation.username = dictionary[#"user"];
[self.mapView addAnnotation:annotation];
[newAnnotations addObject:newAnnotation];
}
}
currentPosition = newLocation;
}
- (void)viewDidLoad { [super viewDidLoad];
// Check if the user has enabled location services.
if ([CLLocationManager locationServicesEnabled]) {
// Create a location manager.
locationManager = [[CLLocationManager alloc] init];
// Set ourselves as it's delegate so that we get notified of position updates.
locationManager.delegate = self;
// Set the desired accuracy.
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// Start tracking.
[locationManager startUpdatingLocation];
}
}
Like I said, I've trimmed this down substantially to the bits I think are relevant. Let me know if I have missed something essential. I've got my storyboard set up with a button ready to go, and a segue with its identifier.
I assume that I should be using prepareForSegue - but my issue is that I quite simply don't know how to get my coordinates into that situation.
I hope somebody will be able to help me (and I apologise for the large amount of copy-pasted code!)
To pass objects between view controllers using segues, do something like the following:
1) create a segue between the source and destination view controllers in IB, give it an identifier #"MySegue".
2) let's say the destination vc needs a string to run:
// DestinationVC.h
#interface DestinationVC : UIViewController
#property(strong, nonatomic) NSString *string;
#end
3) the source vc initiates a segue:
[self performSegueWithIdentifier:#"MySegue" sender:self];
4) the source vc prepares by initializing the property on the destination vc:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"MySegue"]) {
DestinationVC *vc = [segue destinationViewController];
vc.string = // some object that the source vc has. this could be your CLLocationCoordinate2D
}
}

Maximum number of annotations (MKAnnotation) that can be drawn on MKMapView?

I want to add a number of annotations(arround 500) on a mapview but the maximum it seems to display is 100. If I go beyond 100, viewForAnnotation delegate method is not called. However it works perfectly for annotations below 100.
here is the code: (works only when count of annotationArray array is less than 101)
_allPoints = [[NSMutableArray alloc] init];
NSString* responseFile = [[NSBundle mainBundle] pathForResource:#"Datafile" ofType:#"txt"];
NSData *sampleData = [NSData dataWithContentsOfFile:responseFile];
if (sampleData) {
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:sampleData
options:kNilOptions
error:&error];
NSArray* response = [json objectForKey:#"response"];
for (NSDictionary *lineDict in response) {
if ([[lineDict objectForKey:#"data"] isKindOfClass:[NSArray class]]) {
SinglePoint *singlePoint = [[SinglePoint alloc] initWithDictionary:lineDict];
[_allPoints addObject:singlePoint];
}
else {
NSLog(#"Error");
}
}
}
_mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[_mapView setDelegate:self];
[self.view addSubview:_mapView];
NSMutableArray *annotationArray = [[NSMutableArray alloc] init];
for (int i=0; i<[_allPoints count]; i++) {
SinglePoint *singlePoint = [_allPoints objectAtIndex:i];
NVPolylineAnnotation *annotation = [[NVPolylineAnnotation alloc] initWithPoint:singlePoint mapView:_mapView];
[annotationArray addObject:annotation];
}
[_mapView addAnnotations:(NSArray *)annotationArray];
CLLocationCoordinate2D centerCoord = { 28.632747, 77.219982 };
[_mapView setCenterCoordinate:centerCoord zoomLevel:12 animated:NO];
The delegate method is:
EDIT: As per comments, started reusing the view but with no luck :(
if ([annotation isKindOfClass:[NVPolylineAnnotation class]]) {
static NSString *viewIdentifier = #"annotationView";
NVPolylineAnnotationView *annotationView = (NVPolylineAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:viewIdentifier];
if (annotationView == nil) {
annotationView = [[NVPolylineAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:viewIdentifier mapView:_mapView];
}
return annotationView;
}
return nil;
I could not find any restriction in the documentation or anywhere else. Could it be memory issue?
The MKMapView has not Limit for annotationViews. But it gets quite laggy and unusable above a certain number of views (+1000).
I believe, that the reason for this is that you handle the annotationView management totally wrong. You shouldn't create a unique view for every single annotation, even if you are using ARC. Your rather should reuse every unused View like the cell of a UITableView.
There are a couple of good tutorials for this like Introduction to MapKit on iOS - Ray Wenderlich.
If this won't resolve your problem, you should try to debug your annotation classes. (NVPolylineAnnotation and NVPolylineAnnotationView). Maybe there's something wrong.
Did you also checked your points array for equal coordinates?
Finally was able to track down the problem. Solved it by setting the center of mapView first and then adding annotations later. I still dont know why 100 annotations were shown earlier (and why the no 100 only). Thank you all for your suggestions and time on this.
This is the code I changed:-
_mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[_mapView setDelegate:self];
[self.view addSubview:_mapView];
CLLocationCoordinate2D centerCoord = { 28.632747, 77.219982 };
[_mapView setCenterCoordinate:centerCoord zoomLevel:12 animated:NO];
NSMutableArray *annotationArray = [[NSMutableArray alloc] init];
for (int i=0; i<[_allPoints count]; i++) {
SinglePoint *singlePoint = [_allPoints objectAtIndex:i];
NVPolylineAnnotation *annotation = [[NVPolylineAnnotation alloc] initWithPoint:singlePoint mapView:_mapView];
[annotationArray addObject:annotation];
}
[_mapView addAnnotations:(NSArray *)annotationArray];
You can add as many annotations as you like. However, the views for each annotation will not be created until that annotation's coordinate property intersects with the visible portion of your map view. MapKit will not create a view just because you added an annotation to the map.

cannot add another object into an array containing different objects

Someone please help.
I am a noob here who has just created an array to contain all my polyclinics object. Now I need to add in a user object (patientDetail object) into this array. But no matter how i modify the viewDidLoad method, something just seems not quite right.. i cannot populate all the points.. only when i remove all codes that deal with the user object then it works.. Some1 please take a look at the method below and advise? I need to add in the patientDetail object and populate it with the rest of the polyclinics...
thanks for reading =(
- (void)viewDidLoad {
[super viewDidLoad];
_annotation2 = [[NSMutableArray alloc] init];
CLLocation *userLoc = _mapView.userLocation.location;
CLLocationCoordinate2D userCoordinate = userLoc.coordinate;
NSLog(#"user latitude = %f",userCoordinate.latitude);
NSLog(#"user longitude = %f",userCoordinate.longitude);
_annotations=[[NSMutableArray alloc] init];
_listOfPolyClinics = [[NSMutableArray alloc] init];
PatientDetails *patientDetails = [[PatientDatabase database]
patientDetails:_nric];
for (PolyClinics *polyclinics in [[PatientDatabase database]
polyClinics]){
[_listOfPolyClinics addObject:polyclinics];
}
[_listOfPolyClinics addObject:patientDetails];
for (PolyClinics *polyclinics1 in _listOfPolyClinics){
MyAnnotation* myAnnotation=[[MyAnnotation alloc] init];
if ([polyclinics1 isKindOfClass:[PatientDetails class]]){
CLLocationCoordinate2D theCoordinate3;
theCoordinate3.longitude = patientDetails.longitude;
theCoordinate3.latitude = patientDetails.latitude;
myAnnotation.coordinate = theCoordinate3;
myAnnotation.title = _nric;
myAnnotation.subtitle = [NSString stringWithFormat:#"%i",patientDetails.category];
}
else{
CLLocationCoordinate2D theCoordinate;
theCoordinate.longitude = polyclinics1.longtitude;
NSLog(#"Halo");
theCoordinate.latitude = polyclinics1.latitude;
NSLog(#"bye");
//myAnnotation.pinColor = MKPinAnnotationColorPurple;
myAnnotation.coordinate = theCoordinate;
myAnnotation.title = polyclinics1.name;
myAnnotation.subtitle = [NSString stringWithFormat:#"%i",polyclinics1.telephone];
}
[_mapView addAnnotation:myAnnotation];
[_annotation2 addObject:myAnnotation];
}
Because you have different classes in your array you can't use for (PolyClinics *polyclinics1 in _listOfPolyClinics)to iterate over the array. Use idinstead, then ask the object of what class it is and then cast it to that class if you have to.
Try to change your second for loop to
for (id polyclinics1 in _listOfPolyClinics){
MyAnnotation* myAnnotation=[[MyAnnotation alloc] init];
if ([polyclinics1 isKindOfClass:[PatientDetails class]]){
CLLocationCoordinate2D theCoordinate3;
theCoordinate3.longitude = patientDetails.longitude;
theCoordinate3.latitude = patientDetails.latitude;
myAnnotation.coordinate = theCoordinate3;
myAnnotation.title = _nric;
myAnnotation.subtitle = [NSString stringWithFormat:#"%i",patientDetails.category];
} else {
CLLocationCoordinate2D theCoordinate;
PolyClinics *polyclinic = (PolyClinics *)polyclinics1;
theCoordinate.longitude = polyclinic.longtitude;
NSLog(#"Halo");
theCoordinate.latitude = polyclinic.latitude;
NSLog(#"bye");
//myAnnotation.pinColor = MKPinAnnotationColorPurple;
myAnnotation.coordinate = theCoordinate;
myAnnotation.title = polyclinic.name;
myAnnotation.subtitle = [NSString stringWithFormat:#"%i",polyclinic.telephone];
}
[_mapView addAnnotation:myAnnotation];
[_annotation2 addObject:myAnnotation];
}

How to get a description for each pin, in Google Maps for the iPhone

I have a problem with MKPointAnnotation. I want to create my iPhone application like this:
Show a pin in Google Maps
Show a description on each pin, pulling that description from a NSMutableArray.
So, My question is, how do I show a description on each pin?
This is my code:
NSMutableArray *points = [[NSMutableArray alloc] init];
for(int i=0; i < [dataTable count]; i++) {
MKPointAnnotation *point =[[MKPointAnnotation alloc] init];
CLLocationCoordinate2D coordinate;
coordinate.latitude = [[[dataTable objectAtIndex:i] objectForKey:#"latitude"] floatValue];
coordinate.longitude = [[[dataTable objectAtIndex:i] objectForKey:#"longitude"] floatValue];
[point setCoordinate:coordinate];
[point setTitle:[[dataTable objectAtIndex:i] objectForKey:#"name"]]; //-- name of pin
[points addObject:point];
}
[map addAnnotations:points];
I would adopt the MKAnnotation protocol in my own class and simply override the
- (NSString) title
and implement
- (CLLocationCoordinate2D) coordinate {
CLLocationCoordinate2D theCoordinate;
theCoordinate.latitude = self.latitude;
theCoordinate.longitude = self.longitude;
return theCoordinate;
}
My class would also have an initialiser that takes all the data it needs (from the array you mentioned)
- (id) initWithTitle:(NSString *)title_ andLatitude:(CLLocationDegrees)latitude_ andLongitude:(CLLocationDegrees)longitude_;
When iterating I would create my own objects and then add them to the collection of annotations.
Cheers...

Newbie iphone development question

I am trying to draw a map with routes from a file. I am trying to learn this procedure by following this blog (http://spitzkoff.com/craig/?p=108).
When the process hits my function, it terminates (I have given the error message below)
- (void) drawRoute {
//
// load the points from our local resource
//
NSString* filePath = [[NSBundle mainBundle] pathForResource:#"route" ofType:#"csv"];
NSString* fileContents = [NSString stringWithContentsOfFile:filePath];
NSArray* pointStrings = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSMutableArray* points = [[NSMutableArray alloc] initWithCapacity:pointStrings.count];
for(int idx = 0; idx < pointStrings.count; idx++)
{
// break the string down even further to latitude and longitude fields.
NSString* currentPointString = [pointStrings objectAtIndex:idx];
NSArray* latLonArr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#","]];
CLLocationDegrees latitude = [[latLonArr objectAtIndex:0] doubleValue];
CLLocationDegrees longitude = [[latLonArr objectAtIndex:1] doubleValue];
CLLocation* currentLocation = [[[CLLocation alloc] initWithLatitude:latitude longitude:longitude] autorelease];
[points addObject:currentLocation];
}
// CREATE THE ANNOTATIONS AND ADD THEM TO THE MAP
// first create the route annotation, so it does not draw on top of the other annotations.
CSRouteAnnotation* routeAnnotation = [[[CSRouteAnnotation alloc] initWithPoints:points] autorelease];
[secondMap addAnnotation:routeAnnotation];
// create the rest of the annotations
CSMapAnnotation* annotation = nil;
// create the start annotation and add it to the array
annotation = [[[CSMapAnnotation alloc] initWithCoordinate:[[points objectAtIndex:0] coordinate]
annotationType:CSMapAnnotationTypeStart
title:#"Start Point"] autorelease];
[secondMap addAnnotation:annotation];
// create the end annotation and add it to the array
annotation = [[[CSMapAnnotation alloc] initWithCoordinate:[[points objectAtIndex:points.count - 1] coordinate]
annotationType:CSMapAnnotationTypeEnd
title:#"End Point"] autorelease];
[secondMap addAnnotation:annotation];
[points release];
// center and size the map view on the region computed by our route annotation.
[secondMap setRegion:routeAnnotation.region];
}
Error message:
[Session started at 2010-07-30 01:22:13 -0400.]
2010-07-30 01:22:19.078 ActualTry[4893:20b] Found center of new Route Annotation at 0.000000, 0
2010-07-30 01:22:19.079 ActualTry[4893:20b] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (0) beyond bounds (0)'
2010-07-30 01:22:19.080 ActualTry[4893:20b] Stack: (
807902715,
2492862011,
807986683,
807986522,
810976489,
810572359,
13379,
12278,
10121,
814709201,
815110321,
815119058,
815114270,
814813151,
814722763,
814748641,
839148405,
807687520,
807683624,
839142449,
839142646,
814752238,
9380,
9234
)
I will be really thankful if someone could help me in this. I know that this is basic gdb debugging, but I am not able to figure this out.
Thank you.
The error message says that you are trying to access an object in the array which doesn't exist. Looking at your code, it seems pointStrings array is empty. Are you sure your csv file has data? Try printing the pointStrings with NSlog.