【问题标题】:draw line on MKMapView with pattern image在 MKMapView 上用图案图像画线
【发布时间】:2012-07-01 13:14:50
【问题描述】:

我尝试用图案图像在MKMapView 上画一条线。 绘图是通过添加自定义MKMapOverlay 视图来完成的。

我能够画出线条,但似乎只使用图案图像的左侧最上面的像素而不是整个图像来完成绘制。

这是我的绘图程序:

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);
    }
}

知道有什么问题吗?

谢谢!

【问题讨论】:

    标签: ios mkmapview mkoverlay


    【解决方案1】:

    我最终采用了完全不同的策略。 我现在依赖MKPolyLine,而不是通过自己的叠加层添加。

    使用以下代码,我可以在MKMapView 上添加一条从 A 点到 B 点的伪动画线。

    代码在 MKMapViews 中添加了几个叠加层,稍有延迟,给人一种动画的印象

    不是最漂亮的解决方案! - 但它看起来相当不错:-)

    /*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];
        }
    
    }
    

    【讨论】:

    • 缩放地图时会发生什么!
    • MKPolyLine 没有缩放,但每个点之间的距离增加了。您可以在此应用中看到它的实际效果:itunes.apple.com/us/app/city-distance/id561422939?mt=8
    • 应用看起来不错。但是根据上面粘贴的代码。不知何故,当您缩放地图时,points 的计数会超过 intEnd。为什么会发生,您将如何处理。 CLLocation loc2= (CLLocation)[points objectAtIndex:intEnd];导致崩溃!
    【解决方案2】:

    Swift 已接受答案的版本

    使用 MKOverlayRenderer 将图像添加为叠加层

    func addLayersOfAnimatingOverlay() {
            let sourcePoint = // enter as CLLocation
            let destinationPoint =  // enter as CLLocation
            let pointsCoordinatesArray = self.getLocationArrayFrom(startLocation: sourcePoint, endLocation: destinationPoint)
                //add overlay on above coordinates
            DispatchQueue.main.async{
                self.addDirectionOverlayInMap(locationArray: self.pointsCoordinates1, title: "1")
            }
    

    在 MKPolyline 中获取坐标

        func getLocationArrayFrom(startLocation: CLLocation, endLocation: CLLocation) -> [CLLocationCoordinate2D] {
                var coordinatesArray: [CLLocationCoordinate2D] = []
                if let points = helperClass.getPointsOnRoute(from: startLocation, to: endLocation, on: mapView) {
                    for point in points {
                        let coordinate  = point.coordinate
                        coordinatesArray.append(coordinate)
                    }
                }
                return coordinatesArray
            }
    
        //MARK: get cordinates from line
            func getPointsOnRoute(from: CLLocation?, to: CLLocation?, on mapView: MKMapView?) -> [CLLocation]? {
                let NUMBER_OF_PIXELS_TO_SKIP: Int = 120
                //lower number will give a more smooth animation, but will result in more layers
                var ret = [Any]()
    
                var fromPoint: CGPoint? = nil
                if let aCoordinate = from?.coordinate {
                    fromPoint = mapView?.convert(aCoordinate, toPointTo: mapView)
                }
                var toPoint: CGPoint? = nil
                if let aCoordinate = to?.coordinate {
                    toPoint = mapView?.convert(aCoordinate, toPointTo: mapView)
                }
                let allPixels = getAllPoints(from: fromPoint!, to: toPoint!)
                var i = 0
                while i < (allPixels?.count)! {
                    let pointVal = allPixels![i] as? NSValue
                    ret.append(point(toLocation: mapView, from: (pointVal?.cgPointValue)!)!)
                    i += NUMBER_OF_PIXELS_TO_SKIP
                }
                ret.append(point(toLocation: mapView, from: toPoint!)!)
                return ret as? [CLLocation]
            }
    
    /**convert a CGPoint to a CLLocation according to a mapView*/
        func point(toLocation mapView: MKMapView?, from fromPoint: CGPoint) -> CLLocation? {
            let coord: CLLocationCoordinate2D? = mapView?.convert(fromPoint, toCoordinateFrom: mapView)
            return CLLocation(latitude: coord?.latitude ?? 0, longitude: coord?.longitude ?? 0)
        }
    
        func getAllPoints(from fPoint: CGPoint, to tPoint: CGPoint) -> [Any]? {
            /*Simplyfied implementation of Bresenham's line algoritme */
            var ret = [AnyHashable]()
            let deltaX: Float = fabsf(Float(tPoint.x - fPoint.x))
            let deltaY: Float = fabsf(Float(tPoint.y - fPoint.y))
            var x: Float = Float(fPoint.x)
            var y: Float = Float(fPoint.y)
            var err: Float = deltaX - deltaY
            var sx: Float = -0.5
            var sy: Float = -0.5
            if fPoint.x < tPoint.x {
                sx = 0.5
            }
            if fPoint.y < tPoint.y {
                sy = 0.5
            }
            repeat {
                ret.append(NSValue(cgPoint: CGPoint(x: CGFloat(x), y: CGFloat(y))))
                let e: Float = 2 * err
                if e > -deltaY {
                    err -= deltaY
                    x += sx
                }
                if e < deltaX {
                    err += deltaX
                    y += sy
                }
            } while round(Float(x)) != round(Float(tPoint.x)) && round(Float(y)) != round(Float(tPoint.y))
            ret.append(NSValue(cgPoint: tPoint))
            //add final point
            return ret
        }
    

    这将产生以下效果(无动画)

    项目可以找到here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-09
      • 2023-03-05
      • 1970-01-01
      • 2021-04-24
      • 2011-09-30
      • 1970-01-01
      相关资源
      最近更新 更多