【问题标题】:Partial Address Search / Typeahead部分地址搜索/预输入
【发布时间】:2011-11-08 09:55:11
【问题描述】:

我们有一个地址字段,我们想为其提供预先输入。它位于登录后面,但如果我们需要,我们可以狡猾地公开该页面以符合许可规定。

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

1600 佩恩

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

华盛顿特区宾夕法尼亚大道 1600 号

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

Google:每天 7500 次请求,最低 10000 美元 - 荒谬

雅虎:今年关闭

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

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

Mapquest OpenStreetMap 数据:我们可以下载所有这些并实现我们自己的,但 CPU 和数据要求可能暂时无法满足。

我们可以为使用付费,我们只是寻求一种比 Google 更接近亚马逊 0.01 美元/10000 美元请求的解决方案。

【问题讨论】:

    标签: api search partial street-address


    【解决方案1】:

    更新:我的原始答案(完整如下)是在 SmartyStreets 提供 address autocomplete 产品之前给出的,该产品通过 LiveAddress API 订阅免费提供。


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

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

    但请注意,非 CASS 提供商(例如 Google 地图)会进行地址近似,而不是地址验证。谷歌 - 和其他人 - 做出“最佳猜测”,这是谷歌和他们的专家。如果您想要实际的现有地址,请确保找到可以做到这一点的服务。我要补充一点,LiveAddress 现在是免费的,并且提供比 USPS API 等更好的性能。

    【讨论】:

    • 如果您要求用户在验证前输入完整地址,这似乎没问题,但如果您实施真正的自动完成功能(如公司前面的那个),成本可能会失控页面,例如)。免费层的使用限制使其除了演示产品外毫无用处。
    • @ach 实际上,SmartyStreets 的自动补全服务是完全免费的,只要有一个帐户,就可以像您在主页上看到的那样进行预输入:smartystreets.com/kb/faq/autocomplete-endpoint
    【解决方案2】:

    为什么不能自己动手? IMO 部分搜索或预输入对于地址、街道、城市 etc.pp 字段的三元树并不难。

    【讨论】:

    • 客户端的服务器已经处于高负载状态。更大的产品(如谷歌)对这些查询有边缘缓存的响应,我们最好的情况会慢得多。这似乎是应该有答案的那些共同的大问题之一。
    【解决方案3】:

    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');
    

    或其他。 In this case it's tying the address typeahead to whatever input has name=Address in the #billing tag and #shipping tag, and it will fill in the related fields inside those tags for City, State, Zip etc when an address is chosen.

    设置类:

    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);
            });
        }
    };
    

    上面的脚本 sn-p 将异步调用一个名为 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();
        });
    }
    

    将结果解析为规范的 street1、street2、城市、州/省、邮政编码并非易事。谷歌会根据您在世界上的哪个位置使用不同的标签来区分这些地区,并且作为警告,如果您习惯于美国地址,那么例如在非洲,有些地方不符合您对地址外观的期望。您可以将世界上的地址分为 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 有一个有限的 typeahead 解决方案,该解决方案是有效的,但只有在相当多的输入后才返回有限的结果。这里有一个演示:

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

    例如,您可能会输入 1600 Pennsylvania Ave,希望在您输入“1600 Penn”时获得白宫,但必须到达“1600 pennsylvania ave,washington dc”才能响应该地址。不过,在节省时间方面,它可能会给用户带来一点好处。

    【讨论】:

    • 这可能是一个解决方案,问题是谷歌没有提供真实地址。相反,它们会根据它们应该在的位置*(如果它们存在)来近似地址。他们也不会为公寓号码提供预先输入的建议。如果您这样做是为了地图和导航,那就太好了,但如果要用于邮寄/送货地址,那就不好了。
    猜你喜欢
    • 1970-01-01
    • 2014-12-17
    • 1970-01-01
    • 2020-08-02
    • 2015-03-22
    • 2018-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多