aboutsummaryrefslogtreecommitdiffstats
path: root/main/src/cgeo/geocaching/location/MapQuestGeocoder.java
blob: 537ae40df20e0cc1259986623fb0c838485329eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package cgeo.geocaching.location;

import cgeo.geocaching.network.Network;
import cgeo.geocaching.network.Parameters;
import cgeo.geocaching.utils.Log;
import cgeo.geocaching.utils.RxUtils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.jdt.annotation.NonNull;

import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Subscriber;
import rx.functions.Func0;

import android.location.Address;

import java.util.Locale;

public class MapQuestGeocoder {

    private static final String MAPQUEST_KEY = "Fmjtd|luurn1u2n9,bs=o5-9wynua";

    private MapQuestGeocoder() {
        // Do not instantiate
    }

    /**
     * Retrieve addresses from a textual location using MapQuest geocoding API. The work happens on the network
     * scheduler.
     *
     * @param address
     *            the location
     * @return an observable containing zero or more locations
     *
     * @see android.location.Geocoder#getFromLocationName(String, int)
     */
    public static Observable<Address> getFromLocationName(@NonNull final String address) {
        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"));
                if (response == null) {
                    Log.w("MapQuest decoder error: no response");
                    return Observable.error(new RuntimeException("no answer from MapQuest geocoder"));
                }
                final int statusCode = response.path("info").path("statuscode").asInt(-1);
                if (statusCode != 0) {
                    Log.w("MapQuest decoder error: statuscode is not 0");
                    return Observable.error(new RuntimeException("no correct answer from MapQuest geocoder"));
                }
                return Observable.create(new OnSubscribe<Address>() {
                    @Override
                    public void call(final Subscriber<? super Address> subscriber) {
                        try {
                            for (final JsonNode address: response.get("results").get(0).get("locations")) {
                                subscriber.onNext(mapquestToAddress(address));
                            }
                            subscriber.onCompleted();
                        } catch (final Exception e) {
                            Log.e("Error decoding MapQuest address", e);
                            subscriber.onError(e);
                        }
                    }
                });
            }
        }).subscribeOn(RxUtils.networkScheduler);
    }

    private static Address mapquestToAddress(final JsonNode mapquestAddress) {
        final Address address = new Address(Locale.getDefault());
        for (int i = 1; i <= 6; i++) {
            final String adminAreaName = "adminArea" + i;
            setComponent(address, mapquestAddress, adminAreaName, mapquestAddress.path(adminAreaName + "Type").asText());
        }
        setComponent(address, mapquestAddress, "postalCode", "PostalCode");
        int index = 0;
        for (final String addressComponent: new String[]{ mapquestAddress.path("street").asText(), address.getSubLocality(), address.getLocality(),
                address.getPostalCode(), address.getSubAdminArea(), address.getAdminArea(), address.getCountryCode() }) {
            if (StringUtils.isNotBlank(addressComponent)) {
                address.setAddressLine(index++, addressComponent);
            }
        }
        address.setLatitude(mapquestAddress.get("latLng").get("lat").asDouble());
        address.setLongitude(mapquestAddress.get("latLng").get("lng").asDouble());
        return address;
    }

    private static void setComponent(final Address address, final JsonNode mapquestAddress, final String adminArea, final String adminAreaType) {
        final String content = StringUtils.trimToNull(mapquestAddress.path(adminArea).asText());
        switch (adminAreaType) {
            case "City":
                address.setLocality(content);
                break;
            case "Neighborhood":
                address.setSubLocality(content);
                break;
            case "PostalCode":
                address.setPostalCode(content);
                break;
            case "State":
                address.setAdminArea(content);
                break;
            case "County":
                address.setSubAdminArea(content);
                break;
            case "Country":
                address.setCountryCode(content);
                break;
        }
    }

}