Como faço para geocodificar 20 endereços sem receber uma resposta OVER_QUERY_LIMIT?

87

Usando o Google Geocoder v3, se eu tentar geocodificar 20 endereços, obtenho um OVER_QUERY_LIMIT, a menos que o tempo eles tenham cerca de 1 segundo de intervalo, mas leva 20 segundos para que todos os meus marcadores sejam colocados.

Existe alguma outra maneira de fazer isso, além de armazenar as coordenadas com antecedência?

Michiel van Oosterhout
fonte
ainda é o caso? A única restrição que vejo na documentação é: "um limite de consulta de 2.500 solicitações de geolocalização por dia". code.google.com/apis/maps/documentation/geocoding/…
russau
6
Não se trata da quantidade total de consultas por usuário por dia, mas sim do número de consultas em um curto espaço de tempo, como quando se faz uma consulta em loop.
Michiel van Oosterhout
Temos uma licença comercial em nossa loja e ainda nos deparamos com o problema de não sermos capazes de lidar com mais de 10 solicitações por segundo. A única diferença entre uma licença comercial e um desenvolvedor regular é que temos um limite máximo de 100.000 chamadas por dia.
abhi
@michielvoo Você resolveu isso? Se sim, gentilmente me ajude. Estou recebendo OVER_QUERY_LIMIT. Minha pergunta no SO. Fiddle
Prabs de

Respostas:

85

Não, não há realmente outra maneira: se você tem muitos locais e deseja exibi-los em um mapa, a melhor solução é:

  • buscar a latitude + longitude, usando o geocodificador, quando um local é criado
  • armazene-os em seu banco de dados, junto com o endereço
  • e use a latitude + longitude armazenada quando quiser exibir o mapa.

Isso, é claro, considerando que você tem muito menos criação / modificação de locais do que consultas de locais.


Sim, isso significa que você terá que trabalhar um pouco mais ao salvar os locais - mas também significa:

  • Você poderá pesquisar por coordenadas geográficas
    • ou seja, " Eu quero uma lista de pontos que estão perto de onde estou agora "
  • Exibir o mapa será muito mais rápido
    • Mesmo com mais de 20 locais nele
  • Ah, e também (por último, mas não menos importante) : isso vai funcionar ;-)
    • É menos provável que você alcance o limite de chamadas do geocodificador X em N segundos.
    • E é menos provável que você alcance o limite de Y chamadas do geocodificador por dia.
Pascal MARTIN
fonte
Estou curioso para saber como você pode ter certeza de que os resultados estão corretos depois de algum tempo (digamos, um mês). Você os consulta novamente de vez em quando?
Chris
2
Se o endereço (que você já tem em seu banco de dados - caso contrário, você não seria capaz de geocodificar) não mudar, as chances são muito baixas de que a latitude / longitude mude. E, é claro, cada vez que o endereço for modificado, você deve consultar novamente o geocodificador para obter a latitude + longitude que corresponde ao novo endereço.
Pascal MARTIN
Eu armazenei a latitude / longitude no banco de dados e recuperei-a do banco de dados via AJAX como um array, mas ele deve ser passado novamente para um loop de script java, mais sobre eu recebi 173 locais do banco de dados. Agora ele me mostra o mesmo status OVER_QUERY_LIMIT. Por favor, conselhos ...
Prabhu M
20

Na verdade, você não precisa esperar um segundo inteiro para cada solicitação. Descobri que, se eu esperar 200 milissegundos entre cada solicitação, posso evitar a resposta de OVER_QUERY_LIMIT e a experiência do usuário é aceitável. Com esta solução você pode carregar 20 itens em 4 segundos.

$(items).each(function(i, item){

  setTimeout(function(){

    geoLocate("my address", function(myLatlng){
      ...
    });

  }, 200 * i);

}
gabeodess
fonte
5
mas (200 * i) significa que a pausa entre cada solicitação está aumentando. Então, na terceira solicitação é 600, depois 800 etc.
Romano
apenas remova o '* i'
Chris
9
setTimeout irá executá-lo uma vez. Então, se eu estiver correto, (..., 200 * i) irá agendar cada chamada separada por 200ms (como oyatek comentou), que é o que gabeodess queria alcançar. O atual (..., 200) executará todos eles ao mesmo tempo após 200ms. Ou eu estou esquecendo de alguma coisa?
lepe
@gabeodess - você deve fazer setIntervalo número de solicitações necessárias, em vez de setTimeout, e definir como 100- apenas no caso de o valor do endereço em algum momento no futuro aumentar o 20valor.
Rob Scott
3
@gabeodess Tentei sua solução, mas ainda estou recebendo OVER_QUERY_LIMIT Fiddle
Prabs de
6

Infelizmente, esta é uma restrição do serviço Google Maps.

Atualmente, estou trabalhando em um aplicativo que usa o recurso de geocodificação e salvando cada endereço exclusivo por usuário. Eu gero as informações de endereço (cidade, rua, estado, etc) com base nas informações retornadas pelo Google maps e, em seguida, salvo as informações de latitude / longitude no banco de dados também. Isso evita que você tenha que recodificar as coisas e fornece endereços formatados de maneira adequada.

Outro motivo pelo qual você deseja fazer isso é porque há um limite diário no número de endereços que podem ser geocodificados a partir de um endereço IP específico. Você não quer que seu aplicativo falhe para uma pessoa por esse motivo.

Zachary Wright
fonte
2

Estou enfrentando o mesmo problema ao tentar geocodificar 140 endereços.

Minha solução alternativa foi adicionar usleep (100.000) para cada loop da próxima solicitação de geocodificação. Se o status da solicitação for OVER_QUERY_LIMIT, o usleep será aumentado em 50000 e a solicitação será repetida e assim por diante.

E, obviamente, todos os dados recebidos (latitude / longitude) são armazenados em um arquivo XML para não executar a solicitação toda vez que a página estiver carregando.

cinzento
fonte
1
Sua resposta é vaga, você está se referindo ao lado do servidor ou este é javascript, se for o último, usleep não é uma função e, portanto, seria incorreto, se for o primeiro, então sugiro que você altere sua resposta para declarar explicitamente isso está do lado do servidor para evitar ambigüidade.
t0mm13b
1

EDITAR:

Esqueci de dizer que essa solução é em js puro, a única coisa que você precisa é de um navegador que suporte promessas https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Promise


Para aqueles que ainda precisam realizar isso, escrevi minha própria solução que combina promessas com tempos limite.

Código:

/*
    class: Geolocalizer
        - Handles location triangulation and calculations.
        -- Returns various prototypes to fetch position from strings or coords or dragons or whatever.
*/

var Geolocalizer = function () {
    this.queue          = [];     // queue handler..
    this.resolved       = [];
    this.geolocalizer = new google.maps.Geocoder();  
};

Geolocalizer.prototype = {
    /*
        @fn: Localize
        @scope: resolve single or multiple queued requests.
        @params: <array> needles
        @returns: <deferred> object
    */
    Localize: function ( needles ) {
        var that = this;
        // Enqueue the needles.
        for ( var i = 0; i < needles.length; i++ ) {
            this.queue.push(needles[i]);
        }
        // return a promise and resolve it after every element have been fetched (either with success or failure), then reset the queue.
        return new Promise (
            function (resolve, reject) {
                that.resolveQueueElements().then(function(resolved){
                  resolve(resolved);
                  that.queue    = [];
                  that.resolved = [];
                });
            }
        );
    },

    /*
        @fn: resolveQueueElements
        @scope: resolve queue elements.
        @returns: <deferred> object (promise)
    */

    resolveQueueElements: function (callback) {
        var that = this;
        return new Promise(
            function(resolve, reject) {
                // Loop the queue and resolve each element.
                // Prevent QUERY_LIMIT by delaying actions by one second.
                (function loopWithDelay(such, queue, i){
                    console.log("Attempting the resolution of " +queue[i-1]);
                    setTimeout(function(){
                        such.find(queue[i-1], function(res){
                           such.resolved.push(res); 
                        });
                        if (--i) {
                            loopWithDelay(such,queue,i);
                        }
                    }, 1000);
                })(that, that.queue, that.queue.length);

                // Check every second if the queue has been cleared.
                var it = setInterval(function(){
                    if (that.queue.length == that.resolved.length) {
                        resolve(that.resolved);
                        clearInterval(it);
                    }
                }, 1000);
            }
        );
    },

    /*
        @fn: find
        @scope: resolve an address from string
        @params: <string> s, <fn> Callback
    */
    find: function (s, callback) {
        this.geolocalizer.geocode({
            "address": s
        }, function(res, status){
           if (status == google.maps.GeocoderStatus.OK) {
               var r = {
                   originalString:  s,
                   lat: res[0].geometry.location.lat(),
                   lng: res[0].geometry.location.lng()
               };
               callback(r);
           }
            else {
                callback(undefined);
                console.log(status);
                console.log("could not locate " + s);
            }
        });
    }
};

Observe que é apenas uma parte de uma biblioteca maior que escrevi para lidar com as coisas do Google Maps, portanto, os comentários podem ser confusos.

O uso é bastante simples, a abordagem, no entanto, é um pouco diferente: em vez de fazer um loop e resolver um endereço de cada vez, você precisará passar uma matriz de endereços para a classe e ela cuidará da pesquisa por si mesma, retornando uma promessa que , quando resolvido, retorna uma matriz contendo todos os endereços resolvidos (e não resolvidos).

Exemplo:

var myAmazingGeo = new Geolocalizer();
var locations = ["Italy","California","Dragons are thugs...","China","Georgia"];
myAmazingGeo.Localize(locations).then(function(res){ 
   console.log(res); 
});

Saída do console:

Attempting the resolution of Georgia
Attempting the resolution of China
Attempting the resolution of Dragons are thugs...
Attempting the resolution of California
ZERO_RESULTS
could not locate Dragons are thugs...
Attempting the resolution of Italy

Objeto devolvido:

insira a descrição da imagem aqui

Toda a magia acontece aqui:

(function loopWithDelay(such, queue, i){
                    console.log("Attempting the resolution of " +queue[i-1]);
                    setTimeout(function(){
                        such.find(queue[i-1], function(res){
                           such.resolved.push(res); 
                        });
                        if (--i) {
                            loopWithDelay(such,queue,i);
                    }
                }, 750);
            })(that, that.queue, that.queue.length);

Basicamente, ele faz um loop em cada item com um atraso de 750 milissegundos entre cada um deles, portanto, a cada 750 milissegundos, um endereço é controlado.

Fiz mais alguns testes e descobri que mesmo em 700 milissegundos às vezes recebia o erro QUERY_LIMIT, enquanto com 750 não tive nenhum problema.

Em qualquer caso, sinta-se à vontade para editar o 750 acima se achar que está seguro lidando com um atraso menor.

Espero que isso ajude alguém em um futuro próximo;)

Briosheje
fonte
0

Acabei de testar o Google Geocoder e tive o mesmo problema que você. Percebi que só obtenho o status OVER_QUERY_LIMIT uma vez a cada 12 solicitações. Portanto, espero 1 segundo (esse é o atraso mínimo). Isso torna o aplicativo mais lento, mas menos do que esperar 1 segundo a cada solicitação

info = getInfos(getLatLng(code)); //In here I call Google API
record(code, info);
generated++; 
if(generated%interval == 0) {
holdOn(delay); // Every x requests, I sleep for 1 second
}

Com o método holdOn básico:

private void holdOn(long delay) {
        try {
            Thread.sleep(delay);
        } catch (InterruptedException ex) {
            // ignore
        }
    }

Espero que ajude

Hugues
fonte