MapKit: Encoded Polylines
With the introduction of iOS 4, Apple expanded MapKit.framework tremendously. One of the additions I was most excited about was MKPolyline specifically and "overlays" in general. MKPolyline is essentially an object that encapsulates a "path" on a map.
However, disappointingly, there's no support for Google's own encoded polylines, which seems like the ideal data vendor for these MKPolyline objects. These so-called "encoded polylines" are essentially a method for serialising an array of latitudes and longitudes in a space-efficient manner (JSON, typically used with Google APIs, does not support raw binary data.) When you're in Google's line of business, every byte saved in a single request means big monetary savings across all your customers, hence their desire to avoid inefficient decimal ASCII encoding.
This prompted me to write the following category on MKPolyline. Usually I am wary to create categories as I've seen them (ab)used needlessly, but I believe this is a good use-case. Let me know if you disagree!
@implementation MKPolyline (MKPolyline_EncodedString) + (MKPolyline *)polylineWithEncodedString:(NSString *)encodedString { const char *bytes = [encodedString UTF8String]; NSUInteger length = [encodedString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; NSUInteger idx = 0; NSUInteger count = length / 4; CLLocationCoordinate2D *coords = calloc(count, sizeof(CLLocationCoordinate2D)); NSUInteger coordIdx = 0; float latitude = 0; float longitude = 0; while (idx < length) { char byte = 0; int res = 0; char shift = 0; do { byte = bytes[idx++] - 63; res |= (byte & 0x1F) << shift; shift += 5; } while (byte >= 0x20); float deltaLat = ((res & 1) ? ~(res >> 1) : (res >> 1)); latitude += deltaLat; shift = 0; res = 0; do { byte = bytes[idx++] - 0x3F; res |= (byte & 0x1F) << shift; shift += 5; } while (byte >= 0x20); float deltaLon = ((res & 1) ? ~(res >> 1) : (res >> 1)); longitude += deltaLon; float finalLat = latitude * 1E-5; float finalLon = longitude * 1E-5; CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(finalLat, finalLon); coords[coordIdx++] = coord; if (coordIdx == count) { NSUInteger newCount = count + 10; coords = realloc(coords, newCount * sizeof(CLLocationCoordinate2D)); count = newCount; } } MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coords count:coordIdx]; free(coords); return polyline; } @end












