3

I'm having trouble with drawing a line on the map with a stroke. (inside color and outside color) I beleive i'm on the right path and have subclassed mkOverlayView to override the drawing (needs to fill with the road size) so inside drawMapRect...

CGFloat lineWidth = MKRoadWidthAtZoomScale(zoomScale);

MKMapRect clipRect = MKMapRectInset(mapRect, -lineWidth, -lineWidth);

  ...

CGContextAddPath(context, path);
CGContextSetStrokeColorWithColor(context, line.color.CGColor);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, lineWidth);
CGContextSetAlpha(context, 0.4f);
CGContextStrokePath(context);

CGPathRelease(path);

I'm not sure how to add the stroke. Any help would be greatly appreciated. xcode 4.6 / ios 6.0+

enter image description here

Rob
  • 415,655
  • 72
  • 787
  • 1,044
glued
  • 2,579
  • 1
  • 25
  • 40
  • Seems like duplicate of question http://stackoverflow.com/q/8960249/650350. See the answer: http://stackoverflow.com/a/9193196/650350. You probably want `CGContextReplacePathWithStrokedPath()` – Mathew Feb 01 '13 at 00:10
  • @Mathew Thanks for that recommendation, I tried it and got this... http://imgur.com/t8IuKSM which created a new problem, since the path is made up of many lines how do i combine them into one? – glued Feb 01 '13 at 03:15

2 Answers2

2

stroke first a path ( road with) with color 1, then change stroke width and color to a thinner line with road with * 0.6 in color 2, and stroke again.

AlexWien
  • 28,470
  • 6
  • 53
  • 83
  • I dont think that would work, the line is transparent and the inside line needs to be lighter than the outside one. – glued Jan 31 '13 at 22:57
  • my app does this. this works. details is up to you to set the correct trancparency level. make the lines opaque. – AlexWien Feb 01 '13 at 00:39
  • yeah, you are right. exactly as shown in the graphic the inner line is transparent, the outer is a bit of doubt whether or not it is transparent. i think exatly as shown it is difficult. You would have to draw three lines: inner trans, outer left, and right – AlexWien Feb 01 '13 at 00:44
  • You should also look at drawing speed, when zooming out more points are visible..... – AlexWien Feb 01 '13 at 21:31
2

Ok, I managed to figure it out based on Mathew's suggestion but with a few tweaks...

enter image description here

Essentially I created a stroked path using CGPathCreateCopyByStrokingPath and made the stroke slightly darker than the base color. Then created a transparentLayer with the path and stroke, used the blend mode kCGBlendModeDestinationAtop and drew another path with only a fill this time on top. I'm not sure if this is the best approach but appears to work well.

CGPathRef newpath =  CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, kCGLineCapRound, kCGLineJoinMiter, 0.0);
CGContextAddPath(context, newpath);

CGContextSetStrokeColorWithColor(context, line.color.CGColor);
CGContextSetFillColorWithColor(context,  line.color.CGColor);
CGContextSetLineWidth(context, lineWidth * 0.3);
CGContextSetAlpha(context, 0.8f);
CGContextBeginTransparencyLayer (context, NULL);
CGContextStrokePath(context);
CGContextBeginPath(context);
CGContextAddPath(context, newpath);
CGContextFillPath(context);
CGContextEndTransparencyLayer(context);
CGContextSetBlendMode(context, kCGBlendModeDestinationAtop);
CGContextSetAlpha(context, 0.3f);
CGContextBeginPath(context);
CGContextAddPath(context, newpath);
CGContextFillPath(context);

CGPathRelease(path);
CGPathRelease(newpath);

EDIT Here is a simpler solution based on AlexWien's approach enter image description here

    if (path != nil)
    {

        CGPathRef  path2 = CGPathCreateCopy(path);
        CGFloat hue;
        CGFloat saturation;
        CGFloat brightness;
        CGFloat alpha;

        [line.color getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha];

        UIColor *c2 =[UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:0.4];
        CGContextAddPath(context, path);

        CGContextSetStrokeColorWithColor(context, c2.CGColor);
        CGContextSetLineJoin(context, kCGLineJoinRound);
        CGContextSetLineCap(context, kCGLineCapRound);
        CGContextSetLineWidth(context, lineWidth);
        CGContextStrokePath(context);
        CGPathRelease(path);

             CGContextSetBlendMode(context, kCGBlendModeSourceAtop);
            CGContextAddPath(context, path2);
              CGContextSetRGBStrokeColor(context, 1.0f, 1.0f, 1.0f, 0.3f);
            CGContextSetLineJoin(context, kCGLineJoinRound);
            CGContextSetLineCap(context, kCGLineCapRound);
            CGContextSetLineWidth(context, lineWidth/2.0f);
            CGContextStrokePath(context);
            CGPathRelease(path2);

    }
glued
  • 2,579
  • 1
  • 25
  • 40
  • I am not entirely sure what is going on because I don't have any similar code running locally, but I wonder if using the **lighten** blend mode would help here if you did not want the end caps to appear for each line segment. The documentation says "Specifies to create the composite image samples by choosing the lighter samples (either from the foreground or the background)." So I wonder (I'm really not sure) if that would compare the darker color of the stroke on the end cap with the lighter color of the fill for another segment and use the fill color, hiding end caps in the process. – Mathew Feb 01 '13 at 18:06
  • The other possibility I am wondering about (again, if you want to experiment) is using `CGContextClip()` to turn the series of subpaths into a clipping path and use that clipping path to only paint the fill color within the bounds of the paths. – Mathew Feb 01 '13 at 18:10
  • i like this post, really nice graphics. – AlexWien Feb 01 '13 at 21:29