Google Maps iOS SDK: optimizing finding a random street view - ios

This code finds a random location on Google Street Map according to the bounding box of a country´s latitude/longitude. But it is still even with the bounding box way to slow - it can take up to a minute for a steet view photo to be found. What can I do to make this faster?
The delegated method from GMSPanoramaView checks if there was a valid panorama photo at the random position. if not tells to find a search for a new one.
// Delegate method of GMSPanoramaView that get´s called when didMoveToPanorama: is called
- (void)panoramaView:(GMSPanoramaView *)view didMoveToPanorama:(GMSPanorama *)panorama
nearCoordinate:(CLLocationCoordinate2D)coordinate
{
if (!panorama)
{
[self shuffleLocation];
}
}
- (void)shuffleLocation
{
CLLocationCoordinate2D newLocation = [self randomLatitudeLongitude];
[self.panoView moveNearCoordinate:newLocation];
}
(CLLocationCoordinate2D) randomLatitudeLongitude
{
CountryBBVal auBB = [[GGData SharedInstance] boundingBoxForCountry:Australia];
double ranLongitude = [self randomDoubleBetween: auBB.NELng and: auBB.SWLng]; // Boundix Box
double ranLatitude = [self randomDoubleBetween: auBB.NELat and: auBB.SWLat];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setMaximumFractionDigits:4];
[formatter setDecimalSeparator:#"."];
NSString *formattedNumberLng = [formatter stringFromNumber:#(ranLongitude)];
NSString *formattedNumberLat = [formatter stringFromNumber:#(ranLatitude)];
CLLocationCoordinate2D ranLatLng = CLLocationCoordinate2DMake([formattedNumberLat doubleValue], [formattedNumberLng doubleValue]);
//NSLog(#"ranLatLng: [%f] [%f]", ranLatLng.latitude, ranLatLng.longitude);
return ranLatLng;
}

What you have is a non-deterministic way of finding a photo so you cannot control how long it takes. What you have coded is optimal and there is no performance improvement in code. Only your algorithm to do so is not optimal and you need to think of a better strategy to get to a random photo.

Related

Avoid overlapping of google map marker using clustering

There are thousands of data that I want to add on google map using clustering.
Problem
It is possible that multiple records may have same latitude and longitude so I want to add those marker in such a way that it doesn't overlap one another. They can be close to each other but not overlapped.
So I have went through the google place api documentation and even googled or the various articles but I didn't found a solution that solves this issue.
Setting pin on map:-
- (void)setPinsToMap {
[self.mapGoogle clear];
for (int i=0; i<arrUsers.count; i++) {
ClassUser *cls =[arrUsers objectAtIndex:i];
double lat;
double long1;
//Here, what to do with the location coordinates?
lat = [cls.strLat doubleValue];
long1 = [cls.strLong doubleValue];
NSLog(#"\n------------------\nname = %# lat = %#, lng = %#\n---------------\n", cls.strUserName, cls.strLat, cls.strLong);
NSString *name = [NSString stringWithFormat:#"%d", i];
id<GMUClusterItem> item = [[POIItem1 alloc] initWithPosition:CLLocationCoordinate2DMake(lat, long1) name:name];
[_clusterManager addItem:item];
[_clusterManager cluster];
}
}
Could any one suggest a way to get this fixed. Any help will be welcomed.

How do I test that my NSNumberFormatter is only initialized once?

I created a category for NSDecimalNumber in which I take an NSString and return a NSDecimalNumber. I'm using it in a few of my view controllers and wanted to create a single global instance of NSNumberFormatter. I think this works but I have no idea on how to test it. For example, I want to NSLog every time an NSNumberFormatter instance gets allocated. How do I do that?
#import "NSDecimalNumber+amountFromTextField.h"
#implementation NSDecimalNumber (amountFromTextField)
static NSNumberFormatter *nf;
+(NSDecimalNumber *)amountFromTextField:(NSString *)amount {
#synchronized(self) {
if (nf == nil) {
nf = [[NSNumberFormatter alloc] init];
}
}
NSDecimal _amount = [[nf numberFromString:amount] decimalValue];
return [NSDecimalNumber decimalNumberWithDecimal:_amount];
}
#end
You can extract the formatter allocation into another method, and assert on that:
+ (NSNumberFormatter*)amountFormatter;
- (void)testOnlyOneFormatterIsCreated {
NSNumberFormatter *formatter1 = [NSDecimalNumber amountFormatter];
NSNumberFormatter *formatter2 = [NSDecimalNumber amountFormatter];
XCTAssertEqual(formatter1, formatter2, "Expected only one formatter to be created");
}
The problem with your current implementation is the fact you don't have access to the created formatter, thus it very hard to test it. Splitting object creation and business logic into separate units is also good for your code, as it keeps your units short and focused on one task only.

convert string to core location

I have latitude and longitude values as string and want to convert it to corelocation. My main intension is to calculate the distance between the user's current location (returned by the device) and the location returned from the server.
calculate distance using lat and long.. This post helped me to find distance between two locations.
Which of the following will be the best way?
Should I convert string to core location and calculate distance
or
should i convert the device location to string and calculate distance.
Create and object like so:
CLLocation *locA = [[CLLocation alloc] initWithLatitude:lat1 longitude:long1];
lat1 / long2 are of type CLLocationDegrees which is a typedef of a double, representing the point in degrees.
Convert your lat / long to doubles and pass them in, like so:
CLLocation *locA = [[CLLocation alloc] initWithLatitude:[#"" doubleValue] longitude:[#"" doubleValue] ];
The above code was in the example you linked to, you could have very easily google'd the object and it would have brought you to the doc online.
EDIT:
As per Volker's suggestion, if you are getting numbers from a server, there is a possibility of localisation issues, where some local's use a decimal: 46.000 and others use a comma 46,000.
Something like this would be better:
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setLocale:[NSLocale currentLocale]];
NSNumber *temp = [formatter numberFromString:#""];
[temp doubleValue];

How do I get NSNumber with NSLocale smarts from a UITextField?

Getting correct localized formatting of numbers to display is easy.
But prompting for a decimal number input from a UITextField is proving tricky.
Even though the decimal keypad may present the comma for European usage instead of the stop, the method handling the input apparently still needs to be locale-savvy.
my research here on S.O. and other places suggests
-(IBAction)buttonPress:(UIButton *)sender
{
NSNumber *firstNumber = #([self.firstField.text floatValue]); // 2 alternative ways
NSNumber *secondNumber = [NSNumber numberWithFloat:[self.secondField.text floatValue]];
.
.
only gives me integers to perform arithmetic with. They aren't floats at all. They have no float value
Supposing this is the only thing the app does, and I immediately do:
.
.
NSNumberFormatter *numberFormat = [[ NSNumberFormatter alloc] init];
[ numberFormat setNumberStyle:NSNumberFormatterDecimalStyle];
[ numberFormat setLocale:[NSLocale currentLocale]];
[ numberFormat setMaximumFractionDigits:2];
NSString *displayString = [ numberFormat stringFromNumber:firstNumber];
self.resultLabel.text = displayString;
}
I am throwing the input back to a label in the ViewController without any further handling to make sure the the decimals aren't hidden by not having a formatter. No Joy. Still in integers.
Obviously the app has to do something in the way of calculation, and since one can't handle NSNumber wrappers directly, I have to resolve it before i do the conversion thing:
double xD = [firstNumber doubleValue];
double yD = [secondNumber doubleValue];
the question is, where is the decimal-ness of the input being lost?
I have no problem with the desktop keyboard and the simulator, but if i set a test device to - say - Polish then that simply won't do.
EDIT:
and here's the result from the answer provided below
// run these 4 methods first, that way you can re-use *numberFormat for the display
NSNumberFormatter *numberFormat = [[ NSNumberFormatter alloc] init];
[ numberFormat setNumberStyle:NSNumberFormatterDecimalStyle];
[ numberFormat setLocale:[NSLocale currentLocale]];
[ numberFormat setMaximumFractionDigits:2];
// here 'tis
NSNumber *firstNumber = [numberFormat numberFromString:self.firstField.text];
// do the same for other input UITextFields
// sh-boom. there you go
You need use longLongValue not longValue to convert NSString to long type. or use NSNumberFormatter->numberFromString:

How to validate latitude and longitude

I have two UITextFields which users can enter in a latitude and longitude, these co-ordinates are then used to create a pin on an MKMapView.
I want find a way to validate whether the values they enter are actual GPS co-ordinates or just a load of rubbish. Is there any way of doing this?
The latitude must be a number between -90 and 90 and the longitude between -180 and 180.
Here are the functions to validate it in JavaScript.
Latitude must be a number between -90 and 90
const isLatitude = num => isFinite(num) && Math.abs(num) <= 90;
Longitude must a number between -180 and 180
const isLongitude = num => isFinite(num) && Math.abs(num) <= 180;
In Kotlin we can do something like this:
fun isValidLatLang(latitude: Double?, longitude: Double?): Boolean {
return latitude?.toInt() in -90 until 90 && longitude?.toInt() in -180 until 180
}
Using follow latitude and longitude regular expressions, we can validate.
With escape characters in Objective-C:
Latitude RegEx:
#"^(\\+|-)?((\\d((\\.)|\\.\\d{1,6})?)|(0*?[0-8]\\d((\\.)|\\.\\d{1,6})?)|(0*?90((\\.)|\\.0{1,6})?))$"
Longitude RegEx:
#"^(\\+|-)?((\\d((\\.)|\\.\\d{1,6})?)|(0*?\\d\\d((\\.)|\\.\\d{1,6})?)|(0*?1[0-7]\\d((\\.)|\\.\\d{1,6})?)|(0*?180((\\.)|\\.0{1,6})?))$"
Normal Regular expressions for Both latitude & longitude:
Latitude RegEx:
^(\+|-)?((\d((\.)|\.\d{1,6})?)|(0*?[0-8]\d((\.)|\.\d{1,6})?)|(0*?90((\.)|\.0{1,6})?))$
Longitude RegEx:
^(\+|-)?((\d((\.)|\.\d{1,6})?)|(0*?\d\d((\.)|\.\d{1,6})?)|(0*?1[0-7]\d((\.)|\.\d{1,6})?)|(0*?180((\.)|\.0{1,6})?))$
I'd do something like this
numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *latitude = [numberFormatter numberFromString:theInputString];
if((latitude != nil)
{
//check it is within lat/long range
} else {
//not even a valid number, reject it
}
CLLocationCoordinate2D p1;
p1.latitude = [[punto latitud] doubleValue];
p1.longitude = [[punto longitud] doubleValue];
if (CLLocationCoordinate2DIsValid(p1))
{
[Mapa addAnnotation:annotationPoint];
}
After going through a lot of StackOverflow questions, I thought this question was asked in a simple and straightforward manner which described what I was looking for in solving my latitude/longitude validation for AMZO:A Global Map Based Station for Reporting Aliens, Monsters, Zombies and Other Interesting Events (iPhone/iPad app). Shameless, I know, but I think I deserve it for coming up with a complete and elegant answer/solution (adapting Craig's brief answer above)!
I am using the new AlertController which calls each of the following validations for latitude and longitude text inputs.
- (BOOL) validateInput1: (NSString *) latitude
{
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *latitude1 = [numberFormatter numberFromString:latitude];
if (latitude1 != nil)
{
//check it is within lat/long range
if ((latitude1.floatValue > -90.0) && (latitude1.floatValue < 90.0)) {
NSLog(#"Hello Latitude!!!");
return 1;
}
} else {
//not even a valid number, reject it
return 0;
}
return 0;
}
- (BOOL) validateInput2: (NSString *) longitude
{
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *longitude1 = [numberFormatter numberFromString:longitude];
if (longitude1 != nil)
{
//check it is within lat/long range
if ((longitude1.floatValue > -180.0) && (longitude1.floatValue < 180.0)) {
NSLog(#"Hello Longitude!!!");
return 1;
}
} else {
//not even a valid number, reject it
return 0;
}
return 0;
}

Resources