I am using the Route-Me library for the iPhone. My problem is that i want to draw a path on the map for example.
I am in Dallas and i want to go new york then just i will put marker on these two places and path will be drawn between this two marker.
Can any body suggest me how this can be done.
If any other Map rather than RouteMe then also it's ok.
Following code will draw path btween 2 points. (in your case you have to add all route points)
// Set map view center coordinate
CLLocationCoordinate2D center;
center.latitude = 47.582;
center.longitude = -122.333;
slideLocation = center;
[mapView.contents moveToLatLong:center];
[mapView.contents setZoom:17.0f];
// Add 2 markers(start/end) and RMPath with 2 points
RMMarker *newMarker;
UIImage *startImage = [UIImage imageNamed:#"marker-blue.png"];
UIImage *finishImage = [UIImage imageNamed:#"marker-red.png"];
UIColor* routeColor = [[UIColor alloc] initWithRed:(27.0 /255) green:(88.0 /255) blue:(156.0 /255) alpha:0.75];
RMPath* routePath = [[RMPath alloc] initWithContents:mapView.contents];
[routePath setLineColor:routeColor];
[routePath setFillColor:routeColor];
[routePath setLineWidth:10.0f];
[routePath setDrawingMode:kCGPathStroke];
CLLocationCoordinate2D newLocation;
newLocation.latitude = 47.580;
newLocation.longitude = -122.333;
[routePath addLineToLatLong:newLocation];
newLocation.latitude = 47.599;
newLocation.longitude = -122.333;
[routePath addLineToLatLong:newLocation];
[[mapView.contents overlay] addSublayer:routePath];
newLocation.latitude = 47.580;
newLocation.longitude = -122.333;
newMarker = [[RMMarker alloc] initWithUIImage:startImage anchorPoint:CGPointMake(0.5, 1.0)];
[mapView.contents.markerManager addMarker:newMarker AtLatLong:newLocation];
[newMarker release];
newMarker = nil;
newLocation.latitude = 47.599;
newLocation.longitude = -122.333;
newMarker = [[RMMarker alloc] initWithUIImage:finishImage anchorPoint:CGPointMake(0.5, 1.0)];
[mapView.contents.markerManager addMarker:newMarker AtLatLong:newLocation];
[newMarker release];
newMarker = nil;
Route-me has an RMPath class for this purpose. Have you played with it? If so, what did you do, what did and what didn't work?
Route-Me how to draw a path on the map for example, not exactly but close.
Use RMPath to Draw a polygon on overlay layer
//polygonArray is a NSMutableArray of CLLocation
- (RMPath*)addLayerForPolygon:(NSMutableArray*)polygonArray toMap:(RMMapView*)map {
RMPath* polygonPath = [[[RMPath alloc] initForMap:map] autorelease];
[polygonPath setLineColor:[UIColor colorWithRed:1 green:0 blue:0 alpha:0.5]];
[polygonPath setFillColor:[UIColor colorWithRed:1 green:0 blue:0 alpha:0.5]];
[polygonPath setLineWidth:1];
BOOL firstPoint = YES;
for (CLLocation* loc in polygonArray) {
if (firstPoint) {
[polygonPath moveToLatLong:loc.coordinate];
firstPoint = NO;
} else {
[polygonPath addLineToLatLong:loc.coordinate];
}
}
[polygonPath closePath];
polygonPath.zPosition = -2.0f;
NSMutableArray *sublayers = [[[[mapView contents] overlay] sublayers] mutableCopy];
[sublayers insertObject:polygonPath atIndex:0];
[[[mapView contents] overlay] setSublayers:sublayers];
return polygonPath;
}
Note:
The latest RouteMe has addLineToCoordinate:(CLLocationCoordinate2D)coordinate instead of addLineToLatLong.
This is newer one found in the MapTestBed in example in Route-me
- (RMMapLayer *)mapView:(RMMapView *)aMapView layerForAnnotation:(RMAnnotation *)annotation
{
if ([annotation.annotationType isEqualToString:#"path"]) {
RMPath *testPath = [[[RMPath alloc] initWithView:aMapView] autorelease];
[testPath setLineColor:[annotation.userInfo objectForKey:#"lineColor"]];
[testPath setFillColor:[annotation.userInfo objectForKey:#"fillColor"]];
[testPath setLineWidth:[[annotation.userInfo objectForKey:#"lineWidth"] floatValue]];
CGPathDrawingMode drawingMode = kCGPathStroke;
if ([annotation.userInfo containsObject:#"pathDrawingMode"])
drawingMode = [[annotation.userInfo objectForKey:#"pathDrawingMode"] intValue];
[testPath setDrawingMode:drawingMode];
if ([[annotation.userInfo objectForKey:#"closePath"] boolValue])
[testPath closePath];
for (CLLocation *location in [annotation.userInfo objectForKey:#"linePoints"])
{
[testPath addLineToCoordinate:location.coordinate];
}
return testPath;
}
if ([annotation.annotationType isEqualToString:#"marker"]) {
return [[[RMMarker alloc] initWithUIImage:annotation.annotationIcon anchorPoint:annotation.anchorPoint] autorelease];
}
return nil;
}
Sorry, I've not seen anything like this - I would only note that if people are voting you down because they think you are talking about the new SDK mapping features, to read your question again where you are talking about a third party library (still possibly a valid need since you can't do turn-by-turn directions with the official map SDK).
Related
I use gpuimage to build a photography app. But when I select the front camera, the camera picture appears on the back is reversed (left, right)
Code here:
stillCamera = [[GPUImageStillCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
stillCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
filter = [[GPUImageRGBFilter alloc] init];
[stillCamera addTarget:filter];
GPUImageView *filterView = (GPUImageView *)self.view;
[filter addTarget:filterView];
[stillCamera startCameraCapture];
Who can tell me what I'm having problems?
Thank very much!
try this...
stillCamera = [[GPUImageStillCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
stillCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
stillCamera.horizontallyMirrorFrontFacingCamera = NO;
stillCamera.horizontallyMirrorRearFacingCamera = NO;
filter = [[GPUImageRGBFilter alloc] init];
[stillCamera addTarget:filter];
GPUImageView *filterView = (GPUImageView *)self.view;
[filter addTarget:filterView];
[stillCamera startCameraCapture];
try this :
[filterView setInputRotation:kGPUImageFlipHorizonal atIndex:0];
I think that you can easily change the final image :
UIImage *finalImage = //image from the camera
UIImage * flippedImage = [UIImage imageWithCGImage:finalImage.CGImage scale:finalImage.scale orientation:UIImageOrientationLeftMirrored];
Just set:
stillCamera.horizontallyMirrorFrontFacingCamera = YES;
will fix this problem.
Just had a small question. I'm trying to make a UIButton appear programmatically, which I have to animate. But once the animation gets done, I can't click the button for some reason.
I have a CALayer "animationLayer" as a sublayer of the UIView, and within that animationLayer, I have three CAShapeLayers "pathLayers" (which I use to animate three different path ways as you can see in the diagram below). And within those pathLayers, I'm adding three buttons as their respective sublayers so that I can have an animation where the buttons move along the path as the path draws itself. Everything was working good until I tried adding a button as a sublayer of the pathLayer. Now when I click on the button, it's supposed to go to a method that logs on the console window "Button has been pressed". I've tried setting the self.view.userInteractionenabled to YES but that still gives me nothing.
Why I can't click it?
Here's some relevant parts of the code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.animationLayer = [CALayer layer];
self.animationLayer.frame = CGRectMake(20.0f, 64.0f,
CGRectGetWidth(self.view.layer.bounds) - 40.0f, CGRectGetHeight(self.view.layer.bounds) - 84.0f);
[self.view.layer addSublayer:self.animationLayer];
}
- (id)createUIButtonwithXPosition:(CGFloat)x YPosition:(CGFloat)y Width:(CGFloat)width Height:(CGFloat)height
{
UIImage *lionImage = [UIImage imageNamed:#"noun_project_347_2.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self
action:#selector(logger:)
forControlEvents:UIControlEventTouchUpInside];
button.frame = CGRectMake(x, y, width, height);
button.userInteractionEnabled = YES;
// [button setBackgroundColor:[UIColor clearColor]];
[button setBackgroundImage:lionImage forState:UIControlStateNormal];
[self.view addSubview:button];
return button;
}
- (void)setupDrawingLayer
{
if(self.pathLayer != nil){
[self.pathLayer removeFromSuperlayer];
[self.secondPathLayer removeFromSuperlayer];
[self.thirdPathLayer removeFromSuperlayer];
self.pathLayer = nil;
self.secondPathLayer = nil;
self.thirdPathLayer = nil;
...//Other code not relevant
}
CGRect pathRect = CGRectInset(self.animationLayer.bounds, 100.0f, 100.0f);
CGPoint bottomCenter = CGPointMake(CGRectGetMidX(pathRect), (self.lionBtnVerticalAlignment.constant + self.lionBtnHeight.constant + 25.0f));
CGPoint center = CGPointMake(CGRectGetMidX(pathRect), CGRectGetMidY(pathRect));
CGPoint leftCenter = CGPointMake(CGRectGetMinX(pathRect), CGRectGetMidY(pathRect));
CGPoint rightCenter = CGPointMake(CGRectGetMaxX(pathRect), CGRectGetMidY(pathRect));
UIBezierPath *path = [UIBezierPath bezierPath];
UIBezierPath *secondPath = [UIBezierPath bezierPath];
UIBezierPath *thirdPath = [UIBezierPath bezierPath];
[path moveToPoint:bottomCenter];
[path addLineToPoint:center];
[secondPath moveToPoint:bottomCenter];
[secondPath addLineToPoint:leftCenter];
[thirdPath moveToPoint:bottomCenter];
[thirdPath addLineToPoint:rightCenter];
CAShapeLayer *pathLayer = [CAShapeLayer layer];
pathLayer.frame = self.animationLayer.bounds;
pathLayer.bounds = pathRect;
pathLayer.geometryFlipped = YES;
pathLayer.path = path.CGPath;
pathLayer.strokeColor = [UIColor blackColor].CGColor;
pathLayer.fillColor = nil;
pathLayer.lineWidth = 3.0f;
pathLayer.lineDashPattern = [NSArray arrayWithObjects:
[NSNumber numberWithInt:6],
[NSNumber numberWithInt:2],
nil];
pathLayer.lineJoin = kCALineJoinBevel;
CAShapeLayer *secondPathLayer = [CAShapeLayer layer];
secondPathLayer.frame = self.animationLayer.bounds;
secondPathLayer.bounds = pathRect;
secondPathLayer.geometryFlipped = YES;
secondPathLayer.path = secondPath.CGPath;
secondPathLayer.strokeColor = [UIColor redColor].CGColor;
secondPathLayer.fillColor = nil;
secondPathLayer.lineWidth = 3.0f;
secondPathLayer.lineDashPattern = [NSArray arrayWithObjects:
[NSNumber numberWithInt:6],
[NSNumber numberWithInt:2],
nil];
secondPathLayer.lineJoin = kCALineJoinBevel;
CAShapeLayer *thirdPathLayer = [CAShapeLayer layer];
thirdPathLayer.frame = self.animationLayer.bounds;
thirdPathLayer.bounds = pathRect;
thirdPathLayer.geometryFlipped = YES;
thirdPathLayer.path = thirdPath.CGPath;
thirdPathLayer.strokeColor = [UIColor greenColor].CGColor;
thirdPathLayer.fillColor = nil;
thirdPathLayer.lineWidth = 3.0f;
thirdPathLayer.lineDashPattern = [NSArray arrayWithObjects:
[NSNumber numberWithInt:6],
[NSNumber numberWithInt:2],
nil];
thirdPathLayer.lineJoin = kCALineJoinBevel;
[self.animationLayer addSublayer:pathLayer];
[self.animationLayer addSublayer:secondPathLayer];
[self.animationLayer addSublayer:thirdPathLayer];
self.pathLayer = pathLayer;
self.secondPathLayer = secondPathLayer;
self.thirdPathLayer = thirdPathLayer;
UIImage *lionImage = [UIImage imageNamed:#"noun_project_347_2.png"];
btn1 = (UIButton *)[self createUIButtonwithXPosition:0.0f YPosition:0.0f Width:lionImage.size.width Height:lionImage.size.height];
btn1.layer.anchorPoint = CGPointZero;
btn1.layer.frame = CGRectMake(0.0f, 0.0f, lionImage.size.width, lionImage.size.height);
[self.view bringSubviewToFront:btn1];
self.view.userInteractionEnabled = YES;
....//Other code not relevant
}
- (void)startAnimation
{
[self.pathLayer removeAllAnimations];
[self.secondPathLayer removeAllAnimations];
[self.thirdPathLayer removeAllAnimations];
....//Other code not relevant
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 3.0;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[self.pathLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
[self.secondPathLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
[self.thirdPathLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
CAKeyframeAnimation *lionAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
lionAnimation.duration=3.0;
lionAnimation.path = self.pathLayer.path;
lionAnimation.calculationMode = kCAAnimationPaced;
lionAnimation.delegate = self;
[btn1.layer addAnimation:lionAnimation forKey:#"position"];
btn1.layer.position = CGPointMake(365.0, 460.0);
}
Seems to me that you might be experiencing a typical problem in which a UIView is a sub child of another UIView, but outside of its bounds. To check whether this is the case, set the property of the parentview to setClipsToBounds:YES and you will most likely see the button disappear. Check this post for additional details: My custom UIView dos not receive touches
If this is the case you have a couple of options.
Place the button in a place where it is within the bounds of the parent view!!
Subclass the parent view and add the following code to is so that its subviews may respond to touches even when they are outside the bounds of the parent view.
-(UIView *) hitTest:(CGPoint)point withEvent:(UIEvent *)event{
UIView* result = [super hitTest:point withEvent:event];
if (result)
return result;
for (UIView* sub in [self.subviews reverseObjectEnumerator]) {
CGPoint pt = [self convertPoint:point toView:sub];
result = [sub hitTest:pt withEvent:event];
if (result)
return result;
}
return nil;
}
Please try this:
[button setBackgroundImage:lionImage forState:UIControlEventTouchUpInside];
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.
I have been building an app which draw the lines of the current user moving along the map. Now, in iOS5 when I add the overlay to the mapview, it justs add it directly, in iOS6 it adds the overlay with "fade in" animation. I don't want this annoying animation. I also have a function which plays the trail upon the coordinates saved (after the user is done running or walking), when I play this, the lines flicker very fast, just because of this animation. This is really annoying and I really would appreciate any guidance or a solution to get rid of this animation.
Code for adding overlays:
MKMapPoint *pointsArray = malloc(sizeof(CLLocationCoordinate2D)*2);
pointsArray[0]= MKMapPointForCoordinate(oldLocation.coordinate);
pointsArray[1]= MKMapPointForCoordinate(newLocation.coordinate);
self.routeLine = [MKPolyline polylineWithPoints:pointsArray count:2];
free(pointsArray);
[self.mapView addOverlay:self.routeLine];
Code for displaying overlay:
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay{
MKOverlayView *overlayView = nil;
MKPolylineView *routeLineView = [[MKPolylineView alloc] initWithPolyline:(MKPolyline *)overlay];
routeLineView.fillColor = [UIColor blueColor];
routeLineView.strokeColor = [UIColor blueColor];
routeLineView.backgroundColor = [UIColor clearColor];
routeLineView.lineWidth = 10;
routeLineView.lineCap = kCGLineCapRound;
overlayView = routeLineView;
return overlayView;
}
Thanks!
EDIT:
Code for going through the path:
playTimer = [NSTimer scheduledTimerWithTimeInterval:interval
target:self
selector:#selector(playPathTimer)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:playTimer forMode:NSRunLoopCommonModes];
- (void)playPathTimer{
MKMapPoint *pointsArray = malloc(sizeof(CLLocationCoordinate2D) * 2);
double latitude1 = ((WorldPoint *)[coordsArray objectAtIndex:playCounter]).latitude.doubleValue;
double longitude1 = ((WorldPoint *)[coordsArray objectAtIndex:playCounter]).longitude.doubleValue;
double latitude2 = ((WorldPoint *)[coordsArray objectAtIndex:playCounter+nextCord]).latitude.doubleValue;
double longitude2 = ((WorldPoint *)[coordsArray objectAtIndex:playCounter+nextCord]).longitude.doubleValue;
CLLocation *location1 = [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake(latitude1, longitude1)
altitude:0
horizontalAccuracy:0
verticalAccuracy:0
timestamp:[NSDate date]];
CLLocation *location2 = [[CLLocation alloc] initWithCoordinate:CLLocationCoordinate2DMake(latitude2, longitude2)
altitude:0
horizontalAccuracy:0
verticalAccuracy:0
timestamp:[NSDate date]];
pointsArray[0] = MKMapPointForCoordinate(location1.coordinate);
pointsArray[1] = MKMapPointForCoordinate(location2.coordinate);
self.routeLine = [MKPolyline polylineWithPoints:pointsArray count:2];
free(pointsArray);
[self.mapView addOverlay:self.routeLine];
[playRouteLines addObject:self.routeLine];
// self.mapView.centerCoordinate = CLLocationCoordinate2DMake(latitude1, longitude1);
playCounter = playCounter + nextCord;
if(playCounter + nextCord >= coordsArray.count){
// [displayLink setPaused:YES];
// [displayLink invalidate];
[playTimer invalidate];
playTimer = nil;
playCounter = 0;
[self showStartAndEndFlags];
if(ee.trail.checkpoint.count > 0){
[self showCheckpoints];
}
self.playButton.enabled = YES;
MKCoordinateRegion adjustedRegion = [self.mapView regionThatFits:[self getRegionWithCorrectZooming]];
[self.mapView setRegion:adjustedRegion animated:YES];
}
}
Instead of using a MKPolylineView you can use your own subclass of MKOverlayView (superclass of MKPolylineView) and then override drawMapRect:zoomScale:inContext: where you draw the lines yourself.
I guess with this you won't have any fade in animation (I'm using it and never noticed any animation).
Try the following code for your drawMapRect:zoomScale:inContext: implementation.
You need to create the properties lineColor(CGColor) and lineWidth (CGFloat).
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context {
MKPolyline *polyline = self.overlay;
CGContextSetStrokeColorWithColor(context, self.lineColor);
CGContextSetLineWidth(context, self.lineWidth/zoomScale);
CGMutablePathRef path = CGPathCreateMutable();
CGPoint *points = malloc(sizeof(CGPoint)*polyline.pointCount);
for (int i=0; i<polyline.pointCount; i++) {
points[i] = [self pointForMapPoint:polyline.points[i]];
}
CGPathAddLines(path, NULL, points, polyline.pointCount);
CGContextAddPath(context, path);
CGContextDrawPath(context, kCGPathStroke);
CGPathRelease(path);
free(points);
}
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.