How to validate latitude and longitude - geolocation

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;
}

Related

Float value resets after Adding

I start off with a number, lets say 250. I add all sorts of numbers, but anytime I add a high number like 2,000 it adds correctly. Then I add 3. The new number comes out to 5 like it thought 2,000 was 2.0. I do not know why it is doing this.
float start = self.amountLabel.text.floatValue;
float changeAmount = self.amountField.text.floatValue;
float newValue;
if (determConfirm == 1) {
newValue = start + changeAmount;
} else {
newValue = start - changeAmount;
}
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setMaximumFractionDigits:5];
[formatter setRoundingMode: NSNumberFormatterRoundUp];
NSString *numberString = [formatter stringFromNumber:[NSNumber numberWithFloat:newValue]];
[[NSUserDefaults standardUserDefaults] setValue:numberString forKey:#"newValue"];
[[NSUserDefaults standardUserDefaults] synchronize];
self.amountLabel.text = numberString;
self.amountField.text = #"0.00";
[self.amountField resignFirstResponder];
The problem is the use of floatValue to convert the formatted number text to a number. floatValue only works as expected if the text is unformatted (no commas) and uses the period for the decimal separator.
Since you store a formatted number in the field, it only works with small numbers and in certain locales.
Replace your use of floatValue on the text with the same NSNumberFormatter used to format the number. Use it to parse the text and give you an NSNumber (which you can then call floatValue on).
Just a guess as it's hard by looking at your code, but maybe you want this:
float newValue = start;
if (determConfirm == 1) {
newValue += changeAmount;
} else {
newValue -= changeAmount;
}

Google Maps iOS SDK: optimizing finding a random street view

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.

Order results of CLGeoCoder geocodeAddressString by nearest location

I'm sure that I can figure this out based on code that I have implemented in PHP in the past, but I was hoping that someone has either done this and can provide an example or knows of a way to use the iOS SDK to accomplish it.
Here's my relevant code:
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
CLLocationDistance dist = _searchDistance;
CLLocationCoordinate2D point = self.locationManager.location.coordinate;
CLRegion *region = [[CLRegion alloc] initCircularRegionWithCenter:point radius:dist identifier:#"Hint Region"];
[geocoder geocodeAddressString:address inRegion:region completionHandler:^(NSArray *placemarks, NSError *error)
{
if( error )
{
// Push errors to the user
}
else
{
NSLog( [NSString stringWithFormat:#"Got results: %#", placemarks] );
}
}];
This pushes to the log something like the following:
(
"300 Oak St, Phoenix, OR 97535-5722, United States # <+42.26953820,-122.81554259> +/- 100.00m, region (identifier <+42.26953820,-122.81554106> radius 27.00) <+42.26953820,-122.81554106> radius 27.00m",
"300 Oak St, Ashland, OR 97520, United States # <+42.19955633,-122.71289484> +/- 100.00m, region (identifier <+42.19955633,-122.71289484> radius 138.42) <+42.19955633,-122.71289484> radius 138.42m",
"300 Oak St, Jacksonville, OR 97530, United States # <+42.31236366,-122.97179130> +/- 100.00m, region (identifier <+42.31236366,-122.97179130> radius 138.33) <+42.31236366,-122.97179130> radius 138.33m",
"300 Oak St, Central Point, OR 97502, United States # <+42.37422514,-122.91427182> +/- 100.00m, region (identifier <+42.37422514,-122.91427182> radius 138.29) <+42.37422514,-122.91427182> radius 138.29m",
"300 Oak St, Rogue River, OR 97537, United States # <+42.43621216,-123.16864522> +/- 100.00m, region (identifier <+42.43621216,-123.16864522> radius 138.24) <+42.43621216,-123.16864522> radius 138.24m"
)
In this case, the second result is the closest to my current location. I would like to have this list ordered by matches that are closest to my current location. I'm reasonably confident that I know how to do the math to figure out which location is closest, but I was hoping for a shortcut. Iterating through this array and sorting the results is going to be somewhat heavy, so I'd like to avoid that if possible.
EDIT (complete answer):
#Akash gave me the bit of information I was seeking, so I marked that answer as the correct one. If anyone is interested in sorting the results (rather than just getting the closest location), I put the unsorted results into an NSDictionary using the distance as a key, then used compare to sort the keys and then put the original placemark array elements into an output array ordered by the key to which they were associated in the NSDictionary. Here's the complete code (and the complete answer to my question):
-(NSMutableArray *)distanceSortPlacemarks:(NSArray *)inputArray fromLocation:(CLLocation *)originLocation
{
NSMutableArray *returnValue = [[NSMutableArray alloc] initWithCapacity:[inputArray count]];
NSMutableDictionary *sortDictionary = [[NSMutableDictionary alloc] initWithCapacity:[inputArray count]];
for( int i = 0; i < [inputArray count]; i++ )
{
CLPlacemark *currentPlacemark = [inputArray objectAtIndex:i];
[sortDictionary
setObject:currentPlacemark
forKey:[NSNumber numberWithDouble:[currentPlacemark.location distanceFromLocation:originLocation]]
];
}
NSArray *sortedKeys = [[sortDictionary allKeys] sortedArrayUsingSelector:#selector(compare:)];
for( int i = 0; i < [sortedKeys count]; i++ )
{
CLPlacemark *currentPlacemark = [sortDictionary objectForKey:[sortedKeys objectAtIndex:i]];
[returnValue insertObject:currentPlacemark atIndex:i];
}
return returnValue;
}
I know this could be a little tighter (some of the variables aren't absolutely necessary), but I tend to err on the side of making code easier to understand at quick glance.
Try getting distance of your current location from CLLocation in each CLPlacemark as follows :
for(int i = 0; i < placemarks.count; i++)
{
double distance = [placemarks[i].location distanceFromLocation:yourCurrentLocation];
if(i == 0)
{
indexOfClosestLocation = i;
distanceFromClosestLocation = distance;
}
else
{
if(distance < distanceFromClosestLocation)
{
distanceFromClosestLocation = distance;
indexOfClosestLocation = i;
}
}
}
After executing this code you have index of closest CLLocation in placemarks array and distance to it in meters.

Stop Float value rounding off in objective c

I have to prevent the value from rounding off after the decimal.
Here is the code I use :
NSNumberFormatter* nf = [[[NSNumberFormatter alloc] init] autorelease];
nf.positiveFormat = #"0.###";
NSString* trimmedValue = [nf stringFromNumber: [NSNumber numberWithFloat:[exRateLabel doubleValue]*amount]];
trimmedValue = [trimmedValue substringToIndex:[trimmedValue length]-1];
In this case, if I multiply 1000 * 50.1234 I'm getting 50123.3984, but it should be 50123.4.
NSLog(#".2f",50.1234*1000);
For this case it's showing the correct value but for
NSLog(#".2f",50.1234*123);
it is rounding off the actual value, which is 6165.1782, to 6165.18.
Just use double instead of float and a proper rounding rule:
NSNumberFormatter* nf = [[NSNumberFormatter alloc] init];
nf.positiveFormat = #"0.###";
nf.roundingMode = NSNumberFormatterRoundFloor;
NSString* trimmedValue = [nf stringFromNumber: [NSNumber numberWithDouble:50.1234*123]];
NSLog(#"trimmedValue: %#", trimmedValue);
And the result is:
trimmedValue: 6165.178
If the mathematics is required to be precise, I'd suggest using an NSDecimalNumber.
NSDecimalNumber *myNumber = [[NSDecimalNumber alloc] initWithMantissa:501234 exponent:-4 isNegative:NO]];
NSDecimalNumber *answer = [self multiplyDecimalNumber:myNumber with:[NSDecimalNumber decimalNumberWithMantissa:1000 exponent:0 isNegative:NO];
NSLog(#"Answer: %g", floor([answer doubleValue]));
I made a quick wrapper for the multiplication, I never wanted an exception, your needs may be different:
-(NSDecimalNumber *) multiplyDecimalNumber:(NSDecimalNumber *) lhs with:(NSDecimalNumber *) rhs {
NSDecimalNumberHandler *handler = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:NSDecimalNoScale raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
return [lhs decimalNumberByMultiplyingBy:rhs withBehavior:handler];
}
They are both correct.
First, I believe you have a typo - you're missing the %.
NSLog(#"%.2f", 50.1234*1000); // same as #"%.2f", 50,123.4 = #"50123.4"
the math leaves you with a clean 1/10th, even though you are asking for 2 decimal places to be printed.
NSLog(#"%.2f",50.1234*123); // same as #"%.2f", 5,161.1782 = #"5161.18"
you are asking for two decimal numbers and rounding up is the default behavior.
It sounds like the formatting you actually want to use is:
NSLog(#"%.1f", number);
or if you want a forced, zero-padded two digits, use
NSLog(#"%.02", number); // first case would come out #"50123.40"
this will force all trailing zeros to be printed
you are probably using a 'double' representation, cast the number to a float or you can use your own policy for truncation using functions like:
float roundUp (float value, int digits) {
int mod = pow(10, digits);
float roundedUp = value;
if (mod != 0) {
roundedUp = ceilf(value * mod) / mod;
}
return roundedUp;
}
float roundDown (float value, int digits) {
int mod = pow(10, digits);
float roundedDown = value;
if (mod != 0) {
roundedDown = floorf(value * mod) / mod;
}
return roundedDown;
}
float nearestf (float value, int digits) {
int mod = pow(10, digits);
float nearest = value;
if (mod != 0) {
nearest = floorf(value*mod + 0.5) / mod;
}
return nearest;
}
'roundUp' should be the one you need, or try with the more generic 'nearestf'
For me it was requirement to show atleast two decimal and maximum decimal number.
Following code did worked for me.
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[numberFormatter setMaximumFractionDigits:2];
[numberFormatter setMinimumFractionDigits:2];
[numberFormatter setRoundingMode:NSNumberFormatterRoundDown];
NSString *num = #"123456.019";
NSString *stringFromNumber = [numberFormatter stringFromNumber:[numberFormatter numberFromString:num]];
NSLog(#"check this %#", stringFromNumber);

Convert String to Double with Currency

NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle: NSNumberFormatterCurrencyStyle];
double value = [[numberFormatter numberFromString: ProviderTripRate.text] doubleValue];
[User setDouble:value forKey:#"Trip_Rate"];
NSLog(#" %# %f " , ProviderTripRate.text , value );
If the data inside ProviderTripRate.text is 2.75 then value is 0 wrong
If the data inside ProviderTripRate.text is $2.75 then value is 2.75 correct
How do you reliably convert from the string to a double value
NOTE that the text string may or may NOT have the local currency symbol ($ as shown above)
but it should always convert
Thanks in advance
Try this:
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle: NSNumberFormatterCurrencyStyle];
NSNumber* number = [numberFormatter numberFromString:ProviderTripRate.text]
if (!number) {
[numberFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
number = [numberFormatter numberFromString:ProviderTripRate.text];
}
double value = [number doubleValue];
Try this:
if ([providerTripRate.text hasPrefix:#"$"]) {
// use NSNumberFormatter to obtain the value
} else {
float theValue = [providerTripRate.text doubleValue];
}
etc.
extension String {
/// Creates a Double from the string
///
/// Tries various ways of extracting a number, currently handling the following formats
/// - 1000
/// - 1000.00
/// - 1,000.00
/// - $1,000.00
///
/// - Returns: Double value, or 0.0 if a non numerical String is passed
func asDouble() -> Double {
if let double = Double(self) {
return double
}
let formatter = NumberFormatter()
if let number = formatter.number(from: self) {
return number.doubleValue
}
formatter.numberStyle = .currency
if let number = formatter.number(from: self) {
return number.doubleValue
}
formatter.currencySymbol = ""
formatter.internationalCurrencySymbol = ""
if let number = formatter.number(from: self) {
return number.doubleValue
}
return 0
}
}

Resources