2011-04-15 15 views
19

Estoy intentando rastrear una ruta en un MKMapView usando superposiciones (MKOverlay). Sin embargo, dependiendo de la velocidad actual, quiero hacer algo como la aplicación Nike con un degradado mientras trazo la ruta, si el color está cambiando (por ejemplo, de verde a naranja si un usuario conduce de 65 mph a 30 mph).Gradient Polyline con MapKit ios

Aquí hay una captura de pantalla de lo que quiero:

gradient overlay image

Así cada 20 metros, que la adición de una superposición de lo viejo a las nuevas coordenadas usando:

// 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; 

Básicamente estoy agregando muchas pequeñas superposiciones. Entonces me gustaría crear el gradiente en este pequeño dibujo lineal, así que estoy tratando de hacerlo en el delegado de superposición:

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

Estoy intentando añadir el gradiente de esta manera, pero supongo que es no es la manera de hacerlo porque no puedo hacer que funcione.

También puedo rastrear la ruta cada vez que hay una actualización en la ubicación del usuario (en el delegado del objeto de ubicación), o como se ve arriba cada 20 metros.

¿Puede ayudarme en eso, dándome consejos! Gracias!

+1

¿Cómo se declaran routeLine y routeLineView? ¿En qué clase está el código de arriba? "No puedo hacer que funcione", ¿qué ocurre? – Rayfleck

+0

Simplemente no veo los degradados que estoy agregando a mi vista de superposición. 'MKPolyline * routeLine;' y 'MKPolylineView * routeLineView;', luego cada 20 metros creo uno nuevo y lo agrego como superposiciones. Y en la función de delegado MKMapView anterior, creo la vista según los criterios e intento agregarle un degradado. – Dachmt

Respuesta

2

me suena que su drawRect en la vista de dibujo de línea no tiene la configuración del degradado. el dibujo puede ocurrir en un hilo diferente para las superposiciones. por favor publique el código.

+0

Por favor, vea el código de arriba en mi publicación, lo actualicé. Tengo una función que agrega superposiciones del punto A al punto B cada 20 metros, y luego estoy tratando de agregar la capa de degradado cuando creo la vista de superposición en el delegado de vista de mapa. – Dachmt

+0

Tu lógica parece estar equivocada. No puede agregar capas de degradado a la superposición de esta manera. En su lugar, debe tener un método para construir y dibujar la ruta completa con los segmentos individuales de diferentes tonos. Hay un código de ejemplo WWDC 2010 ("Breadcrumbs") que demuestra esto. Su enfoque actual no puede funcionar porque solo está agregando segmentos de ruta como nuevas subcapas la primera vez. Pero esto hará que el árbol de capas se vuelva extremadamente grande con el tiempo. Necesita poder dibujar la ruta completa en una capa. – Cocoanetics

+0

Ok, gracias @Cocoanetics ¡Lo echaré un vistazo y lo mantendré actualizado! – Dachmt

13

Una de la idea que se me ocurrió es crear un CGPath y accidente cerebrovascular con gradiente cada vez que se le ha llamado drawMapRect método, ya que el MKPolylineView se sustituye por MKPlolylineRenderer en iOS7.

Intenté implementar esto mediante la subclasificación de un MKOverlayPathRenderer pero no pude seleccionar un CGPath individual, entonces encuentro un método misterioso llamado -(void) strokePath:(CGPathRef)path inContext:(CGContextRef)context que suena como lo que necesito, pero no se llamará si no llama al súper método cuando anula su drawMapRect.

eso es lo que estoy haciendo ejercicio por ahora.

Lo seguiré intentando así que si encuentro algo, volveré y actualizaré la respuesta.

========= ACTUALIZACIÓN ================================== ==============

enter image description here

Así que eso es lo que estoy trabajado en estos días, casi implementado la idea básica mencionada anteriormente, pero sí, todavía no puedo recoger una PATH individual de acuerdo con el mapRect específico, por lo que simplemente dibujaré todas las rutas con gradiente al mismo tiempo cuando el boundingBox de todas las rutas se cruza con el mapRect actual. pobre truco, pero trabaja por ahora.

En el método render -(void) drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context en clase, hago esto:

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; 

Entonces dividir toda la trayectoria de punto por punto a dibujar su gradiente de forma individual. tenga en cuenta que la matriz hues contiene un valor de matiz que mapea cada velocidad de ubicación.

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

Eso es todo el método de dibujo básico y, por supuesto, necesita points, velocity en su clase de superposición y las alimentan con CLLocationManager.

el último punto es cómo obtener hue valor fuera de velocidad, bueno, encontré que si el tono que varía de 0.03 ~ 0.3 representa exactamente de rojo a verde, entonces hago un mapeo proporcional de tono y velocidad.

último de la última, aquí estás esto está lleno fuente de esta demostración: https://github.com/wdanxna/GradientPolyline

no se preocupe si no puede ver la línea que se dibuja, acabo de posicionar a la región mapa en mi posición :)

+0

Acabo de ver tu respuesta. Creo que esto es exactamente lo que necesito lograr en esta pregunta: http://stackoverflow.com/questions/20247313/gradient-along-mkpolylineview-mkpolylinerenderer. Básicamente, si lo entiendo correctamente, recreas la ruta manualmente segmento por segmento y aplica un degradado solo al que está actualmente agregado al recortar cada vez. ¿Es esto lo óptimo/lo suficientemente rápido? – Templar

+0

Al principio, traté de dibujar una ruta completa con un objeto de degradado que tiene todos los valores de color que necesito en 'gradientColors' y cada valor de color asigna un valor de ubicación en mi' gradientLocation'. Pensé que debería funcionar, pero no es (raro). y el documento dice que no deberíamos sacar nada de MapRect actual, parece que dibujar una ruta completa una vez no es una opción. entonces creo que dividirlo en segmentos es la manera más fácil ...hablando de óptimo, está funcionando bien en mi iPhone :) pero todavía hay algo que mejorar, como: solo dibujar líneas que se cruzan con el MapRect actual. – wdanxna

+0

Implementé mis cosas en función de tu código y ejemplo de github y funciona de maravilla. Solo los cambios que sugeriría es preparar la ruta directamente en el init y solo usar en el 'drawRect' o simplemente usar' CGRect pointsR = [self rectForMapRect: polyline.boundingMapRect]; 'y también verificar CGRectContains, no solo para la intersección . Mi pregunta aún no ha recibido respuesta y tiene una recompensa. Podrías publicar tu respuesta allí arriba y la aceptaría felizmente, me ayudó mucho :) – Templar

Cuestiones relacionadas