diff options
| author | Samuel Tardieu <sam@rfc1149.net> | 2015-01-07 16:06:42 +0100 |
|---|---|---|
| committer | Samuel Tardieu <sam@rfc1149.net> | 2015-01-07 16:27:52 +0100 |
| commit | d7c0c6ec079b6b7a9ee262f9561b1357ca6fbddd (patch) | |
| tree | 61d2725060883b15024d3e6c14a431dfae651d07 | |
| parent | 0babda54fcec1b01c18d5e0a755c9afce18984b5 (diff) | |
| download | cgeo-d7c0c6ec079b6b7a9ee262f9561b1357ca6fbddd.zip cgeo-d7c0c6ec079b6b7a9ee262f9561b1357ca6fbddd.tar.gz cgeo-d7c0c6ec079b6b7a9ee262f9561b1357ca6fbddd.tar.bz2 | |
Use MapQuest reverse geocoder if the Android one fails
| -rw-r--r-- | main/src/cgeo/geocaching/MainActivity.java | 36 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/location/AndroidGeocoder.java | 45 | ||||
| -rw-r--r-- | main/src/cgeo/geocaching/location/MapQuestGeocoder.java | 22 | ||||
| -rw-r--r-- | tests/src/cgeo/geocaching/location/GeocoderTest.java | 32 |
4 files changed, 89 insertions, 46 deletions
diff --git a/main/src/cgeo/geocaching/MainActivity.java b/main/src/cgeo/geocaching/MainActivity.java index de80f5c..399de83 100644 --- a/main/src/cgeo/geocaching/MainActivity.java +++ b/main/src/cgeo/geocaching/MainActivity.java @@ -11,7 +11,9 @@ import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.list.PseudoList; import cgeo.geocaching.list.StoredList; +import cgeo.geocaching.location.AndroidGeocoder; import cgeo.geocaching.location.Geopoint; +import cgeo.geocaching.location.MapQuestGeocoder; import cgeo.geocaching.location.Units; import cgeo.geocaching.maps.CGeoMap; import cgeo.geocaching.network.Network; @@ -37,12 +39,11 @@ import com.google.zxing.integration.android.IntentResult; import org.apache.commons.lang3.StringUtils; import rx.Observable; -import rx.Observable.OnSubscribe; -import rx.Subscriber; import rx.android.schedulers.AndroidSchedulers; import rx.android.view.ViewObservable; import rx.functions.Action0; import rx.functions.Action1; +import rx.functions.Func1; import android.app.AlertDialog; import android.app.AlertDialog.Builder; @@ -54,7 +55,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.location.Address; -import android.location.Geocoder; import android.net.ConnectivityManager; import android.os.Bundle; import android.os.Handler; @@ -75,7 +75,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Locale; public class MainActivity extends AbstractActionBarActivity { @InjectView(R.id.nav_satellites) protected TextView navSatellites; @@ -572,30 +571,21 @@ public class MainActivity extends AbstractActionBarActivity { navAccuracy.setText(null); } + final Geopoint currentCoords = geo.getCoords(); if (Settings.isShowAddress()) { if (addCoords == null) { navLocation.setText(R.string.loc_no_addr); } - if (addCoords == null || (geo.getCoords().distanceTo(addCoords) > 0.5)) { - final Observable<String> address = Observable.create(new OnSubscribe<String>() { + if (addCoords == null || (currentCoords.distanceTo(addCoords) > 0.5)) { + addCoords = currentCoords; + final Observable<String> address = (new AndroidGeocoder(MainActivity.this).getFromLocation(currentCoords) + .onErrorResumeNext(MapQuestGeocoder.getFromLocation(currentCoords))).map(new Func1<Address, String>() { @Override - public void call(final Subscriber<? super String> subscriber) { - try { - addCoords = geo.getCoords(); - final Geocoder geocoder = new Geocoder(MainActivity.this, Locale.getDefault()); - final List<Address> addresses = geocoder.getFromLocation(addCoords.getLatitude(), addCoords.getLongitude(), 1); - if (!addresses.isEmpty()) { - subscriber.onNext(formatAddress(addresses.get(0))); - } - subscriber.onCompleted(); - } catch (final Exception e) { - subscriber.onError(e); - } + public String call(final Address address) { + return formatAddress(address); } - }); - ViewObservable.bindView(navLocation, address.onErrorResumeNext(Observable.just(geo.getCoords().toString()))) - .subscribeOn(RxUtils.networkScheduler) - .subscribe(new Action1<String>() { + }).onErrorResumeNext(Observable.just(currentCoords.toString())); + ViewObservable.bindView(navLocation, address).subscribe(new Action1<String>() { @Override public void call(final String address) { navLocation.setText(address); @@ -603,7 +593,7 @@ public class MainActivity extends AbstractActionBarActivity { }); } } else { - navLocation.setText(geo.getCoords().toString()); + navLocation.setText(currentCoords.toString()); } } } diff --git a/main/src/cgeo/geocaching/location/AndroidGeocoder.java b/main/src/cgeo/geocaching/location/AndroidGeocoder.java index 98ea285..d0d07e8 100644 --- a/main/src/cgeo/geocaching/location/AndroidGeocoder.java +++ b/main/src/cgeo/geocaching/location/AndroidGeocoder.java @@ -16,11 +16,6 @@ import android.location.Geocoder; import java.util.List; import java.util.Locale; -/** - * Encapsulation of the Android {@link Geocoder} with default error handling. All methods of this class - * are blocking and will do network lookups. - * - */ public class AndroidGeocoder { private final Geocoder geocoder; @@ -39,21 +34,49 @@ public class AndroidGeocoder { * @see Geocoder#getFromLocationName(String, int) */ public Observable<Address> getFromLocationName(@NonNull final String keyword) { + if (!Geocoder.isPresent()) { + return Observable.error(new RuntimeException("no Android geocoder")); + } return Observable.defer(new Func0<Observable<Address>>() { @Override public Observable<Address> call() { try { - final List<Address> addresses = geocoder.getFromLocationName(keyword, 20); - if (CollectionUtils.isEmpty(addresses)) { - return Observable.error(new RuntimeException("no result from Android geocoder")); - } - return Observable.from(addresses); + return addressesToObservable(geocoder.getFromLocationName(keyword, 20)); } catch (final Exception e) { - Log.i("Unable to use Android geocoder: " + e.getMessage()); + Log.i("Unable to use Android reverse geocoder: " + e.getMessage()); return Observable.error(e); } } }).subscribeOn(RxUtils.networkScheduler); } + /** + * Retrieve the physical address for coordinates. The work happens on the network scheduler. + * + * @param coords the coordinates + * @return an observable containing one location or an error + */ + public Observable<Address> getFromLocation(@NonNull final Geopoint coords) { + if (!Geocoder.isPresent()) { + return Observable.error(new RuntimeException("no Android reverse geocoder")); + } + return Observable.defer(new Func0<Observable<Address>>() { + @Override + public Observable<Address> call() { + try { + return addressesToObservable(geocoder.getFromLocation(coords.getLatitude(), coords.getLongitude(), 1)); + } catch (final Exception e) { + Log.i("Unable to use Android reverse geocoder: " + e.getMessage()); + return Observable.error(e); + } + } + }).subscribeOn(RxUtils.networkScheduler).first(); + } + + private static Observable<Address> addressesToObservable(final List<Address> addresses) { + return CollectionUtils.isEmpty(addresses) ? + Observable.<Address>error(new RuntimeException("no result from Android geocoder")) : + Observable.from(addresses); + } + } diff --git a/main/src/cgeo/geocaching/location/MapQuestGeocoder.java b/main/src/cgeo/geocaching/location/MapQuestGeocoder.java index 537ae40..cc4a6b5 100644 --- a/main/src/cgeo/geocaching/location/MapQuestGeocoder.java +++ b/main/src/cgeo/geocaching/location/MapQuestGeocoder.java @@ -39,11 +39,25 @@ public class MapQuestGeocoder { * @see android.location.Geocoder#getFromLocationName(String, int) */ public static Observable<Address> getFromLocationName(@NonNull final String address) { + return get("address", new Parameters("location", address, "maxResults", "20", "thumbMaps", "false")); + } + + /** + * Retrieve the physical address for coordinates. The work happens on the network scheduler. + * + * @param coords the coordinates + * @return an observable containing one location or an error + */ + public static Observable<Address> getFromLocation(@NonNull final Geopoint coords) { + return get("reverse", new Parameters("location", String.format(Locale.US, "%f,%f", coords.getLatitude(), coords.getLongitude()))).first(); + } + + private static Observable<Address> get(@NonNull final String method, @NonNull final Parameters parameters) { return Observable.defer(new Func0<Observable<Address>>() { @Override public Observable<Address> call() { - final ObjectNode response = Network.requestJSON("https://www.mapquestapi.com/geocoding/v1/address", - new Parameters("key", MAPQUEST_KEY, "location", address, "maxResults", "20", "thumbMaps", "false")); + final ObjectNode response = Network.requestJSON("https://www.mapquestapi.com/geocoding/v1/" + method, + parameters.put("key", MAPQUEST_KEY)); if (response == null) { Log.w("MapQuest decoder error: no response"); return Observable.error(new RuntimeException("no answer from MapQuest geocoder")); @@ -110,6 +124,10 @@ public class MapQuestGeocoder { break; case "Country": address.setCountryCode(content); + address.setCountryName(new Locale("", content).getDisplayCountry()); + break; + // Make checkers happy + default: break; } } diff --git a/tests/src/cgeo/geocaching/location/GeocoderTest.java b/tests/src/cgeo/geocaching/location/GeocoderTest.java index f53c074..0cc1a79 100644 --- a/tests/src/cgeo/geocaching/location/GeocoderTest.java +++ b/tests/src/cgeo/geocaching/location/GeocoderTest.java @@ -4,45 +4,57 @@ import static org.assertj.core.api.Assertions.assertThat; import cgeo.CGeoTestCase; import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; import org.assertj.core.data.Offset; import rx.Observable; -import android.annotation.TargetApi; import android.location.Address; import android.location.Geocoder; -import android.os.Build; public class GeocoderTest extends CGeoTestCase { private static final String TEST_ADDRESS = "46 rue Barrault, Paris, France"; private static final double TEST_LATITUDE = 48.82677; private static final double TEST_LONGITUDE = 2.34644; + private static final Geopoint TEST_COORDS = new Geopoint(TEST_LATITUDE, TEST_LONGITUDE); private static final Offset<Double> TEST_OFFSET = Offset.offset(0.00050); - @TargetApi(Build.VERSION_CODES.GINGERBREAD) public static void testAndroidGeocoder() { // Some emulators don't have access to Google Android geocoder if (Geocoder.isPresent()) { - testGeocoder(new AndroidGeocoder(CgeoApplication.getInstance()).getFromLocationName(TEST_ADDRESS), "Android"); + final AndroidGeocoder geocoder = new AndroidGeocoder(CgeoApplication.getInstance()); + testGeocoder(geocoder.getFromLocationName(TEST_ADDRESS), "Android", true); + testGeocoder(geocoder.getFromLocation(TEST_COORDS), "Android reverse", true); + } else { + Log.i("not testing absent Android geocoder"); } } public static void testGCGeocoder() { - testGeocoder(GCGeocoder.getFromLocationName(TEST_ADDRESS), "GC"); + testGeocoder(GCGeocoder.getFromLocationName(TEST_ADDRESS), "GC", false); } public static void testMapQuestGeocoder() { - testGeocoder(MapQuestGeocoder.getFromLocationName(TEST_ADDRESS), "MapQuest"); + testGeocoder(MapQuestGeocoder.getFromLocationName(TEST_ADDRESS), "MapQuest", true); + testGeocoder(MapQuestGeocoder.getFromLocation(TEST_COORDS), "MapQuest reverse", true); } - public static void testGeocoder(final Observable<Address> addressObservable, final String geocoder) { + public static void testGeocoder(final Observable<Address> addressObservable, final String geocoder, final boolean withAddress) { final Address address = addressObservable.toBlocking().first(); - assertThat(address.getLatitude()).as("latitude for " + geocoder + " geocoder").isCloseTo(TEST_LATITUDE, TEST_OFFSET); - assertThat(address.getLongitude()).as("longitude for " + geocoder + " geocoder").isCloseTo(TEST_LONGITUDE, TEST_OFFSET); - assertThat(StringUtils.lowerCase(address.getAddressLine(0))).as("street address for " + geocoder + " geocoder").startsWith("46 rue barrault"); + assertThat(address.getLatitude()).as(describe("latitude", geocoder)).isCloseTo(TEST_LATITUDE, TEST_OFFSET); + assertThat(address.getLongitude()).as(describe("longitude", geocoder)).isCloseTo(TEST_LONGITUDE, TEST_OFFSET); + if (withAddress) { + assertThat(StringUtils.lowerCase(address.getAddressLine(0))).as(describe("street address", geocoder)).startsWith("46 rue barrault"); + assertThat(address.getLocality()).as(describe("locality", geocoder)).isEqualTo("Paris"); + assertThat(address.getCountryName()).as(describe("country name", geocoder)).isEqualTo("France"); + } + } + + private static String describe(final String field, final String geocoder) { + return new StringBuilder(field).append(" for ").append(geocoder).append(" .geocoder").toString(); } } |
