The LogYourRun iPhone App uses the GPS functionality of the iPhone to track running distance as well as the running route. When data is uploaded from the app to the website the list of GPS coordinates have to be transferred from the phone to the website. Since I already store route GPS data on the LogYourRun website as an encoded Google polyline I decided to have the iPhone encode the GPS data before sending to the website. This takes processing load off the server and allows for faster transfer of data from the iPhone to the website.
I already have a JavaScript and a PHP version of the functions that both encode and decode GPS data as a Google polyline – so it was just a matter of converting the functions to Objective-C / Cocoa.
The way that the encoder works is first it multiples the coordinate by 10e5, takes the floor value and compares it to the coordinate to the 10e5 value of the previous coordinate and encodes the difference between the two points. The following are functions used to perform the basic math.
This function is used to multiply the coordinate by 10e5:
- (int) floor1e5: (double) coordinate {
return (int) floor(coordinate * 1e5);
}
This function is used to encode a signed number:
- (NSString *) encodeSignedNumber: (int) num {
int sgn_num = num << 1;
if (num < 0) {
sgn_num = ~(sgn_num);
}
return [self encodeNumber: sgn_num];
}
And this function is used to encode an unsigned int:
- (NSString *) encodeNumber: (int) num {
int nextValue;
NSMutableString *encodeString = [[NSMutableString alloc] initWithCapacity:5];
[encodeString autorelease];
while (num >= 0x20) {
nextValue = (0x20 | (num & 0x1f)) + 63;
[encodeString appendFormat:@"%c", ((char) (nextValue))];
num >>= 5;
}
num += 63;
[encodeString appendFormat:@"%c", ((char) (num))];
return encodeString;
}
The following function is used to encode an array of CoreLocation objects (CLLocation *). This array is called locations in the following code. The first thing the function does is to make sure that there are points in our locations assay. Next I setup a mutable string which is going to contain the encoded polyline string as well as a temporary CoreLocation object (tmploc). Since we have not started going through the locations we set the previous latitude and longitude to 0 each (plat and plng). Then I go through the locations array and put them into the temploc. For each location the previous latitude and longitude is subtracted from the current after the 1e5 conversion. The encoded latitude and longitude are then added to the encoded polyline string which is returned at the end of the function.
- (NSString *) googlePolyline {
int i, late5, lnge5, dlat, dlng, plat, plng;
if ([locations count] == 0) {
return @"";
}
NSMutableString *encodedPoints = [[NSMutableString alloc] initWithCapacity:100];
[encodedPoints autorelease];
CLLocation *tmploc;
plat = 0;
plng = 0;
for (i = 0; i < [locations count]; i++) {
tmploc = [locations objectAtIndex:i];
late5 = [self floor1e5: tmploc.coordinate.latitude];
lnge5 = [self floor1e5: tmploc.coordinate.longitude];
dlat = late5 - plat;
dlng = lnge5 - plng;
plat = late5;
plng = lnge5;
[encodedPoints appendFormat:@"%@%@", [self encodeSignedNumber:dlat], [self encodeSignedNumber:dlng]];
}
return [[[NSString alloc] initWithFormat: @"%@", encodedPoints] autorelease];
}
The googlePolyline function can be used to convert an array of location objects to a simple string which can then applied directly to a google map.
For other ports of the google polyline encoder see:
http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/