Polyligne en Gradient avec MapKit ios

j'essaie de tracer une route sur un MKMapView en utilisant des overlays (MKOverlay). Cependant, en fonction de la vitesse actuelle, je veux faire quelque chose comme l'application Nike avec une pente tout en traçant la route, si la couleur change (par exemple, du vert à l'orange si un utilisateur conduit de 65mph à 30mph).

<!-Voici une capture d'écran de ce que je veux:

gradient overlay image

donc tous les 20 mètres, j'ajoute une superposition de l'ancienne aux nouvelles coordonnées en utilisant:

// Create a c array of points. 
MKMapPoint *pointsArray = malloc(sizeof(CLLocationCoordinate2D) * 2);

// Create 2 points.
MKMapPoint startPoint = MKMapPointForCoordinate(CLLocationCoordinate2DMake(oldLatitude, oldLongitude));
MKMapPoint endPoint = MKMapPointForCoordinate(CLLocationCoordinate2DMake(newLatitude, newLongitude));

// Fill the array.
pointsArray[0] = startPoint;
pointsArray[1] = endPoint;

// Erase polyline and polyline view if not nil.
if (self.routeLine != nil)
    self.routeLine = nil;

if (self.routeLineView != nil)
    self.routeLineView = nil;

// Create the polyline based on the array of points.
self.routeLine = [MKPolyline polylineWithPoints:pointsArray count:2];

// Add overlay to map.
[self.mapView addOverlay:self.routeLine];

// clear the memory allocated earlier for the points.
free(pointsArray);

// Save old coordinates.
oldLatitude = newLatitude;
oldLongitude = newLongitude;

en gros, j'ajoute beaucoup de petites superpositions. Alors, j'aimerais créer le dégradé sur ce petit dessin de la ligne, donc j'essaye de le faire dans la superposition délégué:

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
    MKOverlayView* overlayView = nil;

    if(overlay == self.routeLine) {
        // If we have not yet created an overlay view for this overlay, create it now. 
        if(self.routeLineView == nil) {
            self.routeLineView = [[[MKPolylineView alloc] initWithPolyline:self.routeLine] autorelease];

            if (speedMPH < 25.0) {
                self.routeLineView.fillColor = [UIColor redColor];
                self.routeLineView.strokeColor = [UIColor redColor];
            }
            else if (speedMPH >= 25.0 && speedMPH < 50.0) {
                self.routeLineView.fillColor = [UIColor orangeColor];
                self.routeLineView.strokeColor = [UIColor orangeColor];
            }
            else {
                self.routeLineView.fillColor = [UIColor greenColor];
                self.routeLineView.strokeColor = [UIColor greenColor];
            }

            // Size of the trace.
            self.routeLineView.lineWidth = routeLineWidth;

            // Add gradient if color changed.
            if (oldColor != self.routeLineView.fillColor) {
                CAGradientLayer *gradient = [CAGradientLayer layer];
                    gradient.frame = self.routeLineView.bounds;
                    gradient.colors = [NSArray arrayWithObjects:(id)[oldColor CGColor], (id)[self.routeLineView.fillColor CGColor], nil];
                    [self.routeLineView.layer insertSublayer:gradient atIndex:0];
            }

            // Record old color for gradient.
            if (speedMPH < 25.0)
                oldColor = [UIColor redColor];
            else if (speedMPH >= 25.0 && speedMPH < 50.0)
                oldColor = [UIColor orangeColor];
            else
                oldColor = [UIColor greenColor];
        }

        overlayView = self.routeLineView;
    }

    return overlayView;
}

j'essaie d'ajouter le gradient de cette façon, mais je suppose que ce n'est pas la façon de le faire parce que je ne peux pas le faire fonctionner.

je peux aussi tracer la route chaque fois qu'il y a une mise à jour sur l'emplacement de l'utilisateur (dans le délégué de l'objet de localisation), ou comme ci-dessus tous les 20 mètre.

Pouvez-vous m'aider, me donner des conseils! Merci!

20
demandé sur wdanxna 2011-04-16 01:48:52

2 réponses

une de mes idées est de créer un CGPath et de le caresser avec gradient à chaque fois que drawMapRect la méthode a été appelée, depuis le MKPolylineView est remplacé par MKPlolylineRenderer dans ios7.

j'ai essayé d'implémenter ceci en sous-classant un MKOverlayPathRenderer mais je n'ai pas réussi à choisir un CGPath individuel, alors je trouve une méthode mystérieuse nommée-(void) strokePath:(CGPathRef)path inContext:(CGContextRef)context qui ressemble à ce dont j'ai besoin, mais il ne sera pas appelé si vous n'appelez pas la méthode super quand vous remplacez votre drawMapRect.

c'est ce que je travaille pour l'instant.

je vais continuer à essayer donc si je trouve quelque chose je reviendrai et mettre à jour la réponse.

========= UPDATE================================================

enter image description here

donc c'est ce que j'ai travaillé ces jours-ci, j'ai presque implémenté l'idée de base mentionnée ci-dessus mais oui, je ne peux toujours pas choisir un chemin individuel selon mapRect spécifique, donc je dessine juste tous les chemins avec gradient en même temps lorsque la boîte de limite de tous les chemins se croise avec mapRect courant. pauvre tour, mais travaille pour l'instant.

Dans le -(void) drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context méthode de calcul de la classe, je fais ceci:

CGMutablePathRef fullPath = CGPathCreateMutable();
BOOL pathIsEmpty = YES;

//merging all the points as entire path
for (int i=0;i< polyline.pointCount;i++){
    CGPoint point = [self pointForMapPoint:polyline.points[i]];
    if (pathIsEmpty){
        CGPathMoveToPoint(fullPath, nil, point.x, point.y);
        pathIsEmpty = NO;
    } else {
        CGPathAddLineToPoint(fullPath, nil, point.x, point.y);
    }
}

//get bounding box out of entire path.
CGRect pointsRect = CGPathGetBoundingBox(fullPath);
CGRect mapRectCG = [self rectForMapRect:mapRect];
//stop any drawing logic, cuz there is no path in current rect.
if (!CGRectIntersectsRect(pointsRect, mapRectCG))return;

puis je divise le chemin entier point par point pour dessiner son gradient individuellement. notez que l' hues tableau contenant des valeurs de teinte cartographiant chaque vitesse de localisation.

for (int i=0;i< polyline.pointCount;i++){
    CGMutablePathRef path = CGPathCreateMutable();
    CGPoint point = [self pointForMapPoint:polyline.points[i]];
    ccolor = [UIColor colorWithHue:hues[i] saturation:1.0f brightness:1.0f alpha:1.0f];
    if (i==0){
        CGPathMoveToPoint(path, nil, point.x, point.y);
    } else {
        CGPoint prevPoint = [self pointForMapPoint:polyline.points[i-1]];
        CGPathMoveToPoint(path, nil, prevPoint.x, prevPoint.y);
        CGPathAddLineToPoint(path, nil, point.x, point.y);
        CGFloat pc_r,pc_g,pc_b,pc_a,
        cc_r,cc_g,cc_b,cc_a;
        [pcolor getRed:&pc_r green:&pc_g blue:&pc_b alpha:&pc_a];
        [ccolor getRed:&cc_r green:&cc_g blue:&cc_b alpha:&cc_a];
        CGFloat gradientColors[8] = {pc_r,pc_g,pc_b,pc_a,
                                    cc_r,cc_g,cc_b,cc_a};

        CGFloat gradientLocation[2] = {0,1};
        CGContextSaveGState(context);
        CGFloat lineWidth = CGContextConvertSizeToUserSpace(context, (CGSize){self.lineWidth,self.lineWidth}).width;
        CGPathRef pathToFill = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, self.lineCap, self.lineJoin, self.miterLimit);
        CGContextAddPath(context, pathToFill);
        CGContextClip(context);//<--clip your context after you SAVE it, important!
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradientColors, gradientLocation, 2);
        CGColorSpaceRelease(colorSpace);
        CGPoint gradientStart = prevPoint;
        CGPoint gradientEnd = point;
        CGContextDrawLinearGradient(context, gradient, gradientStart, gradientEnd, kCGGradientDrawsAfterEndLocation);
        CGGradientRelease(gradient);
        CGContextRestoreGState(context);//<--Don't forget to restore your context.
    }
    pcolor = [UIColor colorWithCGColor:ccolor.CGColor];
}

C'est toute la méthode de dessin de base et bien sûr vous avez besoin points,velocity dans votre classe de superposition et les nourrir avec CLLocationManager.

le dernier point est de savoir comment obtenir hue valeur hors de la vitesse, Eh bien, j'ai trouvé que si la teinte allant de 0.03~0.3 est exactement représentée du rouge au vert, alors je fais une cartographie proportionnelle à la teinte et à la vitesse.

la dernière de la dernière, vous êtes ici c'est la totalité du code source de cette démo:https://github.com/wdanxna/GradientPolyline

ne paniquez pas si vous ne voyez pas la ligne que vous tracez, je juste la position de la zone de la carte sur ma position :)

14
répondu wdanxna 2013-11-30 02:09:45

sonne pour moi que votre drawRect dans la vue de dessin de ligne manque le réglage du gradient. le dessin peut se produire sur un fil différent pour les couches. merci de poster le code.

2
répondu Cocoanetics 2011-06-21 10:35:18