0

Using the Google API to determine the County from one of two choices (by latitude and longitude) is giving inconsistent results. One gives the County under the key of 2; the other with the key of 4. How can I fetch the needed information with this inconsistency?

I have used Json only a couple times so perhaps I've misunderstood how values are retrieved so here is my PHP function for parsing it:

function getCounty($LatLong) {
    global $googlekey;
    $url = "https://maps.googleapis.com/maps/api/geocode/json?latlng=$LatLong&key=$googlekey";
    $json = @file_get_contents($url);
    $data = json_decode($json);
    $status = $data->status;
    if ($status === "OK") :
        //return $data->results[0]->address_components[2]->long_name;
        return $data->results[0]->address_components[4]->long_name;
    else :
        return FALSE;
    endif;
}

In other words, to get one County name it needs this:

return $data->results[0]->address_components[2]->long_name;

. . . while it needs this to get the other County name:

return $data->results[0]->address_components[4]->long_name;

This defeats the purpose of trying to find dynamically in which County it is being used!

Using Json to parse it, gives this (a partial snippet of the output) for one county where the key for the County is 2

[2] => stdClass Object
    (
        [long_name] => Santa Cruz County
        [short_name] => Santa Cruz County
        [types] => Array
            (
                [0] => administrative_area_level_2
                [1] => political
            )

    )

[3] => stdClass Object
    (
        [long_name] => California
        [short_name] => CA
        [types] => Array
            (
                [0] => administrative_area_level_1
                [1] => political
            )

    )

[4] => stdClass Object
    (
        [long_name] => United States
        [short_name] => US
        [types] => Array
            (
                [0] => country
                [1] => political
            )

    )

. . . and this gives the key for the other County as 4:

[2] => stdClass Object
    (
        [long_name] => West Valley
        [short_name] => West Valley
        [types] => Array
            (
                [0] => neighborhood
                [1] => political
            )

    )

[3] => stdClass Object
    (
        [long_name] => San Jose
        [short_name] => San Jose
        [types] => Array
            (
                [0] => locality
                [1] => political
            )

    )

[4] => stdClass Object
    (
        [long_name] => Santa Clara County
        [short_name] => Santa Clara County
        [types] => Array
            (
                [0] => administrative_area_level_2
                [1] => political
            )

    )

Having said that, is there a way to get the County directly from the IP address? I saw somewhere that Google frowns on it but then within the Google API documentation, I saw references to use the IP but no examples so it was vague on how it might be done.

DonP
  • 725
  • 1
  • 8
  • 27
  • I dont understand your issue. Could you elaborate on that perhaps? – paskl Nov 29 '18 at 21:37
  • This issue is that I need to use return $data->results[0]->address_components[2]->long_name; to see one county and return $data->results[0]->address_components[4]->long_name; to see the other so it's not dynamic enough to be useful. Note the 2 and 4 keys are different and one pulls up one county, the other the other so the Google API uses different keys for the same values depending on location. This can't be right so there must be some other way to get consistent results. – DonP Nov 30 '18 at 00:16
  • 1
    According the documentation the API can return several results. What you want is to extract the value according a specific type. See [available type](https://developers.google.com/maps/documentation/geocoding/intro?hl=de#Types). So you could loop through the results and only extract the one where the wanted type is contained. – enricog Nov 30 '18 at 08:49
  • What is the actual desired result? Is this a simple task of looping through the objects and returning the first row that has the right `type` value? – mickmackusa Sep 26 '22 at 10:15
  • This is a very old post that was resolved already back in 2018. – DonP Sep 30 '22 at 16:28

2 Answers2

1

As @enricog said; What you want is to extract the value according a specific type.

Based on the documentation: https://developers.google.com/maps/documentation/geocoding/intro?hl=de#Types

Here's an idea of what you can do:

$data = json_decode($json);
$counties = [];

foreach($data->results as $result){
    $counties = array_merge($counties, array_filter($result->address_components, function ($address) { 
        return in_array('administrative_area_level_2', $address->types);
    })); 
}


$counties = array_map(function ($county) { 
    return $county->long_name;
}, $counties); 

echo implode(', ', $counties);

https://3v4l.org/UUboK

I don't work with google map API, I have no idea if you can have multiple counties in the response. The JSON structure allow it and I guess, depend of what the request is, it may have some. I have added fictional values to the json to validate the principle.

I believe this snippet can be optimized. But at least it will return every single counties from the response and you won't get an undefined index

Review

After playing a bit with the API I can now answer a bit more precisely. But this is only my constatation. When you request the location it will returns many known place that are close to your location. The first result should always be the closest to you.

Note from Google : Reverse geocoding is not an exact science. The geocoder will attempt to find the closest addressable location within a certain tolerance.

Based on that, we can optimize the code to have only one result which should be the correct county.

function getCounty($LatLong) {
    // do stuff
    $data = json_decode($json);
    $counties = [];

    if(!empty($data->results)){
        $counties = array_merge($counties, array_filter($data->results[0]->address_components, function ($address) { 
            return in_array('administrative_area_level_2', $address->types);
        })); 
    }

    if(!empty($counties)){
        return reset($counties)->long_name; // https://stackoverflow.com/a/3771228/8068675
    }
    return 'No county found';
}

https://3v4l.org/5RGOe
I did read, you where not using PHP 7. However I will keep this code with PHP 7 compatibility since php 5.6 is no longer supported and shouldn't be used anymore.

Clément Baconnier
  • 5,718
  • 5
  • 29
  • 55
  • Just to clarity, I am looking for County, not Country. The Country is always the United States. – DonP Nov 30 '18 at 17:16
  • @DonP Sorry, I miss read. In that case you can replace the `return in_array()` with `return strpos($address->long_name, 'County') !== false;` The logic remain the same. – Clément Baconnier Nov 30 '18 at 17:23
  • I also don't know the address in advance - this is supposed to be automatic and change from one to the other as I go from one county to the other. It would be better if it could be done directly from the IP but if that's not possible, then through latitude and longitude as I am doing now. Doing it this way requires another third party plugin that I would prefer not to use but apparently there is no choice although I'm open to ideas. – DonP Nov 30 '18 at 17:23
  • Do you mean, you want the `administrative_area_level_2` in that case ? From my initial snippet, replace `country` with `administrative_area_level_2` – Clément Baconnier Nov 30 '18 at 17:29
  • Not getting errors but not getting output either. What is the $address variable for? Incidentally, this needs to pull up one County only, not all of them, and it needs to be the County where I am located at the time. – DonP Nov 30 '18 at 17:40
  • I have updated with `administrative_area_level_2` the array_filter iterate through each address contained in the `address_components` and set them in `$address`. – Clément Baconnier Nov 30 '18 at 18:24
  • If you give the `long` and `lat` you SHOULD get only one County add the end. But I believe, it MAY have more, depend of the context since the Json is formatted to have multiple Councy. I guess, depend of the political situtation of the location, a place could have multiple administrative_area_level_2 (Ex. Border? war? politic reason?) I don't know. But the Json has been formatted to be this way. – Clément Baconnier Nov 30 '18 at 18:28
  • Thank you, it seems to be working now but is giving multiple results: Santa Cruz CountySanta Cruz CountySanta Cruz CountySanta Cruz CountySanta Cruz CountySanta Cruz County. How can I get it to not repeat? As for "Border? war? politic reason?", this is pulling only one of two counties within a rather small geographic location on the West Coast of the US so none of that applies. – DonP Nov 30 '18 at 19:25
0

Using cbaconnier's looping and limiting it to run only once, it is now working! Note that $counties = []; worked on my local PHP 7.2 development system but not on the older version of PHP on the live server so I changed it to $counties = array();.

Thank you for all the help. Now if there is a way to do it directly from the IP, I would like to know what it is!

function getCounty($LatLong) {
    global $googlekey;

    $url = "https://maps.googleapis.com/maps/api/geocode/json?latlng=$LatLong&key=$googlekey";
    $json = @file_get_contents($url);
    $data = json_decode($json);
    $status = $data->status;
    if ($status === "OK") :
        //$counties = []; // for later PHP versions but does not work on live server
        $counties = array();

        $i = 0;
        foreach (range(1, 1)  as $i) :
            foreach($data->results as $result) :
                if ($i++ > 1) break;
                $counties = array_merge($counties, array_filter($result->address_components, function ($address) { 
                    return in_array('administrative_area_level_2', $address->types);
                })); 
            endforeach;
        endforeach;

        $counties = array_map(function ($county) { 
            return $county->long_name;
        }, $counties);

        return $counties[0];

    else :
        return FALSE;
    endif;
}
DonP
  • 725
  • 1
  • 8
  • 27
  • @cbaconnier I may have spoken too soon. If I use **echo** $county->long_name; it shows as expected to the screen but if I use **return** $county->long_name; the function does not produce any output. – DonP Nov 30 '18 at 22:18
  • The `return $county->long_name;` that you see return the long_name in a `closure`. See how [array_map() works](http://php.net/manual/en/function.array-map.php) You need to return $counties from your function. (Which from my example, contains all the "long_name" in an array – Clément Baconnier Nov 30 '18 at 23:37
  • Got it, thank you, and I corrected the post above to reflect the correction. I appreciate the help. It is working perfectly now in one county and in the next hour or so I'll be in the other county where I'll test it again but I don't expect any surprises. It's possible, I suppose, since it's using the IP for it to provide a nearby county rather than the one I'm in but once I see which ones it might get, I can program it to ignore them as I already did with one where the function is being called. – DonP Nov 30 '18 at 23:58