Частичный поиск адресов / Typeahead - PullRequest
2 голосов
/ 08 ноября 2011

У нас есть поле адреса, для которого мы хотим указать typeahead. Он находится за логином, хотя если бы нам было нужно, мы могли бы стать хитроумными и сделать эту страницу общедоступной для соответствия лицензированию.

API Карт Google блокируется. Мы использовали его часть «обратный геокод» для частичного поиска адресов / typeahead для адресов - например, если пользователь набрал:

1600 пенн

Я могу обратиться к службе и получить несколько предложений, например:

1600 Пенсильвания авеню, Вашингтон, округ Колумбия

Я встречался с несколькими другими частичными поисками адресов, но у каждого из них есть проблемы.

Google: минимум 10000 долларов в год всего за 7500 запросов в день - смешно

Yahoo: завершение работы в этом году

Bing: требует, чтобы страница была общедоступной / не входила в систему. Для нас это не трудная остановка, но это будет сложная модернизация работы страницы.

Mapquest OpenStreetMap API: ищет точную строку, а не начальную строку, поэтому он возвращает Penn Station вместо 1600 Pennsylvania Ave.

Данные Mapquest OpenStreetMap: мы могли бы загрузить все это и реализовать свои собственные, но требования к процессору и данным, вероятно, пока слишком велики.

У нас все хорошо с оплатой за использование, мы бы просто искали решение, более близкое к запросам Amazon на 0.01 / 10000, чем к Google.

Ответы [ 3 ]

1 голос
/ 20 января 2012

ОБНОВЛЕНИЕ: Мой первоначальный ответ, без изменений ниже, был дан до того, как SmartyStreets предложили автозаполнение адреса , которое бесплатно при наличии подписки LiveAddress API.1007 * Это действительно просто.USPS одобрил определенных поставщиков услуг по проверке / стандартизации адресов.Я работаю в одной такой компании, SmartyStreets , и, по моему опыту, то, что вы пытаетесь сделать, вероятно, проще, чем вы думаете, с хорошим API, который имеет конечную точку REST.Просмотрите API LiveAddress.

Отправьте адрес улицы и, по крайней мере, город / штат, или почтовый индекс, или оба, и вы получите список предлагаемых результатов.

ПРИМЕЧАНИЕ, однакочто провайдеры, не входящие в CASS, такие как Карты Google, применяют приближение адреса, а не проверку адреса.Google - и другие - делают "лучшую догадку", в чем Google и они являются экспертами.Если вы хотите фактические существующие адреса, убедитесь, что вы найдете службу, которая делает именно это.Добавлю, что LiveAddress теперь бесплатен и предлагает лучшую производительность, чем, например, API USPS.

0 голосов
/ 03 мая 2012

Обновлено

Google выпустила Автозаполнение Google Адресов , которое решает именно эту проблему.

Внизу страницы добавьте туда:

<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 />

и т.д.

Затем вы инициализируете его (до того, как обычно загружается API Google Places, так как он будет обрабатываться асинхронно) с помощью:

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

Или что угодно. В этом случае он привязывает адрес типа к любому входу, имеющему name = Address в тегах #billing и #shipping, и заполняет соответствующие поля внутри этих тегов для 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 в надежде получить белый дом к тому времени, когда вы наберете "1600 Penn", но до того, как он ответит этим адресом, придется пройти до "1600 pennsylvania ave, вашингтон, округ Колумбия". Тем не менее, это может принести небольшую пользу пользователям в экономии времени.

0 голосов
/ 08 ноября 2011

Как получается, что делать это самостоятельно не вариант?IMO, частичный поиск или ввод текста не так уж сложно сделать с тройным набором в поле адреса, улицы, города и т. Д .pp.

...