从单个字符串中猜测城市、州和邮政编码


Guess city, state, and zip from single string

所以我有一个位置搜索字段,我想接受几乎所有内容(城市、州和邮政编码),例如:

  • 加利福尼亚州洛杉矶
  • 加州
  • 90210
  • 奥兰治加州

以及其中的任何组合...

由此,我将单词分成一个数组

$inputs = preg_split("/['s,-'/]+/", $input);

这给了我类似的东西

array(5) {
    [0]=> string(4) "Some"
    [1]=> string(4) "City"
    [2]=> string(3) "New"
    [3]=> string(4) "York"
    [4]=> string(5) "88888"
}

然后我先选择邮政编码

foreach ($inputs as $key => $value) {
    if (is_numeric($value) && strlen($value) == 5) {
        $zip = $value;              
        unset($inputs[$key]);
    }
}

请注意unset()

现在我需要将州名称与我的州数据库匹配。 困境是一些州的名称中有多个单词(北卡罗来纳州,纽约州)。

如何将我的$inputs与州名称和缩写相匹配,从我的数组中删除匹配的条件(接下来我必须对城市做同样的事情)?


我想尝试一下

$inputString = "'" . implode("','", $inputs) . "'";
$result = mysql_query("SELECT state_name
                      FROM states
                      WHERE state_name IN ({$inputString})
                      OR state_abbrev IN ({$inputString})");

但这并不能说明它匹配哪些东西或适用于多词状态

编辑:

对于仇恨者,我宁愿没有 3 个单独的字段。我认为这会使用户体验复杂化。 我宁愿让服务器代替他们进行思考,以最好地猜测他们试图传达的位置。 我也将有一个"高级"搜索,其中将包含这些字段,但所有这些字段都占用了网站设计太多空间。

例子:

  • http://maps.google.com/
  • http://vein.com/index.php
  • http://www.zillow.com/

您可以向address表添加一列,其中包含城市名称、州名称、邮政编码等的串联。然后对其设置FULLTEXT索引,并对其运行整个输入字符串的全文搜索。

不过,不确定这表现如何。

这是我目前使用的,但是有太多的循环和查询,我怀疑它是否有效或"猜测"非常准确

    function getLocations($input) {
    $state = NULL;
    $zip = NULL;
    $input = strtoupper(trim($input));
    $inputs = preg_split("/[^a-zA-Z0-9]+/", $input);
    // Resolve zip code
    foreach ($inputs as $key => $value) {
        if (is_numeric($value) && strlen($value) == 5) {
            $zip = $value;              
            unset($inputs[$key]);
        }
    }
    $inputs = array_reverse($inputs);
    $result = mysql_query("SELECT state_name, state_abbrev FROM states");
    // Resolve state (one worded)
    while ($row = mysql_fetch_assoc($result)) {
        foreach ($inputs as $key => $value) {
            if ($row['state_abbrev'] == $value || $row['state_name'] == $value) {
                $state = $row['state_abbrev'];
                unset($inputs[$key]);
                return array(
                    'city' => "'" . implode(" ", array_reverse($inputs)) . "'",
                    'state' => "'" . $state . "'",
                    'zip' => "'" . $zip . "'"
                );
            }
        }
    }
    // Resolve state (2/3 worded)
    for ($i = 0; $i < count($inputs) - 1; $i++) {
        $duoValue = @$inputs[$i + 1] . " " . @$inputs[$i];
        if (count($inputs) > $i + 2) {
            $trioValue = $inputs[$i + 2] . " " . $duoValue;
        }
        $result2 = mysql_query("SELECT state_name, state_abbrev FROM states") or die (mysql_error());
        while ($row = mysql_fetch_assoc($result2)) {
            if ($row['state_abbrev'] == $duoValue || $row['state_name'] == $duoValue) {
                $state = $row['state_abbrev'];
                unset($inputs[$i], $inputs[$i + 1]);
                return array(
                    'city' => "'" . implode(" ", array_reverse($inputs)) . "'",
                    'state' => "'" . $state . "'",
                    'zip' => "'" . $zip . "'"
                );
            }
            else if ($i < count($inputs) - 2) {
                if ($row['state_abbrev'] == $trioValue || $row['state_name'] == $trioValue) {
                    $state = $row['state_abbrev'];
                    unset($inputs[$i], $inputs[$i + 1], $inputs[$i + 2]);
                    return array(
                        'city' => "'" . implode(" ", array_reverse($inputs)) . "'",
                        'state' => "'" . $state . "'",
                        'zip' => "'" . $zip . "'"
                    );
                }
            }
        }
    }
    return array(
        'city' => "'" . implode(" ", array_reverse($inputs)) . "'",
        'state' => "'" . $state . "'",
        'zip' => "'" . $zip . "'"
    );
}

我完全同意您的想法,即让用户轻松并将所有地址信息放在一个输入框中。 但是,每个用户输入的信息可能会有所不同,并且很难想出涵盖每种情况的算法。 最好的办法是看看是否有人已经这样做了,正如你提到的,谷歌已经这样做了。 幸运的是,他们有一个针对此类问题的 API。

如果您使用Google地图地理编码器(https://developers.google.com/maps/documentation/geocoding/#GeocodingRequests),则基本上可以向其传递任何看起来合理类似于地址的内容,并且它将返回结构良好的地址结果。

谷歌的例子:https://google-developers.appspot.com/maps/documentation/javascript/examples/geocoding-simple

另一个例子 - 查找白宫: 将此 URL 放入您的浏览器中: http://maps.googleapis.com/maps/api/geocode/json?address=1600%20pennsylvania%20ave%20washongton%20dc&sensor=false(注意我故意在这里拼错以显示 API 的容错)。

API 调用返回一个非常有用的 JSON 对象:

{
   "results" : [
      {
         "address_components" : [
            {
               "long_name" : "1600",
               "short_name" : "1600",
               "types" : [ "street_number" ]
            },
            {
               "long_name" : "Pennsylvania Ave NW",
               "short_name" : "Pennsylvania Ave NW",
               "types" : [ "route" ]
            },
            {
               "long_name" : "Washington",
               "short_name" : "Washington",
               "types" : [ "locality", "political" ]
            },
            {
               "long_name" : "District of Columbia",
               "short_name" : "DC",
               "types" : [ "administrative_area_level_1", "political" ]
            },
            {
               "long_name" : "United States",
               "short_name" : "US",
               "types" : [ "country", "political" ]
            },
            {
               "long_name" : "20502",
               "short_name" : "20502",
               "types" : [ "postal_code" ]
            }
         ],
         "formatted_address" : "1600 Pennsylvania Ave NW, Washington, DC 20502, USA",
         "geometry" : {
            "location" : {
               "lat" : 38.89767770,
               "lng" : -77.03651700000002
            },
            "location_type" : "ROOFTOP",
            "viewport" : {
               "northeast" : {
                  "lat" : 38.89902668029149,
                  "lng" : -77.03516801970852
               },
               "southwest" : {
                  "lat" : 38.89632871970850,
                  "lng" : -77.03786598029153
               }
            }
         },
         "partial_match" : true,
         "types" : [ "street_address" ]
      },
      {
         "address_components" : [
            {
               "long_name" : "1600",
               "short_name" : "1600",
               "types" : [ "street_number" ]
            },
            {
               "long_name" : "Pennsylvania Ave NW",
               "short_name" : "Pennsylvania Ave NW",
               "types" : [ "route" ]
            },
            {
               "long_name" : "Washington",
               "short_name" : "Washington",
               "types" : [ "locality", "political" ]
            },
            {
               "long_name" : "District of Columbia",
               "short_name" : "DC",
               "types" : [ "administrative_area_level_1", "political" ]
            },
            {
               "long_name" : "United States",
               "short_name" : "US",
               "types" : [ "country", "political" ]
            },
            {
               "long_name" : "20500",
               "short_name" : "20500",
               "types" : [ "postal_code" ]
            }
         ],
         "formatted_address" : "1600 Pennsylvania Ave NW, Washington, DC 20500, USA",
         "geometry" : {
            "location" : {
               "lat" : 38.89871490,
               "lng" : -77.03765550
            },
            "location_type" : "ROOFTOP",
            "viewport" : {
               "northeast" : {
                  "lat" : 38.90006388029150,
                  "lng" : -77.03630651970849
               },
               "southwest" : {
                  "lat" : 38.89736591970851,
                  "lng" : -77.03900448029150
               }
            }
         },
         "partial_match" : true,
         "types" : [ "street_address" ]
      },
      {
         "address_components" : [
            {
               "long_name" : "1600",
               "short_name" : "1600",
               "types" : [ "street_number" ]
            },
            {
               "long_name" : "Pennsylvania Ave NW",
               "short_name" : "Pennsylvania Ave NW",
               "types" : [ "route" ]
            },
            {
               "long_name" : "Washington",
               "short_name" : "Washington",
               "types" : [ "locality", "political" ]
            },
            {
               "long_name" : "District of Columbia",
               "short_name" : "DC",
               "types" : [ "administrative_area_level_1", "political" ]
            },
            {
               "long_name" : "United States",
               "short_name" : "US",
               "types" : [ "country", "political" ]
            },
            {
               "long_name" : "20004",
               "short_name" : "20004",
               "types" : [ "postal_code" ]
            }
         ],
         "formatted_address" : "1600 Pennsylvania Ave NW, Washington, DC 20004, USA",
         "geometry" : {
            "location" : {
               "lat" : 38.89549710,
               "lng" : -77.03008090000002
            },
            "location_type" : "ROOFTOP",
            "viewport" : {
               "northeast" : {
                  "lat" : 38.89684608029150,
                  "lng" : -77.02873191970852
               },
               "southwest" : {
                  "lat" : 38.89414811970850,
                  "lng" : -77.03142988029153
               }
            }
         },
         "partial_match" : true,
         "types" : [ "street_address" ]
      },
      {
         "address_components" : [
            {
               "long_name" : "1600",
               "short_name" : "1600",
               "types" : [ "street_number" ]
            },
            {
               "long_name" : "Pennsylvania Ave SE",
               "short_name" : "Pennsylvania Ave SE",
               "types" : [ "route" ]
            },
            {
               "long_name" : "Hill East",
               "short_name" : "Hill East",
               "types" : [ "neighborhood", "political" ]
            },
            {
               "long_name" : "Washington",
               "short_name" : "Washington",
               "types" : [ "locality", "political" ]
            },
            {
               "long_name" : "District of Columbia",
               "short_name" : "DC",
               "types" : [ "administrative_area_level_1", "political" ]
            },
            {
               "long_name" : "United States",
               "short_name" : "US",
               "types" : [ "country", "political" ]
            },
            {
               "long_name" : "20003",
               "short_name" : "20003",
               "types" : [ "postal_code" ]
            }
         ],
         "formatted_address" : "1600 Pennsylvania Ave SE, Washington, DC 20003, USA",
         "geometry" : {
            "bounds" : {
               "northeast" : {
                  "lat" : 38.87865290,
                  "lng" : -76.98170180
               },
               "southwest" : {
                  "lat" : 38.87865220,
                  "lng" : -76.98170229999999
               }
            },
            "location" : {
               "lat" : 38.87865290,
               "lng" : -76.98170180
            },
            "location_type" : "RANGE_INTERPOLATED",
            "viewport" : {
               "northeast" : {
                  "lat" : 38.88000153029150,
                  "lng" : -76.98035306970850
               },
               "southwest" : {
                  "lat" : 38.87730356970850,
                  "lng" : -76.98305103029151
               }
            }
         },
         "partial_match" : true,
         "types" : [ "street_address" ]
      }
   ],
   "status" : "OK"
}    

一个可能的解决方案是只向用户请求邮政编码并使用 http://www.zippopotam.us/的 API 来获取州和城市,例如不确定这是否遵循您的 UX 设计您的搜索,但我使用他们的 API 使用 jQuery 完成了此操作,该 API 返回两个带有值的字段

   $("#text-4edcd39ecca23").keyup(function (event) {
        if (this.value.length === 5) {
            var $citywrap = $("#fm-item-text-4edcd393cb50f");
            var $city = $("#text-4edcd38744891");
            var $statewrap = $("#fm-item-text-4edcd38744891");
            var $state = $("#text-4edcd393cb50f");
            var $zip = $('#text-4edcd39ecca23');
            $.ajax({
                url:"http://zippo-zippopotamus.dotcloud.com/us/" + $zip.val(),
                cache:false,
                dataType:"json",
                type:"GET",
                data:"us/" + $zip.val(),
                success:function (result, success) {
                    // Remove Error Message if one is presant
                    $zip.parent().find('small').remove();
                    // US Zip Code Records Officially Map to only 1 Primary Location
                    var places = result['places'][0];
                    $city.val(places['place name']);
                    $state.val(places['state']);
                    $citywrap.slideDown();
                    $statewrap.slideDown();
                },
                error:function (result, success) {
                    $citywrap.slideUp();
                    $statewrap.slideUp();
                    $city.val('');
                    $state.val('');
                    $zip.parent().find('br').remove();
                    $zip.parent().find('small').remove();
                    $zip.after('<br /><small class="error">Sorry your zipcode was not reconized please try again</small>');
                }
            });
        }
    });