¿Cómo geocodificar 20 direcciones sin recibir una respuesta OVER_QUERY_LIMIT?

87

Usando Google Geocoder v3, si trato de geocodificar 20 direcciones, obtengo un OVER_QUERY_LIMIT a menos que las programe para que estén separadas por ~ 1 segundo, pero luego pasan 20 segundos antes de que se coloquen todos mis marcadores.

¿Hay alguna otra forma de hacerlo, además de almacenar las coordenadas por adelantado?

Michiel van Oosterhout
fuente
¿sigue siendo así? La única restricción que veo en la documentación es: "un límite de consulta de 2.500 solicitudes de geolocalización por día". code.google.com/apis/maps/documentation/geocoding/…
russau
6
No se trata de la cantidad total de consultas por usuario por día, se trata de la cantidad de consultas en un corto período de tiempo, como cuando se consulta en un bucle.
Michiel van Oosterhout
Tenemos una licencia comercial en nuestra tienda y todavía nos encontramos con el problema de no poder manejar más de 10 solicitudes por segundo. La única diferencia entre una licencia comercial y un desarrollador regular es que tenemos un límite de 100.000 llamadas por día.
abhi
@michielvoo ¿Has resuelto esto? Si es así, entonces amablemente ayúdame. Obtengo OVER_QUERY_LIMIT. Mi pregunta en SO. Fiddle
Prabs

Respuestas:

85

No, realmente no hay otra manera: si tiene muchas ubicaciones y desea mostrarlas en un mapa, la mejor solución es:

  • obtener la latitud + longitud, utilizando el geocodificador, cuando se crea una ubicación
  • almacenarlos en su base de datos, junto con la dirección
  • y utilice los valores de latitud + longitud almacenados cuando desee visualizar el mapa.

Esto es, por supuesto, considerando que tiene mucha menos creación / modificación de ubicaciones que consultas de ubicaciones.


Sí, significa que tendrá que trabajar un poco más al guardar las ubicaciones, pero también significa:

  • Podrás buscar por coordenadas geográficas
    • es decir, " quiero una lista de puntos que están cerca de donde estoy ahora "
  • Mostrar el mapa será mucho más rápido
    • Incluso con más de 20 ubicaciones en él
  • Ah, y también (por último, pero no menos importante) : esto funcionará ;-)
    • Es menos probable que alcance el límite de X llamadas del geocodificador en N segundos.
    • Y es menos probable que alcance el límite de Y llamadas de geocodificador por día.
Pascal MARTIN
fuente
Tengo curiosidad por saber cómo puede estar seguro de que los resultados son correctos después de un tiempo (digamos, un mes). ¿Los vuelve a consultar de vez en cuando?
Chris
2
Si la dirección (que ya tiene en su base de datos; de lo contrario, no podría geocodificar) no cambia, es muy probable que cambie la latitud / longitud. Y, por supuesto, cada vez que se modifica la dirección, debe volver a consultar el geocodificador, para obtener la latitud + longitud que corresponden a la nueva dirección.
Pascal MARTIN
He almacenado la latitud / longitud en la base de datos y la recuperé de la base de datos a través de AJAX como una matriz, pero luego debería pasar nuevamente a un bucle de script java, además he recibido 173 ubicaciones de DB. Ahora me muestra el mismo estado OVER_QUERY_LIMIT. Por favor, consejo ...
Prabhu M
20

En realidad, no tiene que esperar un segundo completo para cada solicitud. Descubrí que si espero 200 milisegundos entre cada solicitud, puedo evitar la respuesta OVER_QUERY_LIMIT y la experiencia del usuario es aceptable. Con esta solución puede cargar 20 artículos en 4 segundos.

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

  setTimeout(function(){

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

  }, 200 * i);

}
gabeodess
fuente
5
pero (200 * i) significa que la pausa entre cada solicitud está aumentando. Así, el 3 de petición es 600, entonces 800 etc.
Romano
simplemente elimine la '* i'
Chris
9
setTimeout lo ejecutará una vez. Entonces, si estoy en lo cierto, (..., 200 * i) programaré cada llamada separada por 200ms (como comentó oyatek), que es lo que gabeodess quería lograr. La corriente (..., 200) los ejecutará todos al mismo tiempo después de 200ms. ¿O me estoy perdiendo algo?
lepe
@gabeodess: debe hacerlo setIntervalen la cantidad de solicitudes necesarias, en lugar de setTimeout, y establecerlo en 100, en caso de que la cantidad de la dirección en algún momento en el futuro extienda la 20cantidad.
Rob Scott
3
@gabeodess Probé tu solución, pero todavía obtengo OVER_QUERY_LIMIT Fiddle
Prabs
6

Desafortunadamente, esta es una restricción del servicio de mapas de Google.

Actualmente estoy trabajando en una aplicación que utiliza la función de codificación geográfica y estoy guardando cada dirección única por usuario. Genero la información de la dirección (ciudad, calle, estado, etc.) en función de la información devuelta por los mapas de Google y luego también guardo la información de latitud y longitud en la base de datos. Esto evita que tenga que volver a codificar cosas y le brinda direcciones con un formato agradable.

Otra razón por la que desea hacer esto es porque existe un límite diario en la cantidad de direcciones que se pueden geocodificar desde una dirección IP en particular. No desea que su solicitud falle para una persona por ese motivo.

Zachary Wright
fuente
2

Estoy enfrentando el mismo problema al intentar geocodificar 140 direcciones.

Mi solución fue agregar usleep (100000) para cada ciclo de la siguiente solicitud de codificación geográfica . Si el estado de la solicitud es OVER_QUERY_LIMIT, el usleep se incrementa en 50000 y la solicitud se repite, y así sucesivamente.

Y, por lo tanto, todos los datos recibidos (lat / long) se almacenan en un archivo XML para no ejecutar la solicitud cada vez que se carga la página.

gris
fuente
1
Su respuesta es vaga, ¿se refiere al lado del servidor o es este javascript? Si es el último, usleep no es una función y, por lo tanto, sería incorrecto. Si es el primero, le sugiero que modifique su respuesta para indicarlo explícitamente. es del lado del servidor para evitar ambigüedades.
t0mm13b
1

EDITAR:

Olvidé decir que esta solución está en js puro, lo único que necesita es un navegador que admita promesas https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Promise


Para aquellos que todavía necesitan lograrlo, he escrito mi propia solución que combina promesas con tiempos de espera.

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

Tenga en cuenta que es solo una parte de una biblioteca más grande que escribí para manejar cosas de Google Maps, por lo que los comentarios pueden ser confusos.

El uso es bastante simple, sin embargo, el enfoque es ligeramente diferente: en lugar de hacer un bucle y resolver una dirección a la vez, deberá pasar una matriz de direcciones a la clase y esta manejará la búsqueda por sí misma, devolviendo una promesa que , cuando se resuelve, devuelve una matriz que contiene todas las direcciones resueltas (y no resueltas).

Ejemplo:

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

Salida de consola:

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 devuelto:

ingrese la descripción de la imagen aquí

Toda la magia sucede aquí:

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

Básicamente, repite cada elemento con un retraso de 750 milisegundos entre cada uno de ellos, por lo que cada 750 milisegundos se controla una dirección.

Hice algunas pruebas adicionales y descubrí que incluso en 700 milisegundos a veces recibía el error QUERY_LIMIT, mientras que con 750 no he tenido ningún problema.

En cualquier caso, siéntase libre de editar el 750 anterior si se siente seguro manejando un retraso menor.

Espero que esto ayude a alguien en un futuro cercano;)

briosheje
fuente
0

Acabo de probar Google Geocoder y tengo el mismo problema que tú. Noté que solo obtengo el estado OVER_QUERY_LIMIT una vez cada 12 solicitudes. Así que espero 1 segundo (ese es el retraso mínimo para esperar). Ralentiza la aplicación, pero menos de esperar 1 segundo cada solicitud.

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
}

Con el método holdOn básico:

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

Espero eso ayude

Hugues
fuente