I am writing GPS coordinates to my JPEG image, and the coordinates are correct (as demonstrated by my logcat output) but it appears that it's being corrupted somehow. Reading the exif data results in either null values or, in the case of my GPS: 512.976698 degrees, 512.976698 degrees. Can anyone shed some light on this problem?
writing it:
try {
ExifInterface exif = new ExifInterface(filename);
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, latitude);
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, longitude);
exif.saveAttributes();
Log.e("LATITUDE: ", latitude);
Log.e("LONGITUDE: ", longitude);
} catch (IOException e) {
e.printStackTrace();
}
and reading it:
try {
ExifInterface exif = new ExifInterface("/sdcard/globetrotter/mytags/"+ TAGS[position]);
Log.e("LATITUDE EXTRACTED", exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE));
Log.e("LONGITUDE EXTRACTED", exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE));
} catch (IOException e) {
e.printStackTrace();
}
It goes in (for example) 37.715183, -117.260489 and comes out 33619970/65540, 14811136/3368550, 33619970/65540, 14811136/3368550. Am I doing it wrong?
EDIT:
So, the problem is I am not encoding it in the properly defined format, which is something like you see here:
Can anyone explain what this format is? Obviously the first number is 22/1 = 22 degrees, but I can't figure out how to compute the decimal there.
GPSLatitude
Indicates the latitude. The latitude is expressed as three
RATIONAL values giving the degrees,
minutes, and seconds, respectively.
If latitude is expressed as degrees,
minutes and seconds, a typical format
would be dd/1,mm/1,ss/1. When degrees
and minutes are used and, for
example, fractions of minutes are
given up to two decimal places, the
format would be dd/1,mmmm/100,0/1.
https://docs.google.com/viewer?url=http%3A%2F%2Fwww.exif.org%2FExif2-2.PDF
The Android docs specify this without explanation: http://developer.android.com/reference/android/media/ExifInterface.html#TAG_GPS_LATITUDE
Exif data is standardized, and GPS data must be encoded using geographical coordinates (minutes, seconds, etc) described above instead of a fraction. Unless it's encoded in that format in the exif tag, it won't stick.
How to encode: http://en.wikipedia.org/wiki/Geographic_coordinate_conversion
How to decode: http://android-er.blogspot.com/2010/01/convert-exif-gps-info-to-degree-format.html
Here is some code I've done to geotag my pictures. It's not heavily tested yet, but it seems to be ok (JOSM editor and exiftool read location).
ExifInterface exif = new ExifInterface(filePath.getAbsolutePath());
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, GPS.convert(latitude));
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, GPS.latitudeRef(latitude));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, GPS.convert(longitude));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, GPS.longitudeRef(longitude));
exif.saveAttributes();
And class GPS is here. (method could be shorter, but it's readable at least)
/*
* #author fabien
*/
public class GPS {
private static StringBuilder sb = new StringBuilder(20);
/**
* returns ref for latitude which is S or N.
* #param latitude
* #return S or N
*/
public static String latitudeRef(double latitude) {
return latitude<0.0d?"S":"N";
}
/**
* returns ref for latitude which is S or N.
* #param latitude
* #return S or N
*/
public static String longitudeRef(double longitude) {
return longitude<0.0d?"W":"E";
}
/**
* convert latitude into DMS (degree minute second) format. For instance<br/>
* -79.948862 becomes<br/>
* 79/1,56/1,55903/1000<br/>
* It works for latitude and longitude<br/>
* #param latitude could be longitude.
* #return
*/
synchronized public static final String convert(double latitude) {
latitude=Math.abs(latitude);
int degree = (int) latitude;
latitude *= 60;
latitude -= (degree * 60.0d);
int minute = (int) latitude;
latitude *= 60;
latitude -= (minute * 60.0d);
int second = (int) (latitude*1000.0d);
sb.setLength(0);
sb.append(degree);
sb.append("/1,");
sb.append(minute);
sb.append("/1,");
sb.append(second);
sb.append("/1000");
return sb.toString();
}
}
Other answers delivered nice background info and even an example. This is not a direct answer to the question but I would like to add an even simpler example without the need to do any math. The Location class delivers a nice convert function:
public String getLonGeoCoordinates(Location location) {
if (location == null) return "0/1,0/1,0/1000";
// You can adapt this to latitude very easily by passing location.getLatitude()
String[] degMinSec = Location.convert(location.getLongitude(), Location.FORMAT_SECONDS).split(":");
return degMinSec[0] + "/1," + degMinSec[1] + "/1," + degMinSec[2] + "/1000";
}
I stored the return value in my image and the tag is parsed fine. You can check your image and the geocoordinates inside here: http://regex.info/exif.cgi
Edit
#ratanas comment translated to code:
public boolean storeGeoCoordsToImage(File imagePath, Location location) {
// Avoid NullPointer
if (imagePath == null || location == null) return false;
// If we use Location.convert(), we do not have to worry about absolute values.
try {
// c&p and adapted from #Fabyen (sorry for being lazy)
ExifInterface exif = new ExifInterface(imagePath.getAbsolutePath());
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, getLatGeoCoordinates(location));
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, location.getLatitude() < 0 ? "S" : "N");
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, getLonGeoCoordinates(location));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, location.getLongitude() < 0 ? "W" : "E");
exif.saveAttributes();
} catch (IOException e) {
// do something
return false;
}
// Data was likely written. For sure no NullPointer.
return true;
}
Here are some nice LatLong converter: latlong.net
ExifInterface exif = new ExifInterface(compressedImage.getPath());
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE,gpsTracker.dec2DMS(gpsTracker.getLatitude()));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE,gpsTracker.dec2DMS(gpsTracker.getLongitude()));
Convertor double to String
String dec2DMS(double coord) {
coord = coord > 0 ? coord : -coord;
String sOut = Integer.toString((int)coord) + "/1,";
coord = (coord % 1) * 60;
sOut = sOut + Integer.toString((int)coord) + "/1,";
coord = (coord % 1) * 60000;
sOut = sOut + Integer.toString((int)coord) + "/1000";
return sOut;
}
The most modern and shortest solution (with AndroidX) is using ExifInterface.setGpsInfo(Location), for example:
ExifInterface exif = new ExifInterface(filename);
Location location = new Location(""); //may be empty
location.setLatitude(latitude); //double value
location.setLongitude(longitude); //double value
exif.setGpsInfo(location)
exif.saveAttributes();
Sources: one and two
check android source code: https://android.googlesource.com/platform/frameworks/base/+/android-4.4.2_r2/core/java/android/hardware/Camera.java
/**
* Sets GPS longitude coordinate. This will be stored in JPEG EXIF
* header.
*
* #param longitude GPS longitude coordinate.
*/
public void setGpsLongitude(double longitude) {
set(KEY_GPS_LONGITUDE, Double.toString(longitude));
}
So it's a direct print, my log supports it as well: ExifInterface.TAG_GPS_LONGITUDE : -121.0553966
My conclusion is setting it as direct print is fine.
Related
DIRECTION_URL_API = "https://maps.googleapis.com/maps/api/directions/json?"
DIRECTION_URL_API + "origin=" + origin + "&destination=" + destination + "&sensor=true" + "&mode=" +typeOpt+"&key=" + GOOGLE_API_KEY ;
I am using this format but its not working
Please suggest me :)
You can find distance following way
http://maps.googleapis.com/maps/api/directions/json?origin=21.1702,72.8311&destination=21.7051,72.9959&sensor=false&units=metric&mode=driving
origin=lat1,long1
destination=lat2,long2
Please use the below method to calculate the distance between two points
/**
* Returns Distance in kilometers (km)
*/
public static String distance(double startLat, double startLong, double endLat, double endLong) {
Location startPoint = new Location("locationA");
startPoint.setLatitude(startLat);
startPoint.setLongitude(startLong);
Location endPoint = new Location("locationA");
endPoint.setLatitude(endLat);
endPoint.setLongitude(endLong);
return String.format("%.2f", startPoint.distanceTo(endPoint) / 1000); //KMs
}
Method usage -
String mDistance = distance(startLat,
startLong,
endLat,endLng)).concat("km");
If I know the degress, minutes, and seconds of a location, how do I convert them to a valid location for ExifInterface.TAG_GPS_LATITUDE and ExifInterface.TAG_GPS_LONGITUDE?
I found following: https://developer.android.com/reference/android/media/ExifInterface.html#TAG_GPS_LATITUDE
But I'm not sure if I understand the format correctly. There is written following:
String. Format is "num1/denom1,num2/denom2,num3/denom3".
I'm not sure which fractions to use for each values... Always 1? Like in following code example:
String exifLatitude1 = degress+ "/1," + minutes + "/1," + seconds + "/1";
I often see strings with /1000 for the seconds, so I'm not sure if following is correct instead of the example above:
String exifLatitude2 = degress+ "/1," + minutes + "/1," + seconds + "/1000";
Can anyone tell me, which one is correct?
My working solution uses milliseconds/1000
-79.948862 becomes
-79 degrees, 56 minutes, 55903 millisecs (equals 55.903 seconds)
79/1,56/1,55903/1000
I have never checked if 79/1,56/1,56/1 would be ok, too.
I am using this code:
from https://github.com/k3b/APhotoManager/blob/FDroid/app/src/main/java/de/k3b/android/util/ExifGps.java
public static boolean saveLatLon(File filePath, double latitude, double longitude) {
exif = new ExifInterface(filePath.getAbsolutePath());
debugExif(sb, "old", exif, filePath);
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, convert(latitude));
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, latitudeRef(latitude));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, convert(longitude));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, longitudeRef(longitude));
exif.saveAttributes();
}
/**
* convert latitude into DMS (degree minute second) format. For instance<br/>
* -79.948862 becomes<br/>
* 79/1,56/1,55903/1000<br/>
* It works for latitude and longitude<br/>
* #param latitude could be longitude.
* #return
*/
private static final String convert(double latitude) {
latitude=Math.abs(latitude);
int degree = (int) latitude;
latitude *= 60;
latitude -= (degree * 60.0d);
int minute = (int) latitude;
latitude *= 60;
latitude -= (minute * 60.0d);
int second = (int) (latitude*1000.0d);
StringBuilder sb = new StringBuilder(20);
sb.append(degree);
sb.append("/1,");
sb.append(minute);
sb.append("/1,");
sb.append(second);
sb.append("/1000");
return sb.toString();
}
private static StringBuilder createDebugStringBuilder(File filePath) {
return new StringBuilder("Set Exif to file='").append(filePath.getAbsolutePath()).append("'\n\t");
}
private static String latitudeRef(double latitude) {
return latitude<0.0d?"S":"N";
}
My goal is to do autocomplete prediction using Google Places API, and now I want to make some kind algorithm that will take current location lat and lng, and make a prediction of places only in 100-200 km diameter.
So, at this moment I get user's current location lat and lng, how to set 100-200 km?
private void getCurrentLocation() {
mLastLocation = LocationServices.FusedLocationApi
.getLastLocation(mGoogleApiClient);
if (mLastLocation != null) {
double latitude = mLastLocation.getLatitude();
double longitude = mLastLocation.getLongitude();
mLatLonBounds = new LatLngBounds(new LatLng(latitude,longitude),
new LatLng(latitude,longitude));
Log.d("myTag","lat = "+mLatLonBounds.northeast.latitude+" ,lon = "+mLatLonBounds.northeast.longitude);
//Log.d("myTag","lat = "+mLatLonBounds.southwest.latitude+" ,lon = "+mLatLonBounds.southwest.longitude);
}else {
//some code
}
}
Here is how I set bounds to auto prediction:
#Nullable
private ArrayList<AutoCompletePlace> getAutocomplete(CharSequence constraint) {
if (mGoogleApiClient.isConnected()) {
Log.i(Constants.AUTO_COMPLETE_TAG, "Starting autocomplete query for: " + constraint);
// Submit the query to the autocomplete API and retrieve a PendingResult that will
// contain the results when the query completes.
PendingResult<AutocompletePredictionBuffer> results = Places.GeoDataApi
.getAutocompletePredictions(mGoogleApiClient, constraint.toString(),
**mBounds**, mPlaceFilter);
// This method should have been called off the main UI thread. Block and wait for at most 60s
// for a result from the API.
AutocompletePredictionBuffer autocompletePredictions = results.await(60, TimeUnit.SECONDS);
// Confirm that the query completed successfully, otherwise return null
final Status status = autocompletePredictions.getStatus();
if (!status.isSuccess()) {
Toast.makeText(getContext(), "Error contacting API: " + status.toString(),
Toast.LENGTH_SHORT).show();
Log.e(Constants.AUTO_COMPLETE_TAG, "Error getting autocomplete prediction API call: " + status.toString());
autocompletePredictions.release();
return null;
}
Log.i(Constants.AUTO_COMPLETE_TAG, "Query completed. Received " + autocompletePredictions.getCount()
+ " predictions.");
// Copy the results into our own data structure, because we can't hold onto the buffer.
// AutocompletePrediction objects encapsulate the API response (place ID and description).
Iterator<AutocompletePrediction> iterator = autocompletePredictions.iterator();
ArrayList resultList = new ArrayList<>(autocompletePredictions.getCount());
while (iterator.hasNext()) {
AutocompletePrediction prediction = iterator.next();
// Get the details of this prediction and copy it into a new PlaceAutocomplete object.
resultList.add(new AutoCompletePlace(prediction.getPlaceId(),
prediction.getDescription()));
}
// Release the buffer now that all data has been copied.
autocompletePredictions.release();
return resultList;
}
Log.e(Constants.AUTO_COMPLETE_TAG, "Google API client is not connected for autocomplete query.");
return null;
Example my current location 48.6180288,22.2984587.
UPDATE: Before the Francois Wouts give me the answer, I found another solution on stackoverflow, you can use it too.
public static final LatLngBounds setBounds(Location location, int mDistanceInMeters ){
double latRadian = Math.toRadians(location.getLatitude());
double degLatKm = 110.574235;
double degLongKm = 110.572833 * Math.cos(latRadian);
double deltaLat = mDistanceInMeters / 1000.0 / degLatKm;
double deltaLong = mDistanceInMeters / 1000.0 / degLongKm;
double minLat = location.getLatitude() - deltaLat;
double minLong = location.getLongitude() - deltaLong;
double maxLat = location.getLatitude() + deltaLat;
double maxLong = location.getLongitude() + deltaLong;
Log.d("Location", "Min: " + Double.toString(minLat) + "," + Double.toString(minLong));
Log.d("Location","Max: "+Double.toString(maxLat)+","+Double.toString(maxLong));
// Set up the adapter that will retrieve suggestions from the Places Geo Data API that cover
// the entire world.
return new LatLngBounds(new LatLng(minLat,minLong),new LatLng(maxLat,maxLong));
According to Wikipedia, you probably want to allow around 1 degree in each direction around the user's location to cover 100-200km. The exact area covered will depend on where the user is, but this should be a good enough approximation for most use cases.
Try the following, for example:
double radiusDegrees = 1.0;
LatLng center = /* the user's location */;
LatLng northEast = new LatLng(center.latitude + radiusDegrees, center.longitude + radiusDegrees);
LatLng southWest = new LatLng(center.latitude - radiusDegrees, center.longitude - radiusDegrees);
LatLngBounds bounds = LatLngBounds.builder()
.include(northEast)
.include(southWest)
.build();
I believe this should work correctly even across the antemeridian. Let me know how you go!
I trying to match hardcoded latitude an longitude with dynamic latitude and longitude, but its not showing correct output, can anyone help me to sort out this error
My code is
String Log = "-122.084095";
String Lat = "37.422005";
try {
if ((Lat.equals(latitude)) && (Log.equals(longitude))) {
AudioManager audiM = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
audiM.setRingerMode(AudioManager.RINGER_MODE_SILENT);
Toast.makeText(getApplicationContext(),
"You are at home",
Toast.LENGTH_LONG).show();
} else {
AudioManager auMa = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
auMa.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
Toast.makeText(getApplicationContext(),
"You are at office ", Toast.LENGTH_LONG)
.show();
}
} catch (Exception e) {
e.printStackTrace();
}
it always goes for else part...
You don't want to use a String comparison here as you can't guarantee the level of accuracy with the real-time location.
The best way to handle this would be to determine the distance between the points and then determine if it's close enough for you to consider, approx, the same.
For this, we use distanceBetween or distanceTo
Docs are here and here
Examples can be found here. Here's one of those examples:
Location locationA = new Location("point A");
locationA.setLatitude(pointA.getLatitudeE6() / 1E6);
locationA.setLongitude(pointA.getLongitudeE6() / 1E6);
Location locationB = new Location("point B");
locationB.setLatitude(pointB.getLatitudeE6() / 1E6);
locationB.setLongitude(pointB.getLongitudeE6() / 1E6);
double distance = locationA.distanceTo(locationB);
The latitude and longitude are variables which vary from point to point, matter of fact they keep on changing while standing on the same spot, because it is not precise.
Instead of comparing the Strings, take a rounded value of the lat and long (in long or float ) and check those values within a certain range. That will help you out with the "Home" and "Office " thing.
For e.g :
String Log = "22.084095";
String Lat = "37.422005";
double lng=Double.parseDouble(Log);
double lat=Double.parseDouble(Lat);
double upprLogHome=22.1;
double lwrLogHome=21.9;
double upprLatHome=37.5;
double lwrLatHome=37.3;
// double upprLogOfc=;
// double lwrLogOfc=;
// double upprLatOfc=;
// double lwrLatOfc=;
if(lng<upprLogHome && lng>lwrLogHome && lat<upprLatHome &&lat>lwrLatHome )
{
System.out.println("You are Home");
}
/* else if(lng<upprLogOfc && lng>lwrLogOfc && lat<upprLatOfc &&lat>lwrLatOfc )
{
System.out.println("You are Home");
}*/
else
System.out.println("You are neither Home nor Ofc");
But for the negative lat long you have to reverse the process of checking.
your matching is okay but you probably should not check for a gps location like this.
You should convert the location to something where you can check that you are in 10m radius of the location.
A nicer way would be to leave the long/lat as doubles and compare the numbers.
if(lat > HOME_LAT - 0.1 && lat < HOME_LAT + 0.1 && ...same for lon... ){}
Try this,
Use google map api to pass lat and long value you will get formatted address. And also pass dynamic lat and lng value same google api you will get formatted address. And then match two formatted address you will get result. i suggest this way you can try this
Use this google api. http://maps.googleapis.com/maps/api/geocode/json?latlng=11.029494,76.954422&sensor=true
Reena, its very easy, Check out below code. You need to use "equalsIgnoreCase()" instead of
"equals".
if ((Lat.equalsIgnoreCase(latitude)) && (Log.equalsIgnoreCase(longitude))) {
should work
Example below :
// Demonstrate equals() and equalsIgnoreCase().
class equalsDemo {
public static void main(String args[]) {
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Good-bye";
String s4 = "HELLO";
System.out.println(s1 + " equals " + s2 + " -> " +
s1.equals(s2));
System.out.println(s1 + " equals " + s3 + " -> " +
s1.equals(s3));
System.out.println(s1 + " equals " + s4 + " -> " +
s1.equals(s4));
System.out.println(s1 + " equalsIgnoreCase " + s4 + " -> " +
s1.equalsIgnoreCase(s4));
}
}
You can print dynamice Latitute and Longitute to Logcat and check with hardcoded Latitute and Longitute
I'm trying to find the location (lat/long) of a street address w/o city - such as "123 Main St." - closest to the current location. This functionality is built into the Google Maps app as well as the iOS maps api, so it's surprising to find it missing for Android - e.g. calling Geocoder.getFromLocation() and have the platform insert a reference point. I have tried several solutions, the following is the best, but still feels inferior.
I make calls to Geocoder.getFromLocationName() with lower-left and upper-right coord. Calls are made beginning with a 10kmx10km area around the current location, and are repeated (30x30, 100x100, and then without the bounding box parameters)until some Addresses are returned. When multiple addresses are returned, the closest is calculated and used:
UPDATE: This approach seemed like it would be inefficient for easily found addresses outside the bounds. E.g. "New york, NY" or "Boston" searched from the west coast - requiring 3 bounded and 1 unbounded call to Geocoder.getFromLocation(). However, unexpectidly, the correct lat/lng is returned for NYC and Boston, on the first call, with tightest bounds here in CA. Google is being smart and ignoring the bounds for us. This may cause problems for some, but it is great for this approach.
package com.puurbuy.android;
import java.io.IOException;
import java.util.List;
import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.AsyncTask;
import android.util.Log;
public class GeocoderRunner extends AsyncTask<String, Void, Address> {
final static double LON_DEG_PER_KM = 0.012682308180089;
final static double LAT_DEG_PER_KM =0.009009009009009;
final static double[] SEARCH_RANGES = {10, 50,800,-1}; //city, region, state, everywhere
private Context mContext;
private GeocoderListener mListener;
private Location mLocation;
public GeocoderRunner(Context context, Location location,
GeocoderListener addressLookupListener) {
mContext = context;
mLocation = location;
mListener = addressLookupListener;
}
#Override
protected Address doInBackground(String... params) {
Geocoder geocoder = new Geocoder(mContext);
List<Address> addresses = null;
//reference location TODO handle null
double lat = mLocation.getLatitude();
double lon = mLocation.getLongitude();
int i = 0;
try {
//loop through SEARCH_RANGES until addresses are returned
do{
//if range is -1, call getFromLocationName() without bounding box
if(SEARCH_RANGES[i] != -1){
//calculate bounding box
double lowerLeftLatitude = translateLat(lat,-SEARCH_RANGES[i]);
double lowerLeftLongitude = translateLon(lon,SEARCH_RANGES[i]);
double upperRightLatitude = translateLat(lat,SEARCH_RANGES[i]);
double upperRightLongitude = translateLon(lon,-SEARCH_RANGES[i]);
addresses = geocoder.getFromLocationName(params[0], 5, lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude);
} else {
//last resort, try unbounded call with 20 result
addresses = geocoder.getFromLocationName(params[0], 20);
}
i++;
}while((addresses == null || addresses.size() == 0) && i < SEARCH_RANGES.length );
} catch (IOException e) {
Log.i(this.getClass().getSimpleName(),"Gecoder lookup failed! " +e.getMessage());
}
if(addresses == null ||addresses.size() == 0)
return null;
//If multiple addresses were returned, find the closest
if(addresses.size() > 1){
Address closest = null;
for(Address address: addresses){
if(closest == null)
closest = address;
else
closest = getClosest(mLocation, closest,address);//returns the address that is closest to mLocation
}
return closest;
}else
return addresses.get(0);
}
#Override
protected void onPostExecute(Address address) {
if(address == null)
mListener.lookupFailed();
else
mListener.addressReceived(address);
}
//Listener callback
public interface GeocoderListener{
public void addressReceived(Address address);
public void lookupFailed();
}
//HELPER Methods
private static double translateLat(double lat, double dx){
if(lat > 0 )
return (lat + dx*LAT_DEG_PER_KM);
else
return (lat - dx*LAT_DEG_PER_KM);
}
private static double translateLon(double lon, double dy){
if(lon > 0 )
return (lon + dy*LON_DEG_PER_KM);
else
return (lon - dy*LON_DEG_PER_KM);
}
private static Address getClosest(Location ref, Address address1, Address address2){
double xO = ref.getLatitude();
double yO = ref.getLongitude();
double x1 = address1.getLatitude();
double y1 = address1.getLongitude();
double x2 = address2.getLatitude();
double y2 = address2.getLongitude();
double d1 = distance(xO,yO,x1,y1);
double d2 = distance(xO,yO,x2,y2);
if(d1 < d2)
return address1;
else
return address2;
}
private static double distance(double x1, double y1, double x2, double y2){
return Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) );
}
}
Perhaps this is the best solution, but I was wondering if there was a way to do this in a single call.
You code looks too complicated, here is much easier way:
String searchPattern = "123 Main St."
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
//I use last known location, but here we can get real location
Location lastKnownLocation = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
List<Address> addresses = null;
try {
//trying to get all possible addresses by search pattern
addresses = (new Geocoder(this)).getFromLocationName(searchPattern, Integer.MAX_VALUE);
} catch (IOException e) {
}
if (addresses == null || lastKnownLocation == null) {
// location service unavailable or incorrect address
// so returns null
return null;
}
Address closest = null;
float closestDistance = Float.MAX_VALUE;
// look for address, closest to our location
for (Address adr : addresses) {
if (closest == null) {
closest = adr;
} else {
float[] result = new float[1];
Location.distanceBetween(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude(), adr.getLatitude(), adr.getLongitude(), result);
float distance = result[0];
if (distance < closestDistance) {
closest = adr;
closestDistance = distance;
}
}
}
return closest; //here can be null if we did not find any addresses by search pattern.
I tried Jin35's suggestion and increased the max_results of Geocoder.getFromLocationName(), but the results were not desirable. Firstly the large max_result, unbounded call took much longer (2.5x - 7x = 1 - 6 seconds) than the 5 result, geocoord bounded call on my emulator. Perhaps realworld would be faster and this factor becomes less significant.
The killer was no matter if the max_results were 50 or 100, only 20 results came back everytime. Seems Google is limiting the results on the server-side. The closest "123 Main St" was not amoung those 20 results for me - Tested from Mt View, CA and was returned Oakley, CA.
Unless there is another method other than Geocoder.getFromLocationName() for doing address lookup, or a better way to use bounding coord, I will accept my own original answer.
getFromLocationName(String locationName, int maxResults, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude)