2012-09-25 172 views
0

我試圖找到一個街道地址w/o城市的位置(緯度/經度) - 比如「123 Main St.」 - 距離當前位置最近。此功能內置於Google地圖應用以及iOS地圖api中,因此發現它缺少Android版本令人驚訝 - 例如,調用Geocoder.getFromLocation()並讓平臺插入一個參考點。我已經嘗試了幾種解決方案,以下是最好的,但仍然感覺自卑。從Android的地址獲取位置(經度/緯度)沒有城市的地址

我使用左下角和右上角座標調用Geocoder.getFromLocationName()。調用以當前位置周圍的一個10kmx10km區域開始,並重復(30x30,100x100,然後沒有邊框參數),直到返回一些地址。當返回多個地址時,計算並使用最近的:

更新:此方法好像對於容易找到的邊界外的地址是低效的。例如。從西海岸搜索「紐約紐約」或「波士頓」 - 需要3次有界和1次無界調用Geocoder.getFromLocation()。然而,沒有特別說明的是,在第一次通話時,紐約證券交易所和波士頓正確的經濟緯度/經濟回報率將在這裏以最嚴格的界限返回。谷歌正在聰明,無視我們的界限。這可能會導致一些問題,但對於這種方法非常有用。

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

}

或許這是最好的解決辦法,但我不知道是否有一種方法在單個調用做到這一點。

回答

0

我試圖Jin35的建議,並增加Geocoder.getFromLocationName()的MAX_RESULTS,但結果不理想。首先,max_result,unbounded調用比第5個結果花費更長的時間(2.5x - 7x = 1-6秒),geocoord在我的模擬器上有界調用。也許現實世界會更快,這個因素變得不那麼重要。

殺手無論如果max_results是50或100,每次只有20個結果回來。 Google似乎限制了服務器端的結果。最接近的「123 Main St」並不是我所獲得的20個結果 - 來自加利福尼亞州Mt View的測試,並被退回到了加利福尼亞州的Oakley。

除非使用Geocoder.getFromLocationName()以外的其他方法來進行地址查找,或者使用邊界座標的更好方法,我會接受我自己的原始答案。

2

你的代碼看起來太複雜,下面是更簡單的方法:

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. 
+0

Geocoder.getFromLocationName()的Javadoc建議請求1-5個結果。有超過5個地方與「123 Main St.」相匹配我剛剛執行了這個任務,得到了Van Buren ME,Madawaska ME,紐約布法羅,紐約伍斯特和紐約州Schenevus。我在加利福尼亞州,可能不是我在找什麼。 – nathanielwolf

+0

是的,當使用「推薦」5結果時,我的結果不好。因爲如果有100個「主要」街道,那麼您很可能會在前5箇中獲得所需的結果。所以,只需獲得所有結果 - 這將花費大量時間 - 然後在AsyncTask中完成。 – Jin35

+0

無論maxResults參數如何,Geocoder.getFromLocationName()最多返回20個結果。見下文。 – nathanielwolf

0
getFromLocationName(String locationName, int maxResults, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude)