IOS native map (MapKit, MKMapView) track gradient

IOS native map (MapKit, MKMapView) track gradient

project has access to high moral map, and most of the features have been achieved, but BOSS think iOS comes with a better map… In the face of the boss programming ideas. Fortunately, the high moral map is encapsulated in the MapKit, most API as long as the prefix MA-> MK can, but there is a problem, is the track gradient, there is no corresponding Mapkit method, High German is not open source, but also the domestic basic website to search solutions, and so on here of their own ideas and find solutions in foreign forums to share out, let the other do sports counterparts to save time.

How to draw mapView on iPhone did not say, draw the trajectory in the mapView to add a MKPolyline, call the [self.mapView method to construct the MKPolyline addOverlay:self.polyLine]; but the only accept and coordinate related values, and a path gradient through natural speed control, but not in, so only rewrite a realization of the &lt protocol; MKOverlay> class. Here’s what I found. Take it with you:

GradientPolylineOverlay.h implementation

#import < Foundation/Foundation.h> #import; < MapKit/MapKit.h> @interface NSObject < GradientPolylineOverlay: MKOverlay> {MKMapPoint *points; NSUInteger; pointCount; NSUInteger pointSpace; MKMapRect boundingMapRect; pthread_rwlock_t rwLock; //Initialize the overlay with the} starting coordinate. //The overlay's boundingMapRect will be set to a sufficiently large square //centered on the starting coordinate. (ID) (initWithCenterCoordinate: Coord; CLLocationCoordinate2D) - (ID) initWithPoints: (CLLocationCoordinate2D*) _points velocity: (float*) _velocity count: (NSUInteger) _count; //Add a location observation. A MKMapRect containing the newly added point //and the previously added point is returned so that t He view can be updated //int that rectangle. If the added coordinate has not moved far enough from //the previously added coordinate it will not be added to the list and //MKMapRectNULL will be returned. / / - (MKMapRect) addCoordinate: (CLLocationCoordinate2D) - (void) coord; lockForReading; //The following properties must only be accessed when holding the read lock via lockForReading. Once you're done accessing / the points, release the lock with unlockForReading. / / read / / @property (assign) MKMapPoint *points @property (readonly); NSUInteger pointCount; @property (assign) float - (void) *velocity; unlockForReading; @end


#import "GradientPolylineOverlay.h" #import < pthread.h> #define; INITIAL_POINT_SPACE 1000 #define MINIMUM_DELTA_METERS 10 @implementation GradientPolylineOverlay{@synthesize points pointCount, velocity},; - (ID) initWithCenterCoordinate: (CLLocationCoordinate2D) coord{self = [super init]; if (self) point storage and place {//initialize this first coordinate in it pointSpace = INITIAL_POINT_SPACE; points = malloc (sizeof (MKMapPoint *pointSpace; points[0])) = MKMapPointForCoordinate (coord); pointCount = 1; //bite off up to 1/4 of the world to draw into MKMapPoint origin = points[0]; origin.x = MKMapSizeWorld.width/8.0; origin.y = MKMapSizeWorld.height /8.0 MKMapSize; size = MKMapSizeWorld; size.width /=4.0; size.height /=4.0; boundingMapRect = (MKMapRect) {origin, size}; MKMapRect worldRect = MKMapRectMake (0, 0, MKMapSizeWorld.width, MKMapSizeWorld.height); boundingMapRect = MKMapRectIntersection (boundingMapRect, worldRect); / / initialize read-write lock for drawing and updates pthread_rwlock_init (& rwLock, NULL);} return self;} - (ID) initWithPoints: (CLLocationCoordinate2D*) _points velocity: (float *) _velocity count: (NSUInteger) _count{self = [super init]; if (self) {pointCount = _count; self.points = malloc (sizeof (MKMapPoint) *pointCount for (int); i=0; i< _count; i++) { Self.points[i] = MKMapPointForCoordinate (_points[i]);} self.velocity = malloc (sizeof (float) *pointCount for (int); i=0; i< _count; i++) {self.velocity[i] = _velocity[i];} //bite off up to 1/4 of the world to draw into MKMapPoint origin = points[0]; origin.x = MKMapSizeWorld.width/8.0; origin.y = MKMapSizeWorld.height/8.0; MKMapSize = MKMapSizeWorld size size.width; /=4.0; size.height /=4.0; boundingMapRect = (MKMapRect) {origin, size}; MKMapRect worldRect = MKMapRectMake (0, 0, MKMapSizeWorld.width, MKMapSizeWorld.height); boundingMapRect = MKMapRectIntersection (boundingMapRect, worldRect); / / initi Alize read-write lock for drawing and updates pthread_rwlock_init (& rwLock, NULL);} return self;} - (void) dealloc{free (points); free (velocity); pthread_rwlock_destroy (& rwLock);} //center - (CLLocationCoordinate2D) coordinate{return MKCoordinateForMapPoint (points[0]);} - (MKMapRect) boundingMapRect{return boundingMapRect}; - (void) lockForReading{pthread_rwlock_rdlock (& rwLock);} - (void) unlockForReading{pthread_rwlock_unlock (& rwLock);} - (MKMapRect) addCoordinate: (CLLocationCoordinate2D) coord{//Acquire the write lock because we are going to changing the list of points pthread_rwlock_wrlock (& rwLock); //Convert a CLLocationCoordinate2D to an MKMapPoint MKMapPoint newPoint = MKMapPointForCoordinate (coord); MKMapPoint prevPoint = points[pointCount-1]; //Get the distance between this new point and previous point CLLocationDistance metersApart = MKMetersBetweenMapPoints (newPoint, prevPoint); MKMapRect updateRect = MKMapRectNull; if (metersApart > MINIMUM_DELTA_METERS) {//Grow the points array if necessary if (pointSpace = = pointCount) {pointSpace = 2; points = realloc (points, sizeof (MKMapPoint) * pointSpace); //Add the new point to} points array points[pointCount] = newPoint; pointCount++; //Compute MKMapRect bounding prevPoint and newPoint double minX (newPoint.x, prevPoi = MIN Nt.x); double minY = MIN (newPoint.y, prevPoint.y); double maxX = MAX (newPoint.x, prevPoint.x); double maxY = MAX (newPoint.y, prevPoint.y); updateRect = MKMapRectMake (minX, minY, maxX - minX, maxY - minY);} pthread_rwlock_unlock (& rwLock); return updateRect @end;}

The following is the method of adding PolyLine in the MapView: [self (smoothTrack] is the demand for the project of speed gradient smoothing algorithm can be ignored; coordinate data structure I draw the trajectory is composed of precision, dimension, speed of the method is the dictionary; finally adding method, classification of calls in MapView so there is level. I need, can be directly replaced by [self.mapView addOverlay:self.polyline])

NSMutableArray *smoothTrackArray = [self smoothTrack]; double count = smoothTrackArray.count; CLLocationCoordinate2D *points; float *velocity; points = malloc (sizeof (CLLocationCoordinate2D) *count; velocity (sizeof) = malloc (float) count * for (int); i=0; i< count; i++) {NSDictionary *dic = [smoothTrackArray objectAtIndex:i]; CLLocationDegrees latitude = [dic[@ latitude "doubleValue] CLLocationDegrees [dic[@; longitude =" longitude "] doubleValue] CLLocationCoordinate2D; coordinate = CLLocationCoordinate2DMake (latitude, longitude); velocity[i] [dic[@ =" speed "doubleValue]; points[i] = coordinate; self.polyline = [[GradientPolylineOverlay alloc] initWithPoints:p} Oints velocity:velocity count:count]; [self.mapView addOverlay:self.polyline level:1];

Track to add a good, but also need to be rendered on the renderer, will call the appropriate agent Mapkit: (GradientPolylineRenderer is the corresponding renderer)

#pragma mark - #pragma mark - MKMap - Delegate (MKOverlayRenderer *) mapView: (* MKMapView) mapView rendererForOverlay: (ID < MKOverlay> overlay{) if ([overlay isKindOfClass:[GradientPolylineOverlay class]]) {/ / GradientPolylineRenderer = [[GradientPolylineRenderer alloc] *polylineRenderer locus initWithOverlay:overlay]; polylineRenderer.lineWidth = 8.f; return polylineRenderer;} return nil;}

GradientPolylineRenderer.h implementation:

#import < MapKit/MapKit.h> @interface GradientPolylineRenderer: MKOverlayPathRenderer @end

GradientPolylineRenderer.m: (a few macros, the above is the speed of the boundary value, and the corresponding color boundary value; and I will judge whether here on hues[i], 0 project needs to distinguish between the suspension point and speed too quickly, has anti cheating, in this case with a dotted line instead of, if only to draw the gradient lines without this)

#import "GradientPolylineRenderer.h" #import < pthread.h> #import "GradientPolylineOverlay.h" #import "Constant.h" #define V_MAX 4.5 #define V_MIN 1 #define H_MAX 0.33 #define H_MIN 0.03 @implementation GradientPolylineRenderer{float* hues; pthread_rwlock_t rwLock; GradientPolylineOverlay* polyline;} - (ID) initWithOverlay: (id< MKOverlay> overlay{) self = [super initWithOverlay:overlay]; if (self) {pthread_rwlock_init (& rwLock, NULL); polyline = ((GradientPolylineOverlay*) self.overlay) float; *velocity = polyline.velocity; int = count (int) polyline.pointCount; [self velocity:velocity ToHue:& hues count:count]; [self createPath];} return self; Convert velocity to Hue} / * * * * * using specific formular. H (V) = Hmax (V, > Vmax = Hmin (+) * (v-Vmin) * (Hmax-Hmin)) / (Vmax-Vmin), (Vmin = < v = < Vmax = (V) * Hmin, < Vmin) * * @param velocity Velocity * @param count count of velocity list. list. @return An array of hues * * mapping each velocity. * / - (void) velocity: (float*) velocity ToHue: (float**) _hue count: (int) count{*_hue = malloc (sizeof (float) *count for (int); i=0; i< count; i++ float) {curVelo = velocity[i]; / / / / / / curVelo = (the original gradient formula (curVelo < V_MIN (curVelo)? V_MIN: > V_MAX)? V_MAX: curVelo); / / (*_hue) = H_MIN ([i] + (curVelo-V_MIN) * (H_MAX-H_MIN) (V_MAX-V_MIN) / if (curV); Elo> (0) {curVelo (curVelo = < V_MIN (curVelo)? V_MIN: > V_MAX)? V_MAX: curVelo); (*_hue) ([i] = H_MIN + (curVelo-V_MIN) * (H_MAX-H_MIN)) / (V_MAX-V_MIN); / /}else{suspension (*_hue) color [i] = 0;}}} - (void) createPath{CGMutablePathRef path (BOOL) = CGPathCreateMutable; pathIsEmpty = YES; for (int i=0; i< polyline.pointCount; i++) {CGPoint point = [self pointForMapPoint:polyline.points[i]]; if (pathIsEmpty) {CGPathMoveToPoint (path, nil, point.x, point.y); pathIsEmpty = NO;} else {(path, nil, CGPathAddLineToPoint point.x, point.y);}} pthread_rwlock_wrlock (& rwLock); Self.path = path; //< don't forget this line. pthread_rwlock_unlock (& rwLock);} //- (BOOL) canDrawMapRect: (MKMapRect) mapRect zoomScale: (MKZoomScale) zoomScale{/ CGRect pointsRect = CGPathGetBoundingBox (self.path); / / CGRect / / return mapRectCG = [self rectForMapRect:mapRect]; CGRectIntersectsRect (pointsRect, mapRectCG); //} - (void) drawMapRect: (MKMapRect) mapRect zoomScale: (MKZoomScale) zoomScale inContext: (CGContextRef) context{//put this Blok into the canDraw method cause problem CGRect pointsRect = CGPathGetBoundingBox (self.path); CGRect mapRectCG = [self rectForMapRect:mapRect]; if (! CGRectIntersectsRect (pointsRect, mapRectCG) CGContextSetLineCap (return); context, kCGLineCapRound; CG) ContextSetLineJoin (context, kCGLineJoinRound); UIColor* Pcolor *ccolor; for (int i=0; i< polyline.pointCount; i++) {CGPoint point = [self pointForMapPoint:polyline.points[i]]; CGMutablePathRef path = CGPathCreateMutable (if); (hues[i]==0.) if (i==0) {/ / dash {CGPathMoveToPoint (path, nil, point.x, point.y); / /}else{color CGContextSetRGBStrokeColor (context, 153 / 255, 153 / 255, 153 / 255, 1); / / lineWidth = CGContextConvertSizeToUserSpace (linewidth of CGFloat (CGSize) context, {self.lineWidth, self.lineWidth}).Width; CGContextSetLineWidth (context, lineWidth); CGFloat lengths[] = {lineWidth*2, lineWidth*2}; / / set (context, lineWidth, CGContextSetLineDash line lengths, 2); / / set CGPoint prevPoint = [self pointForMapPoint:polyline.points[i-1]] line; CGPathMoveToPoint (path, nil, prevPoint.x, prevPoint.y); CGPathAddLineToPoint (path, nil, point.x, point.y); CGContextAddPath (context, path); CGContextStrokePath (context);}}else{/ / ccolor = [UIColor colorWithHue:hues[i] saturation:1.0f gradient running brightness:1.0f alpha:1.0f]; if (i==0) {CGPathMoveToPoint (path, nil, point.x, point.y; else) {CGPoin} T 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); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB (CGGradientRef); gradient = CGGradientCreateWithColorComponents (colorSpace, gradientColors, gradientLocation, 2); CGColorSpaceRelease (colorSpace) CGPoint gradientStart; CGPoint = prevPoint; gradientEnd = point; CGContextDrawLinearGradient (context, gradient, gradientStart, gradientEnd, kCGGradientDrawsAfterEndLoc Ation); CGGradientRelease (gradient); CGContextRestoreGState (context); Pcolor [UIColor = colorWithCGColor:ccolor.CGColor];}}}} @end

The above is I draw the gradient trajectory based on Mapkit method, compared with the High German track, smooth degree is still lacking, but these are the items after optimization point.
finally, attached to my project download address, if there is help to you, please praise, thank you! If there are other issues, also welcome to discuss Mt=8