部分地址搜索/预先输入

时间:2011-11-08 09:55:12

标签: api search partial street-address

我们有一个我们想要提供输入的地址字段。它位于登录后面,但如果我们需要,我们可以变得狡猾,并将这一页公开以获得许可合规性。

Google Maps API已被锁定。我们曾经使用它的“反向地理编码”部分来执行地址的部分地址搜索/预先输入 - 例如,如果用户键入:

1600 Penn

我可以点击该服务并获得一些建议,例如:

1600 Washington Avenue,Washington,DC

我遇到过其他几个部分地址搜索但他们每个人都有问题。

谷歌:每天仅需7500个请求,每年最低10000美元 - 荒谬

雅虎:今年关闭

Bing:要求页面公开/不在登录后面。这对我们来说并不是一件难事,但对页面的工作方式进行重新设计将是一项挑战。

Mapquest OpenStreetMap API:搜索确切的字符串而不是前导字符串 - 因此它返回Penn Station而不是1600 Pennsylvania Ave.

Mapquest OpenStreetMap数据:我们可以下载所有这些并实现我们自己的数据,但CPU和数据要求可能暂时不会太多。

我们支付使用费用很好,我们只是寻求一个比亚马逊的$ 0.01 / 10000要求更接近Google的解决方案。

2 个答案:

答案 0 :(得分:1)

更新:我的原始答案,完整地在下面,在SmartyStreets提供address autocomplete之前提供,使用LiveAddress API订阅免费提供。


这很简单,真的。 USPS已批准某些供应商提供地址验证/标准化服务。我为一家这样的公司SmartyStreets工作,根据我的经验,你想要做的事情可能比你想到的具有REST端点的好API更容易。查看LiveAddress API。

提交街道地址,至少提供城市/州或邮政编码,或两者兼而有之,您将获得建议结果列表。

但是,请注意,Google地图等非CASS提供商确实会解决近似问题,而不会解决地址验证问题。谷歌和其他人做出了“最好的猜测”,谷歌和他们都是专家。如果您想要实际的现有地址,请确保找到能够实现此目的的服务。我将补充说LiveAddress现在是免费的,并提供比USPS API更好的性能。

答案 1 :(得分:0)

更新

Google发布了Google Places Autocomplete,解决了这个问题。

在页面底部将其放入:

<script defer async src="//maps.googleapis.com/maps/api/js?libraries=places&key=(key)&sensor=false&callback=googPlacesInit"></script>

(key)是您的API密钥。

我们设置了我们的代码,因此您可以标记某些字段以处理预先输入并通过该类型输入填写,例如:

<input name=Address placeholder=Address />
<input name=Zip placeholder=Zip />

然后您初始化它(通常在Google Places API加载之前,因为它将导致异步):

GoogleAddress.init('#billing input', '#shipping input');

或者其他什么。在这种情况下,它将地址预先输入绑定到#billing标记和#shipping标记中具有name = Address的任何输入,并且当地址时它将填充City,State,Zip等标记内的相关字段被选中。

设置课程:

var GoogleAddress = {
    AddressFields: [],
    //ZipFields: [],    // Not in use and the support code is commented out, for now
    OnSelect: [],

    /**
     * @param {String} field Pass as many arguments as you like, each a selector to a set of inputs that should use Google
     * Address Typeahead via the Google Places API.
     * 
     * Mark the inputs with name=Address, name=City, name=State, name=Zip, name=Country
     * All fields are optional; you can for example leave Country out and everything else will still work.
     * 
     * The Address field will be used as the typeahead field. When an address is picked, the 5 fields will be filled in.
     */
    init: function (field) {
        var args = $.makeArray(arguments);
        GoogleAddress.AddressFields = $.map(args, function (selector) {
            return $(selector);
        });
    }
};

上面的脚本片段将异步调用到名为googPlacesInit的函数中,因此其他所有内容都被该名称包含在函数中:

function googPlacesInit() {
    var fields = GoogleAddress.AddressFields;

    if (
        // If Google Places fails to load, we need to skip running these or the whole script file will fail
        typeof (google) == 'undefined' ||
        // If there's no input there's no typeahead so don't bother initializing
        fields.length == 0 || fields[0].length == 0
    )
        return;

设置自动完成事件,并处理我们始终使用多个地址字段的事实,但Google希望将整个地址转储到单个输入中。肯定有办法防止这种情况发生,但我还没有找到它。

$.each(fields, function (i, inputs) {
    var jqInput = inputs.filter('[name=Address]');

    var addressLookup = new google.maps.places.Autocomplete(jqInput[0], {
        types: ['address']
    });
    google.maps.event.addListener(addressLookup, 'place_changed', function () {
        var place = addressLookup.getPlace();

        // Sometimes getPlace() freaks out and fails - if so do nothing but blank out everything after comma here.
        if (!place || !place.address_components) {
            setTimeout(function () {
                jqInput.val(/^([^,]+),/.exec(jqInput.val())[1]);
            }, 1);
            return;
        }

        var a = parsePlacesResult(place);

        // HACK! Not sure how to tell Google Places not to set the typeahead field's value, so, we just wait it out
        // then overwrite it
        setTimeout(function () {
            jqInput.val(a.address);
        }, 1);

        // For the rest, assign by lookup
        inputs.each(function (i, input) {
            var val = getAddressPart(input, a);
            if (val)
                input.value = val;
        });

        onGoogPlacesSelected();
    });

    // Deal with Places API blur replacing value we set with theirs
    var removeGoogBlur = function () {
        var googBlur = jqInput.data('googBlur');
        if (googBlur) {
            jqInput.off('blur', googBlur).removeData('googBlur');
        }
    };

    removeGoogBlur();
    var googBlur = jqInput.blur(function () {
        removeGoogBlur();
        var val = this.value;
        var _this = this;
        setTimeout(function () {
            _this.value = val;
        }, 1);
    });
    jqInput.data('googBlur', googBlur);
});

// Global goog address selected event handling
function onGoogPlacesSelected() {
    $.each(GoogleAddress.OnSelect, function (i, fn) {
        fn();
    });
}

将结果解析为规范的街道1,街道2,城市,州/省,邮政编码并非易事。 Google会根据您所在的世界的不同来区分这些地区,并且作为警告,例如非洲有些地方可以满足您对地址的期望。您可以将世界上的地址分为3类:

  • 美国完全相同 - 整个美国和几个使用类似寻址系统的国家

  • 正式地址 - 英国,澳大利亚,中国,基本上是发达国家 - 但他们的地址部分是标签的方式存在相当大的差异

  • 没有正式地址 - 在未开发的地区,没有街道名称,更不用说街道号码,有时甚至不是城镇/城市名称,当然也没有拉链。在这些位置,您真正想要的是GPS位置,此代码无法处理。

此代码仅尝试处理前两种情况。

function parsePlacesResult(place) {
    var a = place.address_components;

    var p = {};
    var d = {};

    for (var i = 0; i < a.length; i++) {
        var ai = a[i];
        switch (ai.types[0]) {
            case 'street_number':
                p.num = ai.long_name;
                break;
            case 'route':
                p.rd = ai.long_name;
                break;
            case 'locality':
            case 'sublocality_level_1':
            case 'sublocality':
                d.city = ai.long_name;
                break;
            case 'administrative_area_level_1':
                d.state = ai.short_name;
                break;
            case 'country':
                d.country = ai.short_name;
                break;
            case 'postal_code':
                d.zip = ai.long_name;
        }
    }

    var addr = [];
    if (p.num)
        addr.push(p.num);
    if (p.rd)
        addr.push(p.rd);

    d.address = addr.join(' ');

    return d;
}

/**
 * @param input  An Input tag, the DOM element not a jQuery object
 * @paran a     A Google Places Address object, with props like .city, .state, .country...
 */
var getAddressPart = function(input, a) {
    switch(input.name) {
        case 'City': return a.city;
        case 'State': return a.state;
        case 'Zip': return a.zip;
        case 'Country': return a.country;
    }
    return null;
}

旧答案

ArcGis / ESRI具有有限的预先解决方案功能,但只有在输入相当多的情况下才会返回有限的结果。这里有一个演示:

http://www.esri.com/services/disaster-response/wildlandfire/latest-news-map.html

例如,您可以键入1600 Pennsylvania Ave,希望在您输入#34; 1600 Penn&#34;时获得白宫,但必须达到&#34; 1600宾夕法尼亚大街,华盛顿特区&# 34;在它回复该地址之前。尽管如此,它可以为用户节省时间。