OBJ-C: Getting the minimum/maximum value in a NSMutableArray - iphone

I want to get the maximum and minimum values of a NSMutableArray so I can create a core-plot plotspace and graph based around those max and min values.
My code is as follows:
NSMutableArray *contentArray = [NSMutableArray arrayWithCapacity:100];
NSUInteger i;
for (i=0; i<60; i++){
id x = [NSNumber numberWithFloat:i*0.05];
id y = [NSNumber numberWithFloat:1.2*rand()/(float)RAND_Max + 0.6];
[contentArray addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:x, #"x", y, #"y", nil]];
}
self.dataForPlot = contentArray;
CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;
plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat() length:CPDecimalFromFloat()];
plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat() length:CPDecimalFromFloat()];
What do I fill in the blanks for xRange and yRange assignment if I want the graph to span only the space of what's specified in contentArray?

In this case, you should use -[CPPlotSpace scaleToFitPlots:]. For more general calculations on array values, read on...
This is an ideal use for Key-Value coding and associated array operators. In your example,
float maxX = [[contentArray valueForKeyPath:#"#max.x"] floatValue];
float minY = [[contentArray valueForKeyPath:#"#min.x"] floatValue];
float maxY = [[contentArray valueForKeyPath:#"#max.y"] floatValue];
float minY = [[contentArray valueForKeyPath:#"#min.y"] floatValue];
The array operators call valueForKeyPath: on the contentArray which gives an array built by calling the same valueForKeyPath: on each member of the array with the key path to the right of the array operator (i.e. #min and #max). The orignal call then applies the given operator to the resulting array. You could easily define a category on NSArray to give you back a struct of min/max values:
typedef struct {
float minX;
float minY;
float maxX;
float maxY;
} ArrayValueSpace;
#implementation NSArray (PlotSpaceAdditions)
- (ArrayValueSpace)psa_arrayValueSpace {
ArrayValueSpace result;
result.maxX = [[contentArray valueForKeyPath:#"#max.x"] floatValue];
result.minX = [[contentArray valueForKeyPath:#"#min.x"] floatValue];
result.maxY = [[contentArray valueForKeyPath:#"#max.y"] floatValue];
result.minY = [[contentArray valueForKeyPath:#"#min.y"] floatValue];
return result;
}

You can find max and min values when you fill it, but it would work just when used in your snippet. If you want something more generic you should simply go through the list and searching for it.
// contentArray already defined
int maxX = 0, minX = 1000, maxY = 0, minY = 1000;
for (int i = 0; i < [contentArray count]; ++1)
{
int curX = [[[contentArray objectForKey:#"x"] objectAtIndex:i] integerValue];
int curY = [[[contentArray objectForKey:#"y"] objectAtIndex:i] integerValue];
if (curX < minX)
minX = curX;
else if (curX > maxX)
maxX = curX;
if (curY < minY)
minY = curY;
else if (curY > maxY)
maxY = curY;
}

You could use fast enumeration through the array.
.
NSUInteger maxX = 0;
NSUInteger minX = 0xFFFFFFFF; //(for 32 bit)
NSUInteger maxY = 0;
NSUInteger minY = 0xFFFFFFFF; //(for 32 bit)
for ( NSDictionary *dict in contentArray )
{
NSUInteger newX = [[dict objectForKey:#"x"] integerValue];
NSUInteger newY = [[dict objectForKey:#"y"] integerValue];
if (maxX > newX) maxX = newX;
if (minX > newX) minX = newX;
if (maxY > newY) maxY = newY;
if (minY > newY) minX = newY;
}

if you have NSMutableArray of NSDictionary objects you can use this:
-(float)findMax:array arrayKey:obj {
float max = [[[array objectAtIndex:0] objectForKey:obj] floatValue];
for ( NSDictionary *dict in array ) {
if(max<[[dict objectForKey:obj] floatValue])max=[[dict objectForKey:obj] floatValue];
}
return max;
}
-(float)findMin:array arrayKey:obj {
float min = [[[array objectAtIndex:0] objectForKey:obj] floatValue];
for ( NSDictionary *dict in array ) {
if (min > [[dict objectForKey:obj] floatValue])min = [[dict objectForKey:obj] floatValue];
}
return min;
}
You would access methods like this if in another class:
GraphData *c=[[GraphData alloc] init];
float maxY=[c findMax:plotData arrayKey:[NSNumber numberWithInt:1]]; //1 or 0 depending on axis
[c release];

//This gives Max and Min Value in NSMutableArrray
-(void)FindingMinAndMaxValueInNSMutableArray{
NSMutableArray *array=[[NSMutableArray alloc]initWithObjects:#"0.987",#"0.951",#"0.881",#"0.784",#"0.662",#"0.522",#"0.381",#"-0.265",#"-0.197", #"0.189",#"-0.233",#"0.310",#"0.402",#"0.402",#"0.988",#"0.633",#"0.661",#"0.656",#"0.617",#"0.634",#"0.690",#"0.767",#"0.836",nil];
NSLog(#"The Array Value is %#",array);
NSLog(#"The Array Count is %lu",(unsigned long)array.count);
NSNumber *maxValue = [array valueForKeyPath:#"#max.doubleValue"];
NSLog(#"The maxValue is %#",maxValue);
NSString *str=[NSString stringWithFormat:#"%#",maxValue];
NSInteger path=[array indexOfObject:str];
NSIndexPath *indexpath=[NSIndexPath indexPathForItem:path inSection:0];
NSLog(#"Max Value = %# and index = %ld",maxValue,(long)indexpath.row);
NSNumber *minValue = [array valueForKeyPath:#"#min.doubleValue"];
NSLog(#"The minValue is %#",minValue);
NSString *str1 = [NSString stringWithFormat:#"%#",minValue];
NSInteger path1 = [array indexOfObject:str1];
NSIndexPath *indexPath1 = [NSIndexPath indexPathForItem:path1 inSection:0];
NSLog(#"Min Value =%# and index = %ld",minValue,(long)indexPath1.row);
}

Related

Min and Max Value of an NSMutableArray

I have a simple looking piece of code that has me completely flummoxed.
NSInteger ymax;
NSInteger ymin;
NSInteger numberIndex1;
NSInteger numberIndex2;
for (NSNumber *theNumber in array2)
{
if ([theNumber integerValue] > ymax) {
ymax = [theNumber integerValue];
numberIndex1 = [array2 indexOfObject:theNumber];
}
}
for (NSNumber *theNumber in array2)
{
if ([theNumber integerValue] < ymin) {
ymin = [theNumber integerValue];
numberIndex2 = [array2 indexOfObject:theNumber];
}
}
NSLog(#"Highest number: %d at index: %d", ymax, numberIndex1);
NSLog(#"Lowest number: %d at index: %d", ymin, numberIndex2);
The NSLog is outputted as:
Highest number: 129171656 at index: -1073752392 (Huh??)
Lowest number: 57 at index: 5 (Correct)
How do you explain this odd behaviour? Both the functions look the same. One is working and one isn't? I've played around a lot with this, but I still can't put my finger on it. Any help would be appreciated/
You can get maximum and minimum number as below code. It may help you
NSNumber * max = [array2 valueForKeyPath:#"#max.intValue"];
NSNumber * min = [array2 valueForKeyPath:#"#min.intValue"];
NSUInteger numberIndex1 = [array indexOfObject:min];
NSUInteger numberIndex2 = [array indexOfObject:max];
NSLog(#"Max Value = %d and index = %d",[max intValue],numberIndex1);
NSLog(#"Min Value = %d and index = %d",[min intValue],numberIndex2);
If I am not wrong you are considering the default value of NSInteger is 0, No, it isn't guaranteed to be zero, since it's a local automatic variable. Without initialization, its value is indeterminate.
so you need to set default values for your var, start with ymax = -1;
Please initialize NSInteger ymax = 0;
NSInteger ymin = 0 ;
NSInteger numberIndex1 = 0;
NSInteger numberIndex2 = 0;
It will fix your issue.
Otherwise it is checking with a garbage value and giving wrong result.
enjoy with my answer.......happy coding
NSArray *arr1=[[NSArray alloc]initWithObjects:#"0.987",#"0.951",#"0.881",#"0.784",#"0.662",#"0.522",#"0.381",#"-0.265",#"-0.197",
#"0.189",#"-0.233",#"0.310",#"0.402",#"0.402",#"0.988",#"0.633",#"0.661",#"0.656",#"0.617",#"0.634",#"0.690",#"0.767",#"0.836",nil];
NSNumber * max = [arr1 valueForKeyPath:#"#max.floatValue"];
NSString *str=[NSString stringWithFormat:#"%#",max];
NSInteger path=[arr1 indexOfObject:str];
NSIndexPath *indepath=[NSIndexPath indexPathForItem:path inSection:0];
NSNumber * min = [arr1 valueForKeyPath:#"#min.floatValue"];
NSString *str1=[NSString stringWithFormat:#"%#",min];
NSInteger path1=[arr1 indexOfObject:str1];
NSIndexPath *indepath1=[NSIndexPath indexPathForItem:path1 inSection:0];
NSLog(#"Max Value = %f and index = %ld",[max floatValue],(long)indepath.row);
NSLog(#"Min Value = %f and index = %ld",[min floatValue],(long)indepath1.row);
A more legitimate solution would be:
NSArray *originalArray = #[[NSNumber numberWithInt:91],
[NSNumber numberWithInt:12],
[NSNumber numberWithInt:99123],
[NSNumber numberWithInt:9],
[NSNumber numberWithInt:43234]];
NSArray *sortedArray = [originalArray sortedArrayUsingSelector:#selector(compare:)];
NSNumber *minNumber = [sortedArray objectAtIndex:0];
NSNumber *maxNumber = [sortedArray lastObject];
NSInteger minIndex = [originalArray indexOfObject:minNumber];
NSInteger maxIndex = [originalArray indexOfObject:maxNumber];

How to create a CLLocationCoordinate2d object

I am trying to to call MKPolylines' + polylineWithCoordinates:count: method.
How can I create a CLLocationCoordinate2D * object in this case. I have gone through this particular answer in CLLocationCoordinate2D without knowing how many will be in the array?
// unpacking an array of NSValues into memory
CLLocationCoordinate2D *points = malloc([mutablePoints count] * sizeof(CLLocationCoordinate2D));
for(int i = 0; i < [mutablePoints count]; i++) {
[[mutablePoints objectAtIndex:i] getValue:(points + i)];
}
MKPolyline *myPolyline = [MKPolyline polylineWithCoordinates:points count:[mutablePoints count]];
free(points);
What kind of entries are in the array mutablePoints in the above case?
If your question is simply what kind of entries are in the array, the answer is quite simple: NSValue entries. You can have a look at this guide for more info on how to use NSValues.
CLLocationCoordinate2D coordinate;
for (int i = 0; i < arr.count; i++)
{
float t1 = [[arr objectAtIndex:1] floatValue];
float t2 = [[arr objectAtIndex:0] floatValue];
coordinate.latitude = t1;
coordinate.longitude = t2;
}

How to decode the Google Directions API polylines field into lat long points in Objective-C for iPhone?

I want to draw routes on a map corresponding to directions JSON which I am getting through the Google Directions API: https://developers.google.com/maps/documentation/directions/start
I have figured out how to extract the latitude and longitude from the steps field, however this doesn't follow curvy roads very well. I think what I need is to decode the polyline information, I found Googles instructions on how to encode polylines: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
I did find some code here for Android and also Javascript on decoding the polylines, for example:
Map View draw directions using google Directions API - decoding polylines
android get and parse Google Directions
But I can't find same for Objective-C iPhone code, can anybody help me with this? I'm sure I can do it myself if I have to, but it sure would save me some time if it's already available somewhere.
EDIT: the key here is being able to decode the base64 encoding on a character by character basis. To be more specific, I get something like this in JSON from Google which is encoded using base64 encoding among other things:
... "overview_polyline" : {
"points" : "ydelDz~vpN_#NO#QEKWIYIIO?YCS#WFGBEBICCAE?G#y#RKBEBEBAD?HTpB#LALALCNEJEFSP_#LyDv#aB\\GBMB"
},
...
Note: I should mention that this question refers to Google Maps API v1, it is much easier to do this in v2 using GMSPolyLine polyLineWithPath as many answers below will tell you (thanks to #cdescours).
I hope it's not against the rules to link to my own blog post if it's relevant to the question, but I've solved this problem in the past. Stand-alone answer from linked post:
#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
The best and lightest answer should be to use the method provided by Google in the framework :
[GMSPolyline polylineWithPath:[GMSPath pathFromEncodedPath:encodedPath]]
If you are working with Google Map on iOS and want to draw the route including the polylines, google itself provides an easier way to get the GMSPath from polyline as,
GMSPath *pathFromPolyline = [GMSPath pathFromEncodedPath:polyLinePoints];
Here is the complete code:
+ (void)callGoogleServiceToGetRouteDataFromSource:(CLLocation *)sourceLocation toDestination:(CLLocation *)destinationLocation onMap:(GMSMapView *)mapView_{
NSString *baseUrl = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&sensor=false", sourceLocation.coordinate.latitude, sourceLocation.coordinate.longitude, destinationLocation.coordinate.latitude, destinationLocation.coordinate.longitude];
NSURL *url = [NSURL URLWithString:[baseUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"Url: %#", url);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
GMSMutablePath *path = [GMSMutablePath path];
NSError *error = nil;
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSArray *routes = [result objectForKey:#"routes"];
NSDictionary *firstRoute = [routes objectAtIndex:0];
NSDictionary *leg = [[firstRoute objectForKey:#"legs"] objectAtIndex:0];
NSArray *steps = [leg objectForKey:#"steps"];
int stepIndex = 0;
CLLocationCoordinate2D stepCoordinates[1 + [steps count] + 1];
for (NSDictionary *step in steps) {
NSDictionary *start_location = [step objectForKey:#"start_location"];
stepCoordinates[++stepIndex] = [self coordinateWithLocation:start_location];
[path addCoordinate:[self coordinateWithLocation:start_location]];
NSString *polyLinePoints = [[step objectForKey:#"polyline"] objectForKey:#"points"];
GMSPath *polyLinePath = [GMSPath pathFromEncodedPath:polyLinePoints];
for (int p=0; p<polyLinePath.count; p++) {
[path addCoordinate:[polyLinePath coordinateAtIndex:p]];
}
if ([steps count] == stepIndex){
NSDictionary *end_location = [step objectForKey:#"end_location"];
stepCoordinates[++stepIndex] = [self coordinateWithLocation:end_location];
[path addCoordinate:[self coordinateWithLocation:end_location]];
}
}
GMSPolyline *polyline = nil;
polyline = [GMSPolyline polylineWithPath:path];
polyline.strokeColor = [UIColor grayColor];
polyline.strokeWidth = 3.f;
polyline.map = mapView_;
}];
}
+ (CLLocationCoordinate2D)coordinateWithLocation:(NSDictionary*)location
{
double latitude = [[location objectForKey:#"lat"] doubleValue];
double longitude = [[location objectForKey:#"lng"] doubleValue];
return CLLocationCoordinate2DMake(latitude, longitude);
}
Swift 3.0
let polyline = GMSPolyline(path: GMSPath.init(fromEncodedPath: encodedPolyline))
Python Implementation
This isn't in Objective-C, but this thread is where Google drops you if you're looking to decode polyline strings from Google Maps. In case anyone else needs it (much like I did), here's a Python implementation for decoding polyline strings. This is ported from the Mapbox JavaScript version; more info found on my repo page.
def decode_polyline(polyline_str):
index, lat, lng = 0, 0, 0
coordinates = []
changes = {'latitude': 0, 'longitude': 0}
# Coordinates have variable length when encoded, so just keep
# track of whether we've hit the end of the string. In each
# while loop iteration, a single coordinate is decoded.
while index < len(polyline_str):
# Gather lat/lon changes, store them in a dictionary to apply them later
for unit in ['latitude', 'longitude']:
shift, result = 0, 0
while True:
byte = ord(polyline_str[index]) - 63
index+=1
result |= (byte & 0x1f) << shift
shift += 5
if not byte >= 0x20:
break
if (result & 1):
changes[unit] = ~(result >> 1)
else:
changes[unit] = (result >> 1)
lat += changes['latitude']
lng += changes['longitude']
coordinates.append((lat / 100000.0, lng / 100000.0))
return coordinates
- (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;
}
- (MKPolygonRenderer *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay {
// MKPolygonRenderer *polylineView = [[MKPolygonRenderer alloc] initWithOverlay:overlay];
MKPolylineView *polylineView = [[MKPolylineView alloc] initWithPolyline:overlay];
polylineView.strokeColor = [UIColor redColor];
polylineView.lineWidth = 4.0;
[self zoomToPolyLine:mapview polyline:overlay animated:YES];
return polylineView;
}
-(void)zoomToPolyLine: (MKMapView*)map polyline: (MKPolyline*)polyline animated: (BOOL)animated
{
[map setVisibleMapRect:[polyline boundingMapRect] edgePadding:UIEdgeInsetsMake(25.0, 25.0, 25.0, 25.0) animated:animated];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
// NSLog(#"didUpdateToLocation: %#", newLocation);
CLLocation *currentLocation = newLocation;
if (currentLocation != nil) {
currlong = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
currlt = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
}
NSString *origin = [NSString stringWithFormat:#"%#%#%#",currlt,#",",currlong];
//I have just mention static location
NSString *drivein = #"23.0472963,72.52757040000006";
NSString *apikey = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/directions/json?origin=%#&destination=%#",origin,drivein];
NSURL *url = [NSURL URLWithString:apikey];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response;
NSError *error;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
if(!error)
{
NSData *data = [responseString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
NSArray *routesArray = [jsonResponse objectForKey:#"routes"];
NSLog(#"route array %#",routesArray);
if ([routesArray count] > 0)
{
NSDictionary *routeDict = [routesArray objectAtIndex:0];
NSDictionary *routeOverviewPolyline = [routeDict objectForKey:#"overview_polyline"];
NSString *points = [routeOverviewPolyline objectForKey:#"points"];
MKPolyline *line = [self polylineWithEncodedString:points];
[mapview addOverlay:line];
}
}
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(currentLocation.coordinate, 500, 500);
MKCoordinateRegion adjustedRegion = [mapview regionThatFits:viewRegion];
[mapview setRegion:adjustedRegion animated:YES];
mapview.showsUserLocation = YES;
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = currentLocation.coordinate;
point.title = #"Your current Locations";
point.subtitle = #"You are here!";
[mapview addAnnotation:point];
[locationmanger stopUpdatingLocation];
}
Here's how I do it in my directions app. keyPlace is your destination object
- (void)getDirections {
CLLocation *newLocation;// = currentUserLocation;
MKPointAnnotation *annotation = [[[MKPointAnnotation alloc] init] autorelease];
annotation.coordinate = CLLocationCoordinate2DMake(newLocation.coordinate.latitude, newLocation.coordinate.longitude);
annotation.title = #"You";
[mapView addAnnotation:annotation];
CLLocationCoordinate2D endCoordinate;
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&sensor=false&mode=walking", newLocation.coordinate.latitude, newLocation.coordinate.longitude, keyPlace.lat, keyPlace.lon]];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request startSynchronous];
if ([[request.responseString.JSONValue valueForKey:#"status"] isEqualToString:#"ZERO_RESULTS"]) {
[[[[UIAlertView alloc] initWithTitle:#"Error"
message:#"Could not route path from your current location"
delegate:nil
cancelButtonTitle:#"Close"
otherButtonTitles:nil, nil] autorelease] show];
self.navigationController.navigationBar.userInteractionEnabled = YES;
return;
}
int points_count = 0;
if ([[request.responseString.JSONValue objectForKey:#"routes"] count])
points_count = [[[[[[request.responseString.JSONValue objectForKey:#"routes"] objectAtIndex:0] objectForKey:#"legs"] objectAtIndex:0] objectForKey:#"steps"] count];
if (!points_count) {
[[[[UIAlertView alloc] initWithTitle:#"Error"
message:#"Could not route path from your current location"
delegate:nil
cancelButtonTitle:#"Close"
otherButtonTitles:nil, nil] autorelease] show];
self.navigationController.navigationBar.userInteractionEnabled = YES;
return;
}
CLLocationCoordinate2D points[points_count * 2];
int j = 0;
NSArray *steps = nil;
if (points_count && [[[[request.responseString.JSONValue objectForKey:#"routes"] objectAtIndex:0] objectForKey:#"legs"] count])
steps = [[[[[request.responseString.JSONValue objectForKey:#"routes"] objectAtIndex:0] objectForKey:#"legs"] objectAtIndex:0] objectForKey:#"steps"];
for (int i = 0; i < points_count; i++) {
double st_lat = [[[[steps objectAtIndex:i] objectForKey:#"start_location"] valueForKey:#"lat"] doubleValue];
double st_lon = [[[[steps objectAtIndex:i] objectForKey:#"start_location"] valueForKey:#"lng"] doubleValue];
//NSLog(#"lat lon: %f %f", st_lat, st_lon);
if (st_lat > 0.0f && st_lon > 0.0f) {
points[j] = CLLocationCoordinate2DMake(st_lat, st_lon);
j++;
}
double end_lat = [[[[steps objectAtIndex:i] objectForKey:#"end_location"] valueForKey:#"lat"] doubleValue];
double end_lon = [[[[steps objectAtIndex:i] objectForKey:#"end_location"] valueForKey:#"lng"] doubleValue];
if (end_lat > 0.0f && end_lon > 0.0f) {
points[j] = CLLocationCoordinate2DMake(end_lat, end_lon);
endCoordinate = CLLocationCoordinate2DMake(end_lat, end_lon);
j++;
}
}
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:points count:points_count * 2];
[mapView addOverlay:polyline];
}
#pragma mark - MapKit
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKPinAnnotationView *annView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentloc"] autorelease];
annView.canShowCallout = YES;
annView.animatesDrop = YES;
return annView;
}
- (MKOverlayView *)mapView:(MKMapView *)mapView
viewForOverlay:(id<MKOverlay>)overlay {
MKPolylineView *overlayView = [[[MKPolylineView alloc] initWithOverlay:overlay] autorelease];
overlayView.lineWidth = 5;
overlayView.strokeColor = [UIColor purpleColor];
overlayView.fillColor = [[UIColor purpleColor] colorWithAlphaComponent:0.5f];
return overlayView;
}
In case anyone would need the decoding-code in VBA, here is a (working) port:
Function decodeGeopoints(encoded)
decodeGeopoints = ""
' This code is a port to VBA from code published here:
' http://blog.synyx.de/2010/06/routing-driving-directions-on-android-part-1-get-the-route/
'//decoding
'List poly = new ArrayList();
'// replace two backslashes by one (some error from the transmission)
'encoded = encoded.replace("\\", "\");
encoded = Replace(encoded, "\\", "\")
'int index = 0, len = encoded.length();
Dim index As Long
index = 0
Dim leng As Long
leng = Len(encoded)
'int lat = 0, lng = 0;
Dim lat As Long
lat = 0
Dim lng As Long
lng = 0
'while (index < len) {
While (index < leng)
'int b, shift = 0, result = 0;
Dim b, shift, result As Long
b = 0
shift = 0
result = 0
'do {
Do
'b = encoded.charAt(index++) - 63;
index = index + 1
b = Asc(Mid(encoded, index, 1)) - 63
'result |= (b & 0x1f) << shift;
result = result Or ((b And 31) * (2 ^ shift))
'shift += 5;
shift = shift + 5
'} while (b >= 0x20);
Loop While (b >= 32)
'int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
Dim dlat As Long
If (result And 1) <> 0 Then
dlat = Not Int(result / 2)
Else
dlat = Int(result / 2)
End If
'lat += dlat;
lat = lat + dlat
'shift = 0;
shift = 0
'result = 0;
result = 0
'do {
Do
'b = encoded.charAt(index++) - 63;
index = index + 1
b = Asc(Mid(encoded, index, 1)) - 63
'result |= (b & 0x1f) << shift;
result = result Or ((b And 31) * (2 ^ shift))
'shift += 5;
shift = shift + 5
'} while (b >= 0x20);
Loop While (b >= 32)
'int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
Dim dlng As Long
If (result And 1) <> 0 Then
dlng = Not Int(result / 2)
Else
dlng = Int(result / 2)
End If
'lng += dlng;
lng = lng + dlng
'GeoPoint p = new GeoPoint((int) (((double) lat / 1E5) * 1E6), (int) (((double) lng / 1E5) * 1E6));
Dim myLat, myLng As Double
myLat = (lat / 100000)
'myLat = myLat * 1000000
myLng = (lng / 100000)
'myLng = myLng * 1000000
'poly.add(p);
decodeGeopoints = decodeGeopoints & Comma2Dot(myLng) & "," & Comma2Dot(myLat) & ",0 "
'}
Wend
End Function
For Google maps it already have a straight forward method , polylineWithPath, so I prefer this snippet.
-(void)drawPathFrom:(CLLocation*)source toDestination:(CLLocation*)destination{
NSString *baseUrl = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&sensor=true", source.coordinate.latitude, source.coordinate.longitude, destination.coordinate.latitude, destination.coordinate.longitude];
NSURL *url = [NSURL URLWithString:[baseUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"Url: %#", url);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if(!connectionError){
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *routes = [result objectForKey:#"routes"];
NSDictionary *firstRoute = [routes objectAtIndex:0];
NSString *encodedPath = [firstRoute[#"overview_polyline"] objectForKey:#"points"];
GMSPolyline *polyPath = [GMSPolyline polylineWithPath:[GMSPath pathFromEncodedPath:encodedPath]];
polyPath.strokeColor = [UIColor redColor];
polyPath.strokeWidth = 3.5f;
polyPath.map = _mapView;
}
}];
}
Swift 4.2 / Swift 5
let gmsPolyline = GMSPolyline(path: GMSPath(fromEncodedPath: encodedPolyline))
gmsPolyline.map = map
This is my own revisitation of Sedate Alien's answer.
It is the same implementation save for removing duplicated code and using NSMutableData instead of manually allocating stuff.
#implementation MKPolyline (EncodedString)
+ (float)decodeBytes:(const char *)bytes atPos:(NSUInteger *)idx toValue:(float *)value {
char byte = 0;
int res = 0;
char shift = 0;
do {
byte = bytes[(*idx)++] - 0x3F;
res |= (byte & 0x1F) << shift;
shift += 5;
}
while (byte >= 0x20);
(*value) += ((res & 1) ? ~(res >> 1) : (res >> 1));
return (*value) * 1E-5;
}
+ (MKPolyline *)polylineWithEncodedString:(NSString *)encodedString {
const char *bytes = [encodedString UTF8String];
NSUInteger length = [encodedString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSUInteger idx = 0;
NSMutableData *data = [NSMutableData data];
float lat = 0;
float lon = 0;
CLLocationCoordinate2D coords = CLLocationCoordinate2DMake(0, 0);
while (idx < length) {
coords.latitude = [self decodeBytes:bytes atPos:&idx toValue:&lat];
coords.longitude = [self decodeBytes:bytes atPos:&idx toValue:&lon];
[data appendBytes:&coords length:sizeof(CLLocationCoordinate2D)];
}
return [MKPolyline polylineWithCoordinates:(CLLocationCoordinate2D *)data.bytes count:data.length / sizeof(CLLocationCoordinate2D)];
}
#end
The other answers here seem to be about using Apple Maps, for using Google Maps I found I had to make some modifications to #SedateAlien's great category.
MODIFIED CATEGORY
+ (GMSPolyline *)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;
}
}
GMSMutablePath *path = [[GMSMutablePath alloc] init];
int i;
for (i = 0; i < coordIdx; i++)
{
[path addCoordinate:coords[i]];
}
GMSPolyline *polyline = [GMSPolyline polylineWithPath:path];
free(coords);
return polyline;
}
USAGE
// Here I make the call to the Google Maps API to get the routes between two points...
....
// Get the encoded array of points.
NSString *points = routes[#"routes"][0][#"overview_polyline"][#"points"];
// Use the modified category to get a polyline from the points.
GMSPolyline *polyline = [GMSPolyline polylineWithEncodedString:points];
// Add the polyline to the map.
polyline.strokeColor = [UIColor redColor];
polyline.strokeWidth = 10.f;
polyline.map = theMapView;
}
If anybody else is trying to do this in swift, here's #RootCode's answer adapted to swift (2.3):
let path = GMSMutablePath()
let steps = directionsToShowOnMap.steps
for (idx, step) in steps.enumerate() {
path.addCoordinate(coordinateFromJson(step["start_location"]))
if let polylinePoints = step["polyline"].string, subpath = GMSPath(fromEncodedPath: polylinePoints) {
for c in 0 ..< subpath.count() {
path.addCoordinate(subpath.coordinateAtIndex(c))
}
}
if idx == steps.count - 1 {
path.addCoordinate(coordinateFromJson(step["end_location"]))
}
}
let polyline = GMSPolyline(path: path)
polyline.strokeColor = UIColor.blueColor()
polyline.strokeWidth = 3
polyline.map = mapView
and then:
private func coordinateFromJson(location: JSON) -> CLLocationCoordinate2D {
return CLLocationCoordinate2DMake(location["lat"].double!, location["lng"].double!)
}

Assigning a button tag from an array which stores integers

I am trying to assign a tag to button. The normal command is:
button.tag = 1;
The tag must be an integer.
My problem is that I would like to assign an integer which I stored in an array (tabReference) which is yet again part of a class (currentNoteBook). So I need this:
int k = 0;
button.tag = [currentNoteBook.tabReference objectAtIndex:k]; // This is where I get the warning.
This doesn't seem to work, however, as xCode tells me: Passing argument 1 of setTag: makes integer from pointer without a cast.
My array looks like this (I tried to use integers...):
NSMutableArray *trArray = [[NSMutableArray alloc] init];
NSNumber *anumber = [NSNumber numberWithInteger:1];
[trArray addObject: anumber];
[trArray addObject: anumber];
[trArray addObject: anumber];
[trArray addObject: anumber];
currentNoteBook.tabReference = trArray;
An NSMutableArray stores a modifiable array of objects. You can't directly store an integer in an NSMutableArray. That's why you have to do something like this to store a bunch of integers:
NSMutableArray *the_array = [[NSMutableArray alloc] init];
int max = 100;
for (int i = 0; i < max; i++)
{
NSNumber *temp_number = [NSNumber numberWithInt:arc4random() % max];
[the_array addObject:temp_number];
}
Of course, you could do pretty much the same thing and store something else in there:
NSMutableArray *the_array = [[NSMutableArray alloc] init];
int max = 100;
int max_x = 50;
int max_y = 25;
int max_w = 100;
int max_h = 200;
for (int i = 0; i < max; i++)
{
CGFloat temp_x = arc4random() % max_x;
CGFloat temp_y = arc4random() % max_y;
CGFloat temp_w = arc4random() % max_w;
CGFloat temp_h = arc4random() % max_h;
CGRect temp_rect = CGRectMake(temp_x, temp_y, temp_w, temp_h);
[the_array addObject:[NSValue valueWithCGRect:temp_rect]];
}
When you go to retrieve these values you need to specify what it is you want out of the array because the same array can contain very different objects.
For your integers:
for (int i = 0; i < max; i++)
{
NSLog(#"%i: %i", i, [[the_array objectAtIndex:i] intValue]);
}
For the CGRect example:
for (int i = 0; i < max; i++)
{
CGRect temp_rect = [[the_array objectAtIndex:i] CGRectValue];
NSLog(#"%i: x:%f y:%f w:%f h:%f", i, temp_rect.origin.x, temp_rect.origin.y, temp_rect.size.width, temp_rect.size.height);
}
In a nutshell, you are storing objects not integers in your code. You have to pull them out of there as objects and then extract your integer to get your data back.
Just found the answer in another question I posed:
it must be:
btn.tag = [[currentNoteBook.tabReference objectAtIndex:k] intValue];

How Can I Edit My Array's For Loop To Reflrect This?

My array's for loop currently results in numbers like: 0,1,2,3,4,5,6 ....to 1000.
Here is the code:
pickerArray = [[NSMutableArray alloc] initWithCapacity:700];
for ( int i = 1 ; i <= 1000 ; i = i++)
[pickerArray addObject:[NSString stringWithFormat:#"%i", i]]
I want it instead, to have the following pattern, 0, 2.5, 5, 7.5, 10, 12.5, 15, 17.5, 20, etc.
And it is displayed using this code:
float weight = [[pickerArray objectAtIndex:row] intValue];
label.text = [NSString stringWithFormat:#"%f lb", weight];
pickerArray = [[NSMutableArray alloc] initWithCapacity:700];
for ( float i = 0.0 ; i <= 1000.0 ; i = i + 2.5)
{
//[pickerArray addObject:[NSString stringWithFormat:#"%.1f", i]]
[pickerArray addObject:[NSNumber numberWithFloat:i];
}
float weight = [[pickerArray objectAtIndex:row] floatValue];
label.text = [NSString stringWithFormat:#"%.1f", weight];
You can do this in the loop:
for ( float f = 0; f <= 1000; f = f + 2.5 )
[pickerArray adObject:[NSString stringWithFormat:#"%.1f", f]];
Or you can just multiply it when creating the strings:
[pickerArray adObject:[NSString stringWithFormat:#"%.1f", 2.5*i]];
Also, if you are immediately putting 1000 objects in, why start with an initial capacity of 700? And are you really sure you want all those strings in this array?
EDIT: and having looked at your edit, are you really sure you want to do this seemingly-redundant conversion back and forth to strings? Well, if you're sure:
float weight = [[pickerArray objectAtIndex:row] floatValue];
Try something like that:
pickerArray = [[NSMutableArray alloc] initWithCapacity:1001];
for ( int i = 0 ; i <= 1000 ; ++i)
[pickerArray addObject:[NSString stringWithFormat:#"%.1f", 2.5*i]]