Obtaining State abbreviation from getAdminArea();

2019-02-21 16:30发布

问题:

I've attempted two different ways in trying to obtain the city name as well as the state abbreviation only from the Address class, with no luck. The first is returning the State like so "CA 92055" with the zip code, and the second attempt returns the full State name. Any quick ways around this?

First attempt which the state ends up returning "CA 92055" (Zip followed after the abbrev)

Geocoder geoCoder = new Geocoder(getActivity(), Locale.getDefault());
                List<Address> addresses;
                try {
                    addresses = geoCoder.getFromLocation(mLatitude, mLongitude, 10);
                     int i=1;
                     for(Address addObj:addresses)
                     {
                         // Looping once
                         if(i==1)
                         {

                             String add_line1_extract;

                             add_line1_extract=addObj.getAddressLine(1);

                             String string = add_line1_extract;
                             String[] parts = string.split(",");

                             //Setting city
                             mCity = parts[0]; 

                             //setting state
                             mState = parts[1]; 

                             // Final Output
                             String cityAndState = mCity + ", " + mState;
                             i++;

                         }
                     }
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }

Second attempt, getting closer no zip now...but... (Returns the full state name):

Geocoder geoCoder = new Geocoder(getActivity(), Locale.getDefault());
                List<Address> addresses;
                try {
                    addresses = geoCoder.getFromLocation(mLatitude, mLongitude, 10);
                     int i=1;
                     for(Address addObj:addresses)
                     {
                         // Looping once
                         if(i==1)
                         {

                             //Setting city
                             mCity = addObj.getSubLocality();                            
                             //setting state
                             mState = addObj.getAdminArea(); 

                             i++;
                         }
                     }
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }

回答1:

I don't believe you can get a state abbreviation directly from getAdminArea() despite what the documentation says. However, when dealing with US and Canada, you can set up a hashmap that will map out the state/provinces to the abbreviations on reference.

Use something like this:

Map<String, String> states = new HashMap<String, String>();
states.put("Alabama","AL");
states.put("Alaska","AK");
states.put("Alberta","AB");
states.put("American Samoa","AS");
states.put("Arizona","AZ");
states.put("Arkansas","AR");
states.put("Armed Forces (AE)","AE");
states.put("Armed Forces Americas","AA");
states.put("Armed Forces Pacific","AP");
states.put("British Columbia","BC");
states.put("California","CA");
states.put("Colorado","CO");
states.put("Connecticut","CT");
states.put("Delaware","DE");
states.put("District Of Columbia","DC");
states.put("Florida","FL");
states.put("Georgia","GA");
states.put("Guam","GU");
states.put("Hawaii","HI");
states.put("Idaho","ID");
states.put("Illinois","IL");
states.put("Indiana","IN");
states.put("Iowa","IA");
states.put("Kansas","KS");
states.put("Kentucky","KY");
states.put("Louisiana","LA");
states.put("Maine","ME");
states.put("Manitoba","MB");
states.put("Maryland","MD");
states.put("Massachusetts","MA");
states.put("Michigan","MI");
states.put("Minnesota","MN");
states.put("Mississippi","MS");
states.put("Missouri","MO");
states.put("Montana","MT");
states.put("Nebraska","NE");
states.put("Nevada","NV");
states.put("New Brunswick","NB");
states.put("New Hampshire","NH");
states.put("New Jersey","NJ");
states.put("New Mexico","NM");
states.put("New York","NY");
states.put("Newfoundland","NF");
states.put("North Carolina","NC");
states.put("North Dakota","ND");
states.put("Northwest Territories","NT");
states.put("Nova Scotia","NS");
states.put("Nunavut","NU");
states.put("Ohio","OH");
states.put("Oklahoma","OK");
states.put("Ontario","ON");
states.put("Oregon","OR");
states.put("Pennsylvania","PA");
states.put("Prince Edward Island","PE");
states.put("Puerto Rico","PR");
states.put("Quebec","PQ");
states.put("Rhode Island","RI");
states.put("Saskatchewan","SK");
states.put("South Carolina","SC");
states.put("South Dakota","SD");
states.put("Tennessee","TN");
states.put("Texas","TX");
states.put("Utah","UT");
states.put("Vermont","VT");
states.put("Virgin Islands","VI");
states.put("Virginia","VA");
states.put("Washington","WA");
states.put("West Virginia","WV");
states.put("Wisconsin","WI");
states.put("Wyoming","WY");
states.put("Yukon Territory","YT");


回答2:

I worked around it by finding the last 2 letters word in the full address (assuming an US address provided by google maps android geocoder). It works for all cases I have found:

private String getUSStateCode(Address USAddress){
    String fullAddress = "";
    for(int j = 0; j <= USAddress.getMaxAddressLineIndex(); j++)
        if (USAddress.getAddressLine(j) != null)
            fullAddress = fullAddress + " " + USAddress.getAddressLine(j);

    String stateCode = null;
    Pattern pattern = Pattern.compile(" [A-Z]{2} ");
    String helper = fullAddress.toUpperCase().substring(0, fullAddress.toUpperCase().indexOf("USA"));
    Matcher matcher = pattern.matcher(helper);
    while (matcher.find())
        stateCode = matcher.group().trim();

    return stateCode;
}


回答3:

Here is a combined and modified version of Bourne and pellyadolfo's answers. It first tries to map the full state name to a state code (works for Canadian provinces too, and is less error prone than regex), and if that doesn't work then it falls back on the regex solution (which has the potential for error, hence why I prefer it as a backup solution, but can work with different languages or countries).

The regex solution has been improved to include a sanity check at the beginning, and has a more advanced regex to eliminate needing to manually filter out "USA" (this allows Canadian addresses to work). It also removes the "toupper()" call which has a side effect of converting "St" (short for "street") into "ST" which can cause a false match.

import android.location.Address;
import android.util.Log;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StateNameAbbreviator {
    private static final String TAG = "StateNameAbbreviator";

    static private Map<String, String> mStateMap = null;

    static public String getStateAbbreviation(Address address) {
        if (address == null) {
            return null;
        }

        populateStates();

        String stateCode = mStateMap.get(address.getAdminArea());
        if (stateCode == null) {
            Log.d(TAG, "State mapping failed, parsing from address");
            stateCode = parseStateCodeFromFullAddress(address);
            if (stateCode == null) {
                Log.d(TAG, "Could not parse state from address");
            }
        }
        else {
            Log.d(TAG, "Successfully mapped " + address.getAdminArea() + " to " + stateCode);
        }

        return stateCode;
    }

    static private String parseStateCodeFromFullAddress(Address address) {
        if ((address == null) || address.getMaxAddressLineIndex() < 0) {
            return null;
        }

        String fullAddress = "";
        for(int j = 0; j <= address.getMaxAddressLineIndex(); j++) {
            if (address.getAddressLine(j) != null) {
                fullAddress += " " + address.getAddressLine(j);
            }
        }

        Log.d(TAG, "Full address: " + fullAddress);

        Pattern pattern = Pattern.compile("(?<![A-Za-z0-9])([A-Z]{2})(?![A-Za-z0-9])");
        Matcher matcher = pattern.matcher(fullAddress);

        String stateCode = null;
        while (matcher.find()) {
            stateCode = matcher.group().trim();
        }

        Log.d(TAG, "Parsed statecode: " + stateCode);

        return stateCode;
    }

    private static void populateStates() {
        if (mStateMap == null) {
            mStateMap = new HashMap<String, String>();
            mStateMap.put("Alabama", "AL");
            mStateMap.put("Alaska", "AK");
            mStateMap.put("Alberta", "AB");
            mStateMap.put("American Samoa", "AS");
            mStateMap.put("Arizona", "AZ");
            mStateMap.put("Arkansas", "AR");
            mStateMap.put("Armed Forces (AE)", "AE");
            mStateMap.put("Armed Forces Americas", "AA");
            mStateMap.put("Armed Forces Pacific", "AP");
            mStateMap.put("British Columbia", "BC");
            mStateMap.put("California", "CA");
            mStateMap.put("Colorado", "CO");
            mStateMap.put("Connecticut", "CT");
            mStateMap.put("Delaware", "DE");
            mStateMap.put("District Of Columbia", "DC");
            mStateMap.put("Florida", "FL");
            mStateMap.put("Georgia", "GA");
            mStateMap.put("Guam", "GU");
            mStateMap.put("Hawaii", "HI");
            mStateMap.put("Idaho", "ID");
            mStateMap.put("Illinois", "IL");
            mStateMap.put("Indiana", "IN");
            mStateMap.put("Iowa", "IA");
            mStateMap.put("Kansas", "KS");
            mStateMap.put("Kentucky", "KY");
            mStateMap.put("Louisiana", "LA");
            mStateMap.put("Maine", "ME");
            mStateMap.put("Manitoba", "MB");
            mStateMap.put("Maryland", "MD");
            mStateMap.put("Massachusetts", "MA");
            mStateMap.put("Michigan", "MI");
            mStateMap.put("Minnesota", "MN");
            mStateMap.put("Mississippi", "MS");
            mStateMap.put("Missouri", "MO");
            mStateMap.put("Montana", "MT");
            mStateMap.put("Nebraska", "NE");
            mStateMap.put("Nevada", "NV");
            mStateMap.put("New Brunswick", "NB");
            mStateMap.put("New Hampshire", "NH");
            mStateMap.put("New Jersey", "NJ");
            mStateMap.put("New Mexico", "NM");
            mStateMap.put("New York", "NY");
            mStateMap.put("Newfoundland", "NF");
            mStateMap.put("North Carolina", "NC");
            mStateMap.put("North Dakota", "ND");
            mStateMap.put("Northwest Territories", "NT");
            mStateMap.put("Nova Scotia", "NS");
            mStateMap.put("Nunavut", "NU");
            mStateMap.put("Ohio", "OH");
            mStateMap.put("Oklahoma", "OK");
            mStateMap.put("Ontario", "ON");
            mStateMap.put("Oregon", "OR");
            mStateMap.put("Pennsylvania", "PA");
            mStateMap.put("Prince Edward Island", "PE");
            mStateMap.put("Puerto Rico", "PR");
            mStateMap.put("Quebec", "PQ");
            mStateMap.put("Rhode Island", "RI");
            mStateMap.put("Saskatchewan", "SK");
            mStateMap.put("South Carolina", "SC");
            mStateMap.put("South Dakota", "SD");
            mStateMap.put("Tennessee", "TN");
            mStateMap.put("Texas", "TX");
            mStateMap.put("Utah", "UT");
            mStateMap.put("Vermont", "VT");
            mStateMap.put("Virgin Islands", "VI");
            mStateMap.put("Virginia", "VA");
            mStateMap.put("Washington", "WA");
            mStateMap.put("West Virginia", "WV");
            mStateMap.put("Wisconsin", "WI");
            mStateMap.put("Wyoming", "WY");
            mStateMap.put("Yukon Territory", "YT");
        }
    }
}

The regex will match any two letter uppercase word:

(?<![A-Za-z0-9])([A-Z]{2})(?![A-Za-z0-9])

The challenge here is "USA" will actually match "US" with a simple two letter upper case search. So we need a lookahead and lookbehind:

?<!

lookbehind the match

(?<![A-Za-z0-9])

Look behind the match, and make sure there is not an alphanumeric character there (ie, must be a "start of line", whitespace, comma, etc. before the match)

([A-Z]{2})

Match two uppercase letters

?!

lookahead of the match

(?![A-Za-z0-9])

Look ahead of the match, and make sure there is not an alphanumeric character there (ie, must be an "end of line" or whitespace, comma, etc. after the match)



回答4:

This works for me on US addresses:

String[] spState = addressInformation.get(0).getAddressLine(1).split(" ");
String state = spState[1];