[英]Get location (latitude / longitude) from address without city in Android
I'm trying to find the location (lat/long) of a street address w/o city - such as "123 Main St." 我试图找到没有城市的街道地址(纬度/经度) - 例如“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 - eg calling Geocoder.getFromLocation() and have the platform insert a reference point.
此功能内置于Google Maps应用程序以及iOS地图api中,因此令人惊讶地发现它缺少Android - 例如调用Geocoder.getFromLocation()并让平台插入参考点。 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. 我使用左下角和右上角坐标调用Geocoder.getFromLocationName()。 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.
呼叫从当前位置周围的10kmx10km区域开始,并重复(30x30,100x100,然后没有边界框参数),直到返回一些地址。 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. 更新:这种方法似乎对于在边界之外容易找到的地址效率低下。 Eg "New york, NY" or "Boston" searched from the west coast - requiring 3 bounded and 1 unbounded call to Geocoder.getFromLocation().
例如,从纽约西海岸搜索“纽约,纽约”或“波士顿” - 需要3个有界和1个无限制的Geocoder.getFromLocation()调用。 However, unexpectidly, the correct lat/lng is returned for NYC and Boston, on the first call, with tightest bounds here in CA.
然而,毫无疑问,在第一次调用时,纽约和波士顿会返回正确的lat / lng,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. 我尝试了Jin35的建议并增加了Geocoder.getFromLocationName()的max_results,但结果并不理想。 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.
首先,大的max_result,无限制的调用比我的模拟器上的5个结果,geocoord有界调用花了更长的时间(2.5x - 7x = 1 - 6秒)。 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 . 杀手是无论max_results是50还是100, 每次只有20个结果回来 。 Seems Google is limiting the results on the server-side.
似乎Google正在限制服务器端的结果。 The closest "123 Main St" was not amoung those 20 results for me - Tested from Mt View, CA and was returned Oakley, CA.
最接近的“123 Main St”对我来说并不是那20个结果 - 从加利福尼亚州的Mt View进行测试,然后返回加利福尼亚州的Oakley。
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. 除非Geocoder.getFromLocationName()之外的其他方法用于进行地址查找,或者更好的方式来使用边界坐标,我将接受我自己的原始答案。
getFromLocationName(String locationName, int maxResults, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.