Adding an arrow over the polyline in mkmapkit? - iphone

I had an application in which i am showing polylines in mkmapkit like this -
self.routeLine = [MKPolyline polylineWithCoordinates:coordinateArray count:4];
//[self.mapView setVisibleMapRect:[self.routeLine boundingMapRect]]; //If you want the route to be visible
[self.mapView addOverlay:self.routeLine];
-(MKPolylineRenderer *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
if(overlay == self.routeLine)
{
if(nil == self.routeLineView)
{
self.routeLineView = [[MKPolylineRenderer alloc] initWithPolyline:self.routeLine];
self.routeLineView.fillColor = [UIColor redColor];
self.routeLineView.strokeColor = [UIColor redColor];
self.routeLineView.lineWidth = 2;
}
return self.routeLineView;
}
return nil;
}
Now i want to add an arrow over that polylines to show the direction like this
How to draw an arrow on every polyline segment on Google Maps V3
. I googled for the same but no luck so far. Can anybody direct me on how to achieve this?

I would suggest actually drawing the arrow yourself with an MKOverlayPathRenderer, as I do in this example:

Related

how can i draw line on mkmapview

I am using OCMapView in my project . OCMapView is Map Kit cluster pinpoints.
I want to add line on mapkit. like this
But my code doing this : it's not logical add to overlay line on map .
I handle this code .
CLLocationCoordinate2D *coordinates
= malloc(sizeof(CLLocationCoordinate2D) * [self.mapView.annotations count]);
for (int i=0; i<[self.mapView.annotations count]; i++) {
OCMapViewSampleHelpAnnotation * ann=[annodationArray objectAtIndex:i];
coordinates[i]=ann.coordinate;
}
self.routeLineView=nil;
self.routeLine = [MKPolyline polylineWithCoordinates:coordinates count:self.mapView.annotations.count]; //
free(coordinates);
[self.mapView setVisibleMapRect:[self.routeLine boundingMapRect]]; //If you want the route to be visible
dispatch_async(dispatch_get_main_queue(), ^{
[self.mapView addOverlay:self.routeLine];
});
you can see my overlay delagate method
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay{
//normally here is circle.
if(overlay == self.routeLine)
{
if(nil == self.routeLineView)
{
self.routeLineView = [[MKPolylineView alloc] initWithPolyline:self.routeLine];
self.routeLineView.fillColor = [UIColor redColor];
self.routeLineView.strokeColor = [UIColor redColor];
self.routeLineView.lineWidth = 3;
}
return self.routeLineView;
}
return nil;
}
also I am sharing my project code . How can I solve this problem ?
https://www.dropbox.com/s/q0gtwihtl8o2pom/OCMapView-master.zip
Just use self.mapView.displayedAnnotations (these are only the ones visible) instead of self.mapView.annotations (these are all annotations, that were added).

Persist overlay line over map view in iOS6

I have map view over which i like to draw line using overlay method, i added two location latitude and longitude to draw line between them when i pressed button, but when i pressed second time new paire of coordinates i provide and new line over map appers but previous overlay path got vanished, but i need previou path on the same map also
here is my code .
- (IBAction)refreshMapMethod:(id)sender
{
int kk=[[NSUserDefaults standardUserDefaults]integerForKey:#"ham"];
if (kk==1)
{
CLLocationCoordinate2D coordinateArray[2];
coordinateArray[0] = CLLocationCoordinate2DMake(12.915181,+77.626055);
coordinateArray[1] = CLLocationCoordinate2DMake(12.892156, +77.582188);
self.routeLine = [MKPolyline polylineWithCoordinates:coordinateArray count:2];
[self.myMapView setVisibleMapRect:[self.routeLine boundingMapRect]];
[self.myMapView addOverlay:self.routeLine];
[[NSUserDefaults standardUserDefaults]setInteger:2 forKey:#"ham" ];
}
if (kk==2)
{
CLLocationCoordinate2D coordinateArray[2];
coordinateArray[0] = CLLocationCoordinate2DMake(12.892156,+77.426055);
coordinateArray[1] = CLLocationCoordinate2DMake(12.892156, +77.582188);
self.routeLine = [MKPolyline polylineWithCoordinates:coordinateArray count:2];
[self.myMapView setVisibleMapRect:[self.routeLine boundingMapRect]];
[self.myMapView addOverlay:self.routeLine];
[[NSUserDefaults standardUserDefaults]setInteger:3 forKey:#"ham" ];
}
if (kk==3)
{
CLLocationCoordinate2D coordinateArray[2];
coordinateArray[0] = CLLocationCoordinate2DMake(12.892156, +77.382188);
coordinateArray[1] = CLLocationCoordinate2DMake(12.892156, +77.282188);
self.routeLine = [MKPolyline polylineWithCoordinates:coordinateArray count:2];
[self.myMapView setVisibleMapRect:[self.routeLine boundingMapRect]];
[self.myMapView addOverlay:self.routeLine];
[[NSUserDefaults standardUserDefaults]setInteger:3 forKey:#"ham" ];
}
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude= 12.915181;
zoomLocation.longitude=77.626055;
MKCoordinateRegion viewRegion=MKCoordinateRegionMakeWithDistance(zoomLocation, 3*METERS_PER_MILE, 3*METERS_PER_MILE);
[self.myMapView setRegion:viewRegion animated:YES];
[[NSUserDefaults standardUserDefaults]setInteger:2 forKey:#"change" ];
}
calling overlay method ..
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
if(overlay == self.routeLine)
{
if(nil == self.routeLineView)
{
self.routeLineView = [[MKPolylineView alloc] initWithPolyline:self.routeLine];
self.routeLineView.fillColor = [UIColor redColor];
self.routeLineView.strokeColor = [UIColor redColor];
self.routeLineView.lineWidth = 5;
}
return self.routeLineView;
}
return nil;
}
please provide me the solution in tye same way or the other any alternate way .
than you .
It's because you are only allowing the viewForOverlay function to return the view for self.routeline and you only have one of those. Every other call to viewForOverlay will return nil and therefore not be drawn. What you need to do is draw all the overlays.
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay
{
MKPolylineView* routeLineView = [[MKPolylineView alloc] initWithPolyline:(MKPolyLine)overlay];
routeLineView.fillColor = [UIColor redColor];
routeLineView.strokeColor = [UIColor redColor];
routeLineView.lineWidth = 5;
return routeLineView;
}
You'll probably need to do some more stuff like checking the overlay actually is a polyline first, but this should be enough to get you going.

Trouble with overlay using MKPolyline & MKPolylineView

I have added an overlay to my MKMapView using the addOverlay: method. The overlay was made using MKPolyline and stroked using MKPolylineView. The stroke color is blue, with an alpha value of 0.7.
When the view initially loads, the overlay is drawn correctly but the surrounding areas of the overlay are blue as well...
When I pinch & zoom out, the blue area is still there but it adjusts to my new zoom level. This is somewhat hard to describe... but basically I have a small rectangle of "normal map" trapped inside a larger rectangle that is blue.
It will disappear when I zoom out to view the entire country and when I zoom back in everything is normal.
I think this may be caused by me not implementing the MKOverlayProtocol correctly?
If anyone has any ideas please throw them my way...
EDIT:
Here is the code that creates the MKPolyline and the delegate method.
-(MKPolyline *)bluePolyline
{
CLLocationCoordinate2D bluePoints[16];
bluePoints[0] = CLLocationCoordinate2DMake(27.526483, -97.882454);
bluePoints[1] = CLLocationCoordinate2DMake(27.526407, -97.887883);
bluePoints[2] = CLLocationCoordinate2DMake(27.527244, -97.887905);
bluePoints[3] = CLLocationCoordinate2DMake(27.527282, -97.887304);
bluePoints[4] = CLLocationCoordinate2DMake(27.527577, -97.887304);
bluePoints[5] = CLLocationCoordinate2DMake(27.527596, -97.885727);
bluePoints[6] = CLLocationCoordinate2DMake(27.530194, -97.88577); //Seale St. & Corrale Ave.
bluePoints[7] = CLLocationCoordinate2DMake(27.530213, -97.883892); //Retama & Corral Ave.
bluePoints[8] = CLLocationCoordinate2DMake(27.530279,-97.881907);
bluePoints[9] = CLLocationCoordinate2DMake(27.530337,-97.880201);
bluePoints[10] = CLLocationCoordinate2DMake(27.530356,-97.877959);
bluePoints[11] = CLLocationCoordinate2DMake(27.52753,-97.877884); //West C Ave. & Armstrong
bluePoints[12] = CLLocationCoordinate2DMake(27.527492,-97.878367);
bluePoints[13] = CLLocationCoordinate2DMake(27.527397,-97.878817);
bluePoints[14] = CLLocationCoordinate2DMake(27.527349,-97.882454);
bluePoints[15] = CLLocationCoordinate2DMake(27.526483, -97.882453);
if(bluePolyline == nil)
{
bluePolyline = [MKPolyline polylineWithCoordinates:bluePoints count:16];
}
bluePolyline.title = #"Blue Route";
_bluePolyline = bluePolyline;
return _bluePolyline;
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
MKPolylineView *aView = [[MKPolylineView alloc] initWithPolyline:(MKPolyline *)overlay];
//aView.fillColor = [[UIColor cyanColor] colorWithAlphaComponent:0.2];
aView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.6];
aView.lineWidth = 10;
return aView;
}
I ended up fixing my problem by implementing the MKOverlay Protocol method:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay;
like so:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
MKPolylineView *aView = [[MKPolylineView alloc] initWithPolyline:(MKPolyline *)overlay];
if(overlay.title == #"Blue Route")
{
aView.lineWidth = 7.0;
aView.strokeColor = [[UIColor blueColor] colorWithAlphaComponent:0.6];
}
if(overlay.title == #"Gold Route-A")
{
aView.lineWidth = 10.0;
aView.strokeColor = [[UIColor yellowColor] colorWithAlphaComponent:0.6];
}
if(overlay.title == #"Gold Route-B")
{
aView.lineWidth = 7.0;
aView.strokeColor = [[UIColor yellowColor] colorWithAlphaComponent:0.6];
}
return aView;
}
Where each overlay was created elsewhere in my model.

Drawing polyline on mapview does not show

I am trying to connect my pins using MKPolyline on my MKMapView.
It gives no errors, but the line does not show up on my map.
I have tried everything almost but still can't get it to work.
[NEW]
I have tried now commenting the rest out. I also moved everything into the same controller. The child only sends a NSMutableArray with CLLocations to its parent. I have also thought that it might be drawing on a different MKMapView than the one showing on the screen. But it seem not, because I draw pins and a TileOverlay on the same MKMapView with success.
I tried this with no success:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
NSLog(#"Drawing overlay");
if(overlay == self.routeLine)
{
NSLog(#"Drawing routeLine");
if(nil == self.routeLineView)
{
NSLog(#"Drawing routeLineView");
NSLog(#"Point Count: %d",self.routeLine.pointCount);
/*MKMapPoint *tmp = self.routeLine.points;
for(int i = 0; i < self.routeLine.pointCount;i++)
NSLog(#"Points: %d\t%#",i, tmp[i]);*/ // Can't find way to output points
self.routeLineView = [[[MKPolylineView alloc] initWithPolyline:self.routeLine] autorelease];
self.routeLineView.fillColor = [UIColor redColor];
self.routeLineView.strokeColor = [UIColor redColor];
self.routeLineView.lineWidth = 3;
NSLog(#"After setting line specs");
}
return self.routeLineView;
NSLog(#"Not supposed to be called");
}
/*TileOverlayView *view = [[TileOverlayView alloc] initWithOverlay:overlay];
view.tileAlpha = 0.6;
return [view autorelease];*/
return nil;
}
This outputs:
Drawing overlay
Drawing routeLine
Drawing routeLineView
Point Count: 2
After setting line specs
[Initial Question]
I will post my code in reverse of how stuff happens:
mapView:viewForOverlay:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay
{
NSLog(#"Drawing overlay");
if(overlay == self.routeLine)
{
NSLog(#"Drawing routeLine");
if(nil == self.routeLineView)
{
NSLog(#"Drawing routeLineView");
self.routeLineView = [[[MKPolylineView alloc] initWithPolyline:self.routeLine] autorelease];
self.routeLineView.fillColor = [UIColor redColor];
self.routeLineView.strokeColor = [UIColor redColor];
self.routeLineView.lineWidth = 3;
}
return self.routeLineView;
}
//Not part of the Question
TileOverlayView *view = [[TileOverlayView alloc] initWithOverlay:overlay];
view.tileAlpha = 0.6;
return [view autorelease];
}
This outputs all to the console:
Drawing overlay
Drawing routeLine
Drawing routeLineView
Adding the line
-(void)addRouteLine:(MKPolyline *)route
{
self.routeLine = route;
NSLog(#"addRouteLine"); //Successfully prints in console
[myMapView addOverlay:self.routeLine];
}
Creating the line
This happens in a child modal viewcontroller
-(void)loadRoute
{
// while we create the route points, we will also be calculating the bounding box of our route
// so we can easily zoom in on it.
MKMapPoint northEastPoint;
MKMapPoint southWestPoint;
// create a c array of points.
MKMapPoint *pointArr = malloc(sizeof(CLLocationCoordinate2D) * waypoints.count);
MKPolyline *polyLine;
for(int idx = 0; idx < waypoints.count; idx++)
{
CLLocationCoordinate2D coordinate = ((CLLocation *)[waypoints objectAtIndex:idx]).coordinate;
MKMapPoint point = MKMapPointForCoordinate(coordinate);
// if it is the first point, just use them, since we have nothing to compare to yet.
if (idx == 0)
{
northEastPoint = point;
southWestPoint = point;
}
else
{
if (point.x > northEastPoint.x)
northEastPoint.x = point.x;
if(point.y > northEastPoint.y)
northEastPoint.y = point.y;
if (point.x < southWestPoint.x)
southWestPoint.x = point.x;
if (point.y < southWestPoint.y)
southWestPoint.y = point.y;
}
NSLog(#"Routing Waypoint:\t%f\t%f\t%.02f\t%.02f", point.x, point.y, coordinate.latitude, coordinate.longitude);
pointArr[idx] = point;
}
// create the polyline based on the array of points.
polyLine = [MKPolyline polylineWithPoints:pointArr count:(waypoints.count)];
if([self.presentingViewController isKindOfClass:[FirstViewController class]])
[(FirstViewController*)self.presentingViewController addRouteLine:polyLine];
free(pointArr);
}
This outputs the data correctly. I currently have two "Routing Waypoints". My output was:
Routing Waypoint: 145449142.044444 161268376.039062 -34.07 15.06
Routing Waypoint: 142917646.563556 157564926.760068 -29.86 11.67
This clearly indicates that the two points are valid points with actual coordinates etc.
So it creates the polyline with valid coordinates. Calls the method to draw that polyline. Then it also gets to the point where it adds the polyline as a view.
Why wouldn't this show on the map?
Please help!

Showing annotations only inside the overlays

In my map, I have to show annotations from a kml file via an URL. I also need to show only the annotations inside a polygon or circle area (or both of them if the user has both overlays drawn).
I have seen the question How to determine if an annotation is inside of MKPolygonView (iOS), but I have two perplexities:
Regarding the annotation coordinates, should I use the coordinates of the annotations from the addAnnotation method?
In the mentioned question a new overlay is created, but I have two different overlays created elsewhere. So my question is: what is the most suitable place to put this code (or something like that)?
EDIT: I've created some code:
-(IBAction)showKmlData:(id)sender
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"KMLGenerator" ofType:#"kml"];
kml = [[KMLParser parseKMLAtPath:path] retain];
NSArray *annotationsImmut = [kml points];
//[mapview addAnnotations:annotations]; not anymore
NSMutableArray *annotations = [annotationsImmut mutableCopy];
[self filterAnnotations:annotations];
MKMapRect flyTo = MKMapRectNull;
for (id <MKAnnotation> annotation in annotations) {
MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0, 0);
if (MKMapRectIsNull(flyTo)) {
flyTo = pointRect;
} else {
flyTo = MKMapRectUnion(flyTo, pointRect);
}
}
mapview.visibleMapRect = flyTo;
}
-(void)filterAnnotations:(NSMutableArray *)annotationsToFilter {
for (int i=0; i<[annotationsToFilter count]; i++) {
CLLocationCoordinate2D mapCoordinate = [[annotationsToFilter objectAtIndex:i] coordinate];
MKMapPoint mapPoint = MKMapPointForCoordinate(mapCoordinate);
MKPolygonView *polygonView =
(MKPolygonView *)[mapView viewForOverlay:polygonOverlay];
MKCircleView *circleView =
(MKCircleView *)[mapView viewForOverlay:circleOverlay];
CGPoint polygonViewPoint = [polygonView pointForMapPoint:mapPoint];
CGPoint circleViewPoint = [circleView pointForMapPoint:mapPoint];
BOOL mapCoordinateIsInPolygon =
CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, NO);
BOOL mapCoordinateIsInCircle =
CGPathContainsPoint(circleView.path, NULL, circleViewPoint, NO);
if( mapCoordinateIsInPolygon || mapCoordinateIsInCircle )
[annotationsToFilter removeObjectAtIndex:i];
}
[mapView addAnnotations:annotationsToFilter];
}
EDIT Nr.2 Here is my implementation of viewForOverlay delegate method.I see the overlays, circles and polygons I create.I see all the annotations.ALL of them, those inside and outside the overlays...
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay
{
MKCircleView* circleView = [[[MKCircleView alloc] initWithOverlay:overlay] autorelease];
circleOverlay = circleView;
circleView.fillColor = [UIColor blueColor];
circleView.strokeColor = [UIColor blueColor];
circleView.lineWidth = 5.0;
circleView.alpha = 0.20;
MKPolygonView *polygonView = [[[MKPolygonView alloc] initWithOverlay:overlay] autorelease];
polygonOverlay = polygonView;
polygonView.fillColor = [UIColor blueColor];
polygonView.strokeColor = [UIColor blueColor];
polygonView.lineWidth = 5.0;
polygonView.alpha = 0.20;
if ([overlay isKindOfClass:[MKCircle class]])
{
return circleView;
}
else
return polygonView;
}
Overall, that looks fine but there is a problem in the filterAnnotations method with how the annotations are being removed from the annotationsToFilter array.
What will happen is that some annotations will be skipped and never go through the check.
For example:
Say there are 5 annotations (0=A, 1=B, 2=C, 3=D, 4=E)
The for-loop starts at index 0 and "A" meets the condition for removal
"A" is removed from the array and now the other annotations are moved down one index so after the removal the array is: (0=B, 1=C, 2=D, 3=E)
Now the for-loop goes to the next index which is 1 (so annotation C is checked)
So annotation B is skipped and is never checked
One way to fix this is to collect the annotations you want to keep in another array "annotationsToAdd" instead of removing them from the original and pass annotationsToAdd to the addAnnotations method.
Below is the suggested modification. I'd also recommend moving the viewForOverlay calls outside the for-loop since those references won't change during the loop so there's no need to call them repeatedly.
-(void)filterAnnotations:(NSMutableArray *)annotationsToFilter
{
NSMutableArray *annotationsToAdd = [NSMutableArray array];
//Get a reference to the overlay views OUTSIDE the for-loop since
//they will remain constant so there's no need to keep calling
//viewForOverlay repeatedly...
MKPolygonView *polygonView = (MKPolygonView *)[mapView viewForOverlay:polygonOverlay];
MKCircleView *circleView = (MKCircleView *)[mapView viewForOverlay:circleOverlay];
for (int i=0; i < [annotationsToFilter count]; i++)
{
//get a handy reference to the annotation at the current index...
id<MKAnnotation> currentAnnotation = [annotationsToFilter objectAtIndex:i];
CLLocationCoordinate2D mapCoordinate = [currentAnnotation coordinate];
MKMapPoint mapPoint = MKMapPointForCoordinate(mapCoordinate);
CGPoint polygonViewPoint = [polygonView pointForMapPoint:mapPoint];
CGPoint circleViewPoint = [circleView pointForMapPoint:mapPoint];
BOOL mapCoordinateIsInPolygon = CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, NO);
BOOL mapCoordinateIsInCircle = CGPathContainsPoint(circleView.path, NULL, circleViewPoint, NO);
if ( !mapCoordinateIsInPolygon && !mapCoordinateIsInCircle )
//Note the reversed if-condition because now
//we are finding annotations we want to KEEP
{
[annotationsToAdd addObject:currentAnnotation];
}
}
[mapView addAnnotations:annotationsToAdd];
}
Also, I noticed that in the showKmlData method you are using the variable mapview but in filterAnnotations it's mapView (uppercase V). Hopefully, the compiler will give you a warning about that.
Additional Info:
Based on your comments and the viewForOverlay code you added to your question...
First, the compiler warning class 'MKPolygonView' does not implement the 'MKOverlay' protocol that you are getting implies that the variables polygonOverlay and circleOverlay are declared as MKPolygonView and MKCircleView instead of MKPolygon and MKCircle.
Second, the code in the viewForOverlay delegate method is wrong. It tries to create both a circle and polygon view for whatever overlay is passed in and then checks what kind of class the overlay is. It also seems to be saving a reference to the overlay view but the rest of the code assumes that we are keeping a reference to the overlay (the MKOverlay object--not the MKOverlayView).
Try the following changes...
//polygonOverlay and circleOverlay should be declared as MKOverlay objects...
#property (nonatomic, retain) MKPolygon *polygonOverlay;
#property (nonatomic, retain) MKCircle *circleOverlay;
//save a reference to them when you call addOverlay...
self.polygonOverlay = [MKPolygon polygonWithCoordinates:polyCoords count:coordsCount];
[mapView addOverlay:polygonOverlay];
self.circleOverlay = [MKCircle circleWithCenterCoordinate:cirleCenter radius:circleRadius];
[mapView addOverlay:circleOverlay];
//the viewForOverlay delegate method...
-(MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id)overlay
{
if ([overlay isKindOfClass:[MKCircle class]])
{
MKCircleView* circleView = [[[MKCircleView alloc] initWithOverlay:overlay] autorelease];
circleView.fillColor = [UIColor blueColor];
circleView.strokeColor = [UIColor blueColor];
circleView.lineWidth = 5.0;
circleView.alpha = 0.20;
return circleView;
}
else
if ([overlay isKindOfClass:[MKPolygon class]])
{
MKPolygonView *polygonView = [[[MKPolygonView alloc] initWithOverlay:overlay] autorelease];
polygonView.fillColor = [UIColor blueColor];
polygonView.strokeColor = [UIColor blueColor];
polygonView.lineWidth = 5.0;
polygonView.alpha = 0.20;
return polygonView;
}
return nil;
}
You also mention in your edit that "I see the overlays, circles and polygons". That sounds like you are creating more than one circle and/or polygon overlay. In that case, having just one polygonOverlay and circleOverlay variable won't work.
If you really do have multiple overlays of each type, then don't store references to the overlays. Instead, in the filterAnnotations method, for each annotation, loop through the mapView.overlays array and execute the viewForOverlay and point-in-polygon test in the nested loop.