I try to draw a line on a MKMapView
with a pattern image.
The drawing is done by adding a custom MKMapOverlay
view.
I'm able to get the line drawn, but it seems that the drawing is done using only the left, topmost pixel of the pattern image, instead of the whole image.
Here is my drawing routine:
void drawPatternCellCallback(void *info, CGContextRef cgContext)
{
UIImage *patternImage = [UIImage imageNamed:@"tmpLine"];
CGContextDrawImage(cgContext, CGRectMake(0, 0, patternImage.size.width, patternImage.size.height), patternImage.CGImage);
}
- (void)drawMapRect:(MKMapRect)mapRect
zoomScale:(MKZoomScale)zoomScale
inContext:(CGContextRef)context
{
float alpha = 1;
float tileW = 6.0f;
float tileH = 4.0f;
CGFloat lineWidth = MKRoadWidthAtZoomScale(zoomScale)*2;
CGMutablePathRef path = CGPathCreateMutable();
if (path != nil)
{
//setup styles
CGContextSetRGBStrokeColor(context, 0.0f, 0.0f, 1.0f, 0.5f);
const CGPatternCallbacks kPatternCallbacks = {0, drawPatternCellCallback, NULL};
CGPatternRef strokePattern = CGPatternCreate(
NULL,
CGRectMake(0, 0, tileW, tileH),
CGAffineTransformIdentity,
tileW, // horizontal spacing
tileH,// vertical spacing
kCGPatternTilingConstantSpacing,
true,
&kPatternCallbacks);
//color sapce
CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
CGContextSetStrokeColorSpace(context, patternSpace);
//pattern
CGContextSetStrokePattern(context, strokePattern, &alpha);
//joins/ends
CGContextSetLineJoin(context, kCGLineJoinMiter);
CGContextSetLineCap(context, kCGLineCapButt);
CGContextSetLineWidth(context, lineWidth);
//OK, let's draw it
CGPoint firstCGPoint = [self pointForMapPoint:self.point1];
CGPoint lastCGPoint = [self pointForMapPoint:self.point2];
CGPathMoveToPoint(path, NULL, lastCGPoint.x, lastCGPoint.y);
CGPathAddLineToPoint(path, NULL, firstCGPoint.x, firstCGPoint.y);
CGContextAddPath(context, path);
CGContextStrokePath(context);
//house hold
CGPathRelease(path);
CGPatternRelease(strokePattern);
CGColorSpaceRelease(patternSpace);
}
}
Any idea what's wrong?
Thanx!
I ended up with a completely different strategy.
Instead of adding by own overlay I now rely on MKPolyLine
.
Using the following code I'm able to add a pseudo animated line from point A to point B on a MKMapView
.
The code adds several overlays to the MKMapViews with a slight delay, giving the impression of an animation
Not the most beautiful solution! - but it looks pretty good in action :-)
/*start the animation*/
-(void)plotRouteOnMap
{
[self.mapView removeOverlays:self.mapView.overlays];
//calculate a number locations between the two locations
self.points = [self getPointsOnRouteFrom:<FROM_LOCATION>
to:<TO_LOCATION>
onMapView:self.mapView];
[self addOverlaysFromPointsWithStartFrom:[NSNumber numberWithInt:1]];
}
/*convert a CGPoint to a CLLocation according to a mapView*/
- (CLLocation*)pointToLocation:(MKMapView *)mapView fromPoint:(CGPoint)fromPoint
{
CLLocationCoordinate2D coord = [mapView convertPoint:fromPoint toCoordinateFromView:mapView];
return [[[CLLocation alloc] initWithLatitude:coord.latitude longitude:coord.longitude] autorelease];
}
/*get a list of Location objects between from and to*/
-(NSArray*)getPointsOnRouteFrom:(CLLocation*)from to:(CLLocation*)to onMapView:(MKMapView*)mapView
{
int NUMBER_OF_PIXELS_TO_SKIP =10; //lower number will give a more smooth animation, but will result in more layers
NSMutableArray *ret = [NSMutableArray array];
CGPoint fromPoint = [mapView convertCoordinate:from.coordinate toPointToView:mapView];
CGPoint toPoint = [mapView convertCoordinate:to.coordinate toPointToView:mapView];
NSArray *allPixels = [self getAllPointsFromPoint:fromPoint toPoint:toPoint];
for (int i = 0 ; i < [allPixels count] ; i+=NUMBER_OF_PIXELS_TO_SKIP) {
NSValue *pointVal = [allPixels objectAtIndex:i];
[ret addObject:[self pointToLocation:mapView fromPoint:[pointVal CGPointValue]]];
}
[ret addObject:[self pointToLocation:mapView fromPoint:toPoint]];
return ret;
}
/*calulate alle pixels from point to toPint*/
-(NSArray*)getAllPointsFromPoint:(CGPoint)fPoint toPoint:(CGPoint)tPoint
{
/*Simplyfied implementation of Bresenham's line algoritme */
NSMutableArray *ret = [NSMutableArray array];
float deltaX = fabsf(tPoint.x - fPoint.x);
float deltaY = fabsf(tPoint.y - fPoint.y);
float x = fPoint.x;
float y = fPoint.y;
float err = deltaX-deltaY;
float sx = -0.5;
float sy = -0.5;
if(fPoint.x<tPoint.x)
sx = 0.5;
if(fPoint.y<tPoint.y)
sy = 0.5;
do {
[ret addObject:[NSValue valueWithCGPoint:CGPointMake(x, y)]];
float e = 2*err;
if(e > -deltaY)
{
err -=deltaY;
x +=sx;
}
if(e < deltaX)
{
err +=deltaX;
y+=sy;
}
} while (round(x) != round(tPoint.x) && round(y) != round(tPoint.y));
[ret addObject:[NSValue valueWithCGPoint:tPoint]];//add final point
return ret;
}
/*add a poly line overlay to mapview which start at position 0 and end in 'end' in the array points*/
-(void)addOverlaysFromPointsWithStartFrom:(NSNumber*)end
{
int intEnd = [end intValue];
//construct polyline view from start
CLLocationCoordinate2D *locations = malloc(sizeof(CLLocationCoordinate2D)*2);
CLLocation *loc1 = (CLLocation*)[points objectAtIndex:0];
CLLocation *loc2= (CLLocation*)[points objectAtIndex:intEnd];
locations[0] = loc1.coordinate;
locations[1] = loc2.coordinate;
MKPolyline *line = [MKPolyline polylineWithCoordinates:locations count:2];
[self.mapView addOverlay:line];
if((intEnd+1) < [points count])//add more overlays after delays unless this is the endpoint
{
[self performSelector:@selector(addOverlaysFromPointsWithStartFrom:) withObject:[NSNumber numberWithInt:intEnd + 1] afterDelay:0.01];
}
}