Create multiple MKOverlays of polyline from locations coming via web-service - iphone

My app is real-time tracker, where multiple users are logged in and updating their location by sending their co-ordinates to our web service which is then called back let's after every 2 minutes to show all the users on my MapView.
Every time I get the locations of users from web service in connectionDidFinishLoading method, I am parsing, creating polyline through pointsArray and adding them to overlay:
-(void) connectionDidFinishLoading: (NSURLConnection *) connection
{
userLatitudeArray = [[NSMutableArray alloc]init];
userLongitudeArray = [[NSMutableArray alloc]init];
userIdArray = [[NSMutableArray alloc]init];
userNameArray = [[NSMutableArray alloc]init];
userProfilePicArray = [[NSMutableArray alloc]init];
profilePicURLStringArray = [[NSMutableArray alloc]init];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSArray *trackingDict = [NSJSONSerialization JSONObjectWithData:empJsonData options:kNilOptions error:nil];
if ([trackingDict count] >= 2) {
for (trackUsersCount = 0; trackUsersCount< trackingDict.count; trackUsersCount++) {
NSLog(#"trackUsersCount %i", trackUsersCount);
NSMutableArray *latlongArray = [[NSMutableArray alloc]init];
latlongArray = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"latlong"];
[userLongitudeArray removeAllObjects];
[userLatitudeArray removeAllObjects];
for (int i = 0; i<latlongArray.count; i++) {
[userLatitudeArray addObject:[[latlongArray objectAtIndex:i]objectForKey:#"lat"]];
[userLongitudeArray addObject:[[latlongArray objectAtIndex:i]objectForKey:#"long"]];
}
NSString *name = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"user_firstName"];
// ProfilePIC URL
profilePicURLString = [[trackingDict objectAtIndex:trackUsersCount]objectForKey:#"user_profilePicture"];
[userNameArray addObject:name];
[profilePicURLStringArray addObject:profilePicURLString];
int i;
if (userLatitudeArray.count>1) {
for (i = 0; i<userLatitudeArray.count; i++) {
CLLocationCoordinate2D userLocation;
userLocation.latitude = [[userLatitudeArray objectAtIndex:i]doubleValue];
userLocation.longitude = [[userLongitudeArray objectAtIndex:i] doubleValue];
MKMapPoint * pointsArray = malloc(sizeof(CLLocationCoordinate2D)*userLongitudeArray.count);
pointsArray[i] = MKMapPointForCoordinate(userLocation);
polyline = [MKPolyline polylineWithPoints:pointsArray count:i];
free(pointsArray);
}
polyline.title = name;
[mapView addOverlay:polyline];
}
}
}
}
What I want to do is to have control on each polyline created for each user, so I can change the color of it and hide/show them on click of a button (one to show/hide my track and the other for all other users), this is why I am adding title to it.
I can see now that I am adding polyline to the same overlay, which is wrong I believe. But I don't know how many users will be there in web-service so can add multiple overlays of them.
Initially I thought I am able to remove a particular polyline with title but then I realised it is removing all as polyline.title property gets updated.
Any help would be much appreciated!

You could collect an array of those tracks that relate to other users, and keep a single track for the current user. If you clean the array at the start of the connectionDidFinishLoading function and populate it where you are currently adding the overlays to the map, then you move the addOverlay to a new function that you call at the end.
- (void) resetMap
{
if (showOtherTracks)
{
[mapView addOverlays:otherUserTracks];
} else {
[mapView removeOverlays:otherUserTracks];
}
if (showMyTrack)
{
[mapView addOverlay:myTrack];
} else {
[mapView removeOverlay:myTrack];
}
}
You can also call this when ever the button is pressed and the state changes.

Related

Google Maps markers not removing iOS

I'm running a thread to fetch drivers location every 10 seconds and want to remove the added markers from the map but it doesn't work..
My code:
-(void)APiResponse:(id)returnJson
{
[googleMapsDriverPin setMap:nil];
googleMapsDriverPin = nil;
NSMutableArray *driverPins = [[NSMutableArray alloc]init];
for (int x = 0; x < [[returnJson valueForKey:#"drivers"] count]; x++) {
CLLocation *driverLocations = [[CLLocation alloc]initWithLatitude:[[[[returnJson valueForKey:#"drivers"] objectAtIndex:x] valueForKey:#"driver_latitude"] doubleValue] longitude:[[[[detail valueForKey:#"drivers"] objectAtIndex:x] valueForKey:#"driver_longitude"] doubleValue]];
[driverPins addObject:driverLocations];
}
for (CLLocation *newLocation in driverPins) {
googleMapsDriverPin = [[GMSMarker alloc] init];
[googleMapsDriverPin setPosition:newLocation.coordinate];
[googleMapsDriverPin setAnimated:YES];
[googleMapsDriverPin setTitle:#"title"];
[googleMapsDriverPin setSnippet:#"snippet"];
[googleMapsDriverPin setIcon:[GMSMarker markerImageWithColor:[UIColor blackColor]]];
[googleMapsDriverPin setMap:googleMaps];
}
}
It just keeps adding and adding every 10 seconds and not removing, please help!
Thanks!
Its a kind of quick and dirty option but if you wanted to go that way GMSMarker has a userData property which you could use to tag the driver pins
- (void)apiResponse:(id)returnJson
{
for (GMSMarker *pin in self.googleMaps.markers) {
if (pin.userData == #"Driver Pin"){
pin.map = nil;
}
}
...
for (CLLocation *newLocation in driverPins) {
googleMapsDriverPin = [[GMSMarker alloc] init];
...
[googleMapsDriverPin setUserData:#"Driver Pin"];
}
}
Update:
[self.googleMapsView clear];
On the based on pin id you can also delete pin.
Here deletePinId integer is for selected pin id.
for(GMSMarker *pin in self.mapView_.markers) {
NSLog(#"pin.userData : %#",pin.userData);
int pinId1 = [[pin.userData valueForKey:#"pin_id"] integerValue];
if(deltePinId == pinId1 ){
pin.map = nil;
}
}
you currently only store ONE marker, but you want to add N markers -- so (as saxon said) you need an array to hold all the pins :)
#interface YouClass
...
#property(nonatomic, retain) NSMutableArray *googleMapsDriverPins;
#end
#implementation YourClass
...
-(void)APiResponse:(id)returnJson
{
for(GMSMarker *pin in self.googleMapsDriverPins) {
pin.map = nil;
}
self.googleMapsDriverPins = nil;
NSMutableArray *driverPins = [[NSMutableArray alloc]init];
for (int x = 0; x < [[returnJson valueForKey:#"drivers"] count]; x++) {
CLLocation *driverLocations = [[CLLocation alloc]initWithLatitude:[[[[returnJson valueForKey:#"drivers"] objectAtIndex:x] valueForKey:#"driver_latitude"] doubleValue] longitude:[[[[detail valueForKey:#"drivers"] objectAtIndex:x] valueForKey:#"driver_longitude"] doubleValue]];
[driverPins addObject:driverLocations];
}
self.googleMapsDriverPins = [NSMutableArray arrayWithCapacity:driverPins.count];
for (CLLocation *newLocation in driverPins) {
GMSMarker *googleMapsDriverPin = [[GMSMarker alloc] init];
[googleMapsDriverPin setPosition:newLocation.coordinate];
[googleMapsDriverPin setAnimated:YES];
[googleMapsDriverPin setTitle:#"title"];
[googleMapsDriverPin setSnippet:#"snippet"];
[googleMapsDriverPin setIcon:[GMSMarker markerImageWithColor:[UIColor blackColor]]];
[googleMapsDriverPin setMap:googleMaps];
[self.googleMapsDriverPins addObject:googleMapsDriverPin];
}
}
#end
It looks like you have a loop adding multiple drivers, each of which assigns to the member variable googleMapsDriverPin. Then next time it removes the googleMapsDriverPin - but that will only be the last pin you added, not all of them.
For this to work you would need to add each marker inside your loop to an array, and then remove all of them from the map on your next update.
In Swift 2:
Create an outlet for your map:
#IBOutlet weak var mapView: GMSMapView!
Create an array to store all markers
var markers = [GMSMarker]()
Create markers like this:
func funcName() {
let position = CLLocationCoordinate2DMake(lat, lon)
let marker = GMSMarker(position: position)
for pin: GMSMarker in self.markers {
if pin.userData as! String == "from" {
pin.map = nil
}
}
marker.icon = UIImage(named: "navigation-red")
marker.userData = "from"
marker.map = self.mapView
self.markers.append(marker)
}
You can set the userData property to anything you want and later on use that string to delete that marker.When the funcName function is executed, all markers with userData as "from" are removed from the map.Let me know if you have any queries.

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.

Collection was mutated while being enumerated when [mapView addOverlay:overlayPolygon];

i tired to look over SF for solution, but can't find a solution.
maybe i missed it please help.
i tried to check out a user pinpoint with polygon by looping through all the KMLs.
the app always crash # this point:
[mapView addOverlay:overlayPolygon];
// zoom the map to the polygon bounds
[mapView setVisibleMapRect:overlayPolygon.boundingMapRect animated:YES];
problem code:
//create KML in hidden Mapview
-(void)loadKML:(NSMutableArray *)kmlNameArray
{
//dispatch_group_t group = dispatch_group_create();
//remove polygon and redraw again.
[NSThread detachNewThreadSelector: #selector(spinEnd) toTarget:self withObject:nil];
[mapView removeOverlays:mapView.overlays];
[inUserRangeArray removeAllObjects];
[inUserRangeArrayObjectIndex removeAllObjects];
[scrollview removeFromSuperview];
[pageControl removeFromSuperview];
[NSThread detachNewThreadSelector: #selector(spinBegin) toTarget:self withObject:nil];
NSArray *sysPaths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );
NSString *docDirectory = [sysPaths objectAtIndex:0];
for (int e=0; e<[kmlNameArray count]; e++)
{
//NSString *kmlNameStr = [kmlNameArray objectAtIndex:e];
Frog *kmlID = [self.fs objectAtIndex:e];
self.kmlID = [NSString stringWithFormat:#"%i",kmlID.fID];
self.kmlIDObjectIndex = [NSString stringWithFormat:#"%i",e];
NSLog(#"asasas %#",kmlIDObjectIndex);
//NSLog(#"KML items %#", kmlNameStr);
//NSLog(#"KML ID %#", kmlID);
//NSLog(#"KML file Path %#",[NSString stringWithFormat:#"%#/data/%#/%#", docDirectory,self.kmlID,[kmlNameArray objectAtIndex:e]]);
SimpleKML *kml = [SimpleKML KMLWithContentsOfFile:[NSString stringWithFormat:#"%#/data/%#/%#", docDirectory,self.kmlID,[kmlNameArray objectAtIndex:e]]error:NULL];
// look for a document feature in it per the KML spec
// dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
if (kml.feature && [kml.feature isKindOfClass:[SimpleKMLDocument class]])
{// see if the document has features of its own
for (SimpleKMLFeature *feature in ((SimpleKMLContainer *)kml.feature).features)
{// otherwise, see if we have any placemark features with a polygon
if ([feature isKindOfClass:[SimpleKMLPlacemark class]] && ((SimpleKMLPlacemark *)feature).polygon)
{
SimpleKMLPolygon *polygon = (SimpleKMLPolygon *)((SimpleKMLPlacemark *)feature).polygon;
SimpleKMLLinearRing *outerRing = polygon.outerBoundary;
//points[i], i = number of coordinates
CLLocationCoordinate2D points[[outerRing.coordinates count]];
NSUInteger i = 0;
for (CLLocation *coordinate in outerRing.coordinates)
{
points[i++] = coordinate.coordinate;
}
// create a polygon annotation for it
self.overlayPolygon = [MKPolygon polygonWithCoordinates:points count:[outerRing.coordinates count]];
//crash here
[mapView addOverlay:overlayPolygon];
// zoom the map to the polygon bounds
[mapView setVisibleMapRect:overlayPolygon.boundingMapRect animated:YES];
}
}
}
}
Before you loop over the array, you can create a new array with the elements. So when the original looped array is mutated (either by you, or by its owner) the array you loop over stays intact.
NSArray *theFeatures = [NSArray arrayWithObjects:((SimpleKMLContainer *)kml.feature).features];
for (SimpleKMLFeature *feature in theFeatures) {
}
So in case of looping over the SimpleKMLContainer features directly, you create a temporary new array with those features, and loop over that array.
Because you experience a crash on addOverlay: you must somehow be looping over the entire overlay collection. I don't see that directly in your code, so I assume that the features collection is somehow tied to the map kit overlays.
Another thing you could is not use the fast enumeration version of the for. So try replacing this:
for (SimpleKMLFeature *feature in ((SimpleKMLContainer *)kml.feature).features)
{
// Your code here
}
with this:
for (NSInteger index = 0; index < [((SimpleKMLContainer *)kml.feature).features count]; index++)
{
SimpleKMLFeature *feature = [((SimpleKMLContainer *)kml.feature).features objectAtIndex:index];
// Your code here
}
Let me know if that solves your problem.
better check out the sample code called HazardMap to see how to implement.
I just resolved the same issue by defining the Array I was enumerating as a normal NSArray instead of a NSMutableArray.
That did the trick for me.

iPhone SDK: Nearby Locations Feature

I have an iphone app which displays a list of businesses and their location on a Google Map using the MKMapView. As a new feature, I am trying to add a "Nearby" feature, whereby it gets the users current location and displays several map annotations on a google map within the vicinity of say 10KM. Currently, the businesses location is stored as an address string in a mysql database, this is then retrieved and geocoded to display it on a google map.
How do I go about doing this? If there is any tutorials around, please point me to one. (I haven't had any luck with finding one).
Thanks in advance!
UPDATE:
I got it partially working - it's only displaying businesses within a 20KM radius which is great, the problem i'm having is that when they open this Nearby Businesses View, it takes quite some time for it to go through each business and check the distance between the user and each individual business. Is there any way to speed this up? Here is my code:, both these methods are called from the viewDidLoad method.
-(void)getMapData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSInteger *bizMax = [[numOfBiz objectAtIndex:0]intValue];
int x = 0;
while(x < bizMax)
{
NSURL *mapURL = [NSURL URLWithString:[NSString stringWithFormat:#"getBizMap-xml.php?biz_id=%#",[bizIDS objectAtIndex:x]]];
NSMutableArray *mapArray = [[NSMutableArray alloc] initWithContentsOfURL:mapURL];
self.mapLocation = mapArray;
self.mapStringLocation = [mapArray objectAtIndex:0];
NSLog(#"Location: %#",mapStringLocation);
[mapArray release];
CLLocationCoordinate2D trueLocation = [self getLocationFromAddressString:mapStringLocation];
AddressAnnotation *addAnnotation = [[AddressAnnotation alloc] initWithCoordinate:trueLocation];
CLLocation *bizLoc = [[CLLocation alloc] initWithLatitude:trueLocation.latitude longitude:trueLocation.longitude];
CLLocation *userLocation = [[CLLocation alloc] initWithLatitude:nearbyMapView.userLocation.coordinate.latitude longitude:nearbyMapView.userLocation.coordinate.longitude];
//double distance = [userLocation getDistanceFrom: bizLoc] / 1000;
double kilometers = [userLocation distanceFromLocation:bizLoc] / 1000;
NSLog(#"Distance from %# to current location is %g",mapStringLocation,kilometers);
if(kilometers < maxDistance)
{
[self.nearbyMapView addAnnotation:addAnnotation];
[addAnnotation release];
}
x++;
}
[pool release];
}
-(void)getNumOfBusinesses
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSString *numOfBizJSON = [[NSString alloc]initWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"numOfBiz.php"]]];
NSString *bizIDSJSON = [[NSString alloc]initWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"allBizIDSJSON.php"]]];
SBJsonParser *parser = [[SBJsonParser alloc]init];
numOfBiz = [[parser objectWithString:numOfBizJSON error:nil]copy];
bizIDS = [[parser objectWithString:bizIDSJSON error:nil]copy];
NSLog(#"biz id 1 = %#",[bizIDS objectAtIndex:0]);
[parser release];
[numOfBizJSON release];
[bizIDSJSON release];
[pool release];
}
This isn't that hard. You can fetch all your data, calculate the distance between you and the location. The next step would be comparing the distance with the maxDistance(the search radius). If the distance < maxDistance, add you can display that location.
I think this could be achieved with very little cocoa knowledge...
If you need something more specific, start coding and I'll help you with that.

How can i show Multiple pins on the map?

i want to show multiple pins on my MapView all with Animation of Dropping pin so how it is possible if any body have sample code then please send send link.i am new in this field.Thanks in Advance.
There are few code samples on developer.apple.com
This
is a simple map example with two pins
Just as you show single pin.. keep the code for single pin in Loop and pass different longitude latitude in loop.. You will get the pins at different location
if([points retainCount] > 0)
{
[points release];
points = nil;
}
if([annotationAry retainCount] > 0)
{
[annotationAry release];
annotationAry = nil;
}
points = [[NSMutableArray alloc]init];
annotationAry = [[NSMutableArray alloc]init];
for(int i=0;i<[longitudeary count];i++)
{
CLLocation* currentLocation1 = [[CLLocation alloc] initWithLatitude:[[latitudeary objectAtIndex:i]doubleValue] longitude:[[longitudeary objectAtIndex:i]doubleValue]];
[points addObject:currentLocation1];
}
for(int i=0;i<[points count];i++)
{
// CREATE THE ANNOTATIONS AND ADD THEM TO THE MAP
CSMapAnnotation* annotation = nil;
// create the start annotation and add it to the array
annotation = [[[CSMapAnnotation alloc] initWithCoordinate:[[points objectAtIndex:i] coordinate]
annotationType:CSMapAnnotationTypeImage
title:#"123456..."
shID:[shIDary objectAtIndex:i]
catID:[catIDary objectAtIndex:i]
ciggUse:[ciggaretteUSEary objectAtIndex:i]
wifiUse:[wifiUSEary objectAtIndex:i]
controller:self]autorelease];
[annotationAry addObject:annotation];
}
[mapViewmy addAnnotations:[NSArray arrayWithArray:annotationAry]];