¿Evitar el límite de geocodificación de Google Maps?

32

Estoy creando un mapa de Google personalizado que tiene 125 marcadores trazados a través de un cms. Al cargar el mapa recibo este mensaje:

  

Geocode no tuvo éxito por el siguiente motivo: OVER_QUERY_LIMIT

Estoy bastante seguro de que es la forma en que he geocodificado los marcadores.

¿Cómo puedo evitar estas advertencias y hay una manera más eficiente de geocodificar los resultados?

ACTUALIZACIÓN: Este es mi intento por la respuesta de Casey, solo estoy obteniendo una página en blanco en este momento.

<script type="text/javascript"> 
(function() { 

window.onload = function() { 
 var mc;
// Creating an object literal containing the properties we want to pass to the map 
var options = { 
zoom: 10, 
center: new google.maps.LatLng(52.40, -3.61), 
mapTypeId: google.maps.MapTypeId.ROADMAP 
}; 

// Creating the map 
var map = new google.maps.Map(document.getElementById('map'), options); 

// Creating a LatLngBounds object 
var bounds = new google.maps.LatLngBounds(); 

// Creating an array that will contain the addresses 
var places = []; 

// Creating a variable that will hold the InfoWindow object 
var infowindow; 
mc = new MarkerClusterer(map);
<?php
$pages = get_pages(array('child_of' => $post->ID, 'sort_column' => 'menu_order'));
$popup_content = array();
foreach($pages as $post)
    {
    setup_postdata($post);
    $fields = get_fields(); 
    $popup_content[] = '<p>'.$fields->company_name.'</p><img src="'.$fields->company_logo.'" /><br /><br /><a href="'.get_page_link($post->ID).'">View profile</a>';
    $comma = ", ";
    $full_address = "{$fields->address_line_1}{$comma}{$fields->address_line_2}{$comma}{$fields->address_line_3}{$comma}{$fields->post_code}";
    $address[] = $full_address;
    }
wp_reset_query();
echo 'var popup_content = ' . json_encode($popup_content) . ';';
echo 'var address = ' . json_encode($address) . ';';
?>

var geocoder = new google.maps.Geocoder(); 

var markers = [];

// Adding a LatLng object for each city  
for (var i = 0; i < address.length; i++) { 
    (function(i) { 
        geocoder.geocode( {'address': address[i]}, function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                places[i] = results[0].geometry.location;

                // Adding the markers 
                var marker = new google.maps.Marker({position: places[i], map: map});
                markers.push(marker);
                mc.addMarker(marker);

                // Creating the event listener. It now has access to the values of i and marker as they were during its creation
                google.maps.event.addListener(marker, 'click', function() {
                    // Check to see if we already have an InfoWindow
                    if (!infowindow) {
                        infowindow = new google.maps.InfoWindow();
                    }

                    // Setting the content of the InfoWindow
                    infowindow.setContent(popup_content[i]);

                    // Tying the InfoWindow to the marker 
                    infowindow.open(map, marker);
                });

                // Extending the bounds object with each LatLng 
                bounds.extend(places[i]); 

                // Adjusting the map to new bounding box 
                map.fitBounds(bounds) 
            } else { 
            alert("Geocode was not successful for the following reason: " + status); 
            }

        });

    })(i);

} 
var markerCluster = new MarkerClusterer(map, markers); 
} 
})
(); 
</script> 

En realidad, no importa cuál sea la solución siempre que los marcadores se carguen instantáneamente y no rompa ningún término & condiciones.

    
pregunta Rob 27.09.2011 - 17:28

11 respuestas

57

Como todos los demás, podría darte una respuesta con código, pero no creo que alguien te haya explicado que estás haciendo algo que es fundamentalmente incorrecto .

¿Por qué estás golpeando este error? ¡Porque estás llamando a geocodificación cada vez que alguien ve tu página y no estás guardando tus resultados en la base de datos!

La razón por la que existe ese límite es para evitar el abuso de los recursos de Google (ya sea voluntariamente o no), que es exactamente lo que está haciendo :)

Aunque el código geográfico de Google es rápido, si todo el mundo lo usara de esta manera, se reducirían los servidores. La razón por la que existen Google Fusion Tables es hacer un montón de tareas pesadas en el lado del servidor. La geocodificación y el almacenamiento en caché de mosaicos se realiza en sus servidores. Si no quiere usar eso, entonces debe guardarlos en su servidor.

Si aún así, 2500 solicitudes por día es muy poco, entonces debe consultar la licencia Premier de Google Maps (pagada) que le proporciona 100,000 solicitudes de geocodificación por día para aproximadamente 10k al año (es mucho, con servidor). el almacenamiento en caché no debería alcanzar este límite a menos que esté en un sitio enorme o esté realizando un procesamiento de datos pesado). Sin el almacenamiento en caché del lado del servidor y el uso de su enfoque actual, ¡solo podría hacer 800 visitas de página por día!

Una vez que se dé cuenta de que otros proveedores cobran por geocodificación , comprenderá que debe almacenar los resultados en la base de datos. Con este enfoque, le costaría aproximadamente 10 centavos de dólar por vista de página.

Su pregunta es, ¿puede evitar el límite del acelerador que Google le ofrece? Por supuesto. Simplemente haga una solicitud desde diferentes direcciones ip. Diablos, podrías hacer un proxy de las llamadas a través de amazon elastic ips y siempre tendrías 2500 nuevas llamadas asignadas. Pero, por supuesto, además de ser ilegal (está eludiendo efectivamente la restricción que le otorgan los términos de servicio de Google Maps), estaría haciendo un truco para cubrir la falla de diseño inherente que tiene en su sistema.

Entonces, ¿cuál es el camino correcto para ese caso de uso? Antes de llamar al google geocode api, envíelo a su servidor y pregunte si está en su caché. Si no lo está, llame al código geográfico, guárdelo en su caché y devuelva el resultado.

Hay otros enfoques, pero esto te ayudará a comenzar en la dirección correcta.

Actualización: De sus comentarios a continuación, se dice que está usando PHP, por lo que aquí hay un ejemplo de código sobre cómo hacerlo correctamente (recomendación del propio equipo de Google) enlace

    
respondido por el Ragi Yaser Burhum 05.10.2011 - 17:14
14

Creo que Sasa tiene razón, en ambos aspectos.

En términos de enviar todas sus direcciones a la vez, una opción es enviar las solicitudes a intervalos. En el pasado, al usar JavaScript, opté por retrasar las solicitudes en 0.25 (¡parece que funciona!) Segundos usando el

setTimeout( [FUNCTION CALL] , 250 )

método.

En .NET he optado por:

System.Threading.Thread.Sleep(250);

Parece funcionar.

EDITAR: Realmente no puedo probarlo, ¡pero esto debería / podría funcionar!

ejemplo de Javascript. AddressArray contiene cadenas que son direcciones ...

for (var i = 0; i < addressArray.length; i++0 
{

setTimeout('googleGeocodingFunction(' + addressArray[i] + ')' , 250);

}

EDITAR:

for (var i = 0; i < address.length; i++) {
    function(i) {
        setTimeout(geocoder.geocode({ 'address': address[i] }, function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                places[i] = results[0].geometry.location;

                var marker = new google.maps.Marker({ position: places[i], map: map });
                markers.push(marker);
                mc.addMarker(marker);
                google.maps.event.addListener(marker, 'click', function() {
                    if (!infowindow) {
                        infowindow = new google.maps.InfoWindow();
                    }

                    // Setting the content of the InfoWindow
                    infowindow.setContent(popup_content[i]);

                    // Tying the InfoWindow to the marker 
                    infowindow.open(map, marker);
                });

                // Extending the bounds object with all coordinates 
                bounds.extend(places[i]);

                // Adjusting the map to new bounding box 
                map.fitBounds(bounds)
            } else {
                alert("Geocode was not successful for the following reason: " + status);
            }
        }), 250);// End of setTimeOut Function - 250 being a quarter of a second. 
    } 
}
    
respondido por el CatchingMonkey 27.09.2011 - 18:01
9

Parece que está alcanzando el límite de solicitud simultánea impuesto por Google (aunque no puedo encontrar una referencia a lo que realmente es el límite). Deberá espaciar sus solicitudes para que no envíe 125 solicitudes a la vez. Tenga en cuenta que también hay un límite de 2500 solicitudes de geocodificación por día.

Consulte el documento Estrategias de geocodificación de Google para obtener más información.

Actualización: Como una solución adicional inspirada gracias a una publicación de Mapperz, podría pensar en crear un nuevo Google Fusion Table , que almacena su dirección y cualquier información relacionada en la tabla, y geocodifica la tabla a través de su interfaz web. Hay un límite en el número de solicitudes de codificación geográfica que realizará una tabla de Fusion, sin embargo, el límite es bastante grande. Una vez que alcance el límite, deberá volver a enviar la solicitud de geocodificación, y Fusion Tables continuará donde lo dejó. La ventaja de este enfoque es una gran mejora en la velocidad, ya que solo necesitará geocodificar UNA VEZ, donde con su solución actual necesitará geocodificar cada carga, alcanzando rápidamente los límites diarios.

    
respondido por el Sasa Ivetic 27.09.2011 - 17:35
7

Aquí es cómo acelero mis solicitudes de geocodificación en javascript, siendo las direcciones una matriz que contiene cada dirección a geocodificar:

for (i=0;i<=addresses.length;i++) {
    setTimeout( function () {
            geocoder.geocode( { 'address': addresses[i]}, function(results, status) {

                if (status == google.maps.GeocoderStatus.OK) { 

                    //create and add marker to map based off geocode result
                    var marker = new google.maps.Marker({  
                        map: map,
                        title: results[0].formatted_address,
                        position: results[0].geometry.location
                    });

                 } //EDIT, was missing the closing bracket here
            });

    }, i * 1500);
}

Esto esencialmente agrega un segundo y medio entre cada solicitud de geocodificación.

    
respondido por el Casey 27.09.2011 - 18:16
5

He probado la página ahora y parece que está funcionando ahora mismo .

Si bien creo que el enfoque del temporizador que está utilizando actualmente es correcto y que yo sepa, el único que funcionará sin necesidad de algún soporte del lado del servidor debería sugerir los siguientes cambios (me tomé la libertad de reescribir su funciones de geocodificación) para aprovechar localStorage .

LocalStorage es una tecnología relativamente nueva incorporada en la mayoría de los navegadores que permite almacenar la página web Datos del cliente. Es algo similar a las cookies, pero es mucho más potente y no siempre se reenvía al servidor (a diferencia de las cookies).

Mi objetivo es guardar la dirección geocodificada en el cliente para que, si un usuario actualiza la página, no es necesario volver a invocar las funciones de geocodificador de Google. La implementación a continuación es básica (deberíamos agregar código para verificar los registros obsoletos y probablemente necesitemos una mejor clave de almacenamiento en caché que el índice), pero debería ser suficiente para comenzar. Espero que ayude.

(function() { 

window.onload = function() { 
 var mc;
// Creating an object literal containing the properties we want to pass to the map 
var options = { 
zoom: 0, maxZoom: 0, 
center: new google.maps.LatLng(52.40, -3.61), 
mapTypeId: google.maps.MapTypeId.ROADMAP 
}; 

// Creating the map 
var map = new google.maps.Map(document.getElementById('map'), options); 

// Creating a LatLngBounds object 
var bounds = new google.maps.LatLngBounds(); 

// Creating an array that will contain the addresses 
var places = []; 

// Creating a variable that will hold the InfoWindow object 
var infowindow; 
mc = new MarkerClusterer(map);
var popup_content = [..redacted..];

var geocoder = new google.maps.Geocoder(); 

var markers = [];

// Adding a LatLng object for each city 
function geocodeAddress(i) {
     // check if localStorage is available (it is now available on most modern browsers)
     // http://html5tutorial.net/tutorials/working-with-html5-localstorage.html
     // NB: this *must* be improved to handle quota limits, age/freshness, etc
     if(localStorage && localStorage['address_'+i]) {
        places[i]=JSON.parse(localStorage['address_'+i]);
        addPlace(i);
     } else {
      geocoder.geocode( {'address': address[i]}, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            places[i] = results[0].geometry.location;
            if(localStorage) {
               // cache result locally on the browser, this will help reducing the number of requests
               // to the google geocoder in case the user refreshes the page
               // remember: the more he will refresh, the more he's likely to hit the limit
               // (this is one case where refreshing or closing the browser does not work)
               localStorage['address_'+i]=JSON.stringify(results[0].geometry.location);
            }

            addPlace(i);
        } else { 
            console.log("Geocoding of address "+address[i]+" failed");
        }
    })
}

function addPlace(i) {
    // Adding the markers 
    var marker = new google.maps.Marker({position: places[i], map: map});
    markers.push(marker);
    mc.addMarker(marker);

    // Creating the event listener. It now has access to the values of i and marker as they were during its creation
    google.maps.event.addListener(marker, 'click', function() {
        // Check to see if we already have an InfoWindow
        if (!infowindow) {
            infowindow = new google.maps.InfoWindow();
        }

        // Setting the content of the InfoWindow
        infowindow.setContent(popup_content[i]);

        // Tying the InfoWindow to the marker 
        infowindow.open(map, marker);
    });

    // Extending the bounds object with each LatLng 
    bounds.extend(places[i]); 

    // Adjusting the map to new bounding box 
    map.fitBounds(bounds);
}

function geocode() {
    if (geoIndex < address.length) {
        geocodeAddress(geoIndex);
        ++geoIndex;
    }
    else {
        clearInterval(geoTimer);
    }
}
var geoIndex = 0;
var geoTimer = setInterval(geocode, 600);  // 200 milliseconds (to try out)

var markerCluster = new MarkerClusterer(map, markers); 
} 
})
(); 
    
respondido por el unicoletti 05.10.2011 - 15:44
3

utilizando geocodificadores geopy Soy capaz de geocodificar una gran cantidad de direcciones sin problemas (i No estoy seguro de la configuración de los carteles, o si él puede incluir una dependencia de python en su proyecto)

aquí está mi prueba:

from geopy import geocoders

g = geocoders.GoogleV3()  #https://geopy.readthedocs.io/en/latest/#geopy.geocoders.GoogleV3

for x in xrange(1,10000):
    print x
    a = g.geocode("AV JOAO NAVES DE AVILA " + str(x) + " UBERLANDIA MG BRASIL")
    print a
  

fragmento de resultado

     

7595 (u'Av. Jo \ xe3o Naves de \ xc1vila, 7595 - Segismundo Pereira,   Uberl \ xe2ndia - Minas Gerais, 38408-288, Brasil ', (-18.9388154,   -48.2220562))

     

7596 (u'Av. Jo \ xe3o Naves de \ xc1vila, 7596 - Segismundo Pereira,   Uberl \ xe2ndia - Minas Gerais, 38408-680, Brasil ', (-18.938814,   -48.2217423))

     

7597 (u'Av. Jo \ xe3o Naves de \ xc1vila, 7597 - Segismundo Pereira,   Uberl \ xe2ndia - Minas Gerais, 38408-288, Brasil ', (-18.9388128,   -48.2220381))

     

7598 (u'Av. Jo \ xe3o Naves de \ xc1vila, 7598 - Segismundo Pereira,   Uberl \ xe2ndia - Minas Gerais, 38408-680, Brasil ', (-18.9388114,   -48.2217242))

     

7599 (u'Av. Jo \ xe3o Naves de \ xc1vila, 7599 - Segismundo Pereira,   Uberl \ xe2ndia - Minas Gerais, 38408-288, Brasil ', (-18.9388102,   -48.2220201))

     

7600 (u'Av. Jo \ xe3o Naves de \ xc1vila, 7600 - Segismundo Pereira,   Uberl \ xe2ndia - Minas Gerais, 38408-680, Brasil ', (-18.9388088,   -48.2217062))

Esto todavía se está ejecutando y estoy con 7k resultados.

EDITAR: Después de un tiempo, llegué al final de la calle y Google comenzó a darme los mismos puntos, pero parece que todavía está haciendo solicitudes y me da respuestas precisas.

    
respondido por el George Silva 05.10.2011 - 16:51
1

Esta función php (no sé en qué idioma estás trabajando, pero puedes descifrarlo, estoy seguro) utiliza la API de google geolocalizador. Dada una dirección, devolverá la dirección normalizada y latlong. HTH.

// GEOLOCATEBYADDRESS
// @arg (string)address
// @return (array) ((int)flag,(array)address,array(rawresponse))

function geolocatebyaddress($lookupaddress){

    $lookupaddress=trim("$lookupaddress New Zealand");
    $lookupaddress=urlencode($lookupaddress);

    //send off google api lookup request
    $apiurl="http://maps.google.com/maps/api/geocode/json";
    $apiurl.="?address=$lookupaddress";
    $apiurl.="&region=NZ";
    $apiurl.="&sensor=false";

    if (function_exists("curl_init")) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $apiurl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $json = curl_exec($ch);
        curl_close($ch);
    }
    else     $json= file_get_contents($apiurl);

    //process response
    $response= json_decode($json,true);
    $approxflag=0;
    if ($response['status']=='OK' and count($response['results'])) {

        $aa= array();
        foreach($response['results'] as $cc=>$result) {
            //set keys
            foreach(array('street_number','road','suburb','city','postcode','region','lat','long') as $t){
                $aa[$cc][$t]='';
            }
            if ($result['geometry']){
                $aa[$cc]['lat']= $result['geometry']['location']['lat'];
                $aa[$cc]['long']=$result['geometry']['location']['lng'];
         }

            if (count($result['address_components'])){
                foreach ($result['address_components'] as $acs){
                    if ($acs['types'][0]=='street_number')               $aa[$cc]['street_number']= $acs['long_name']; 
                    if ($acs['types'][0]=='route')                       $aa[$cc]['road']=      $acs['long_name']; 
                    if ($acs['types'][0]=='sublocality')                 $aa[$cc]['suburb']=   $acs['long_name']; 
                    if ($acs['types'][0]=='locality')                    $aa[$cc]['city']=    $acs['long_name']; 
                    if ($acs['types'][0]=='postal_code')                 $aa[$cc]['postcode']= $acs['long_name']; 
                    if ($acs['types'][0]=='administrative_area_level_1') $aa[$cc]['region']=   $acs['long_name']; 
                }    
            }
            //successful?
            if ($result['geometry']['location_type']=='APPROXIMATE') $approxflag++;
            if (isset($result['partial_match']))  $approxflag++;
            if (!$aa[$cc]['street_number'])         $approxflag++;
        }
        if ($approxflag) return (array(1,$aa,$response));
        else return (array(2,$aa,$response));
    }
    else return (array(0,array(),$response));
}    
    
respondido por el Peter 04.10.2011 - 04:24
1

Acabo de ver tu consulta. Puedes intentar seguir el código. En la parte de otro lado de la verificación de estado de geocodificación, llame recursivamente a la misma función con un intervalo de tiempo para las direcciones perdidas.

function spotPosition(address) {
        geocoder.geocode({ 'address': address }, function (results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                var lat = results[0].geometry.location.lat();
                var lang = results[0].geometry.location.lng();
                setMarker(lat, lang);
            }
            else if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
                setTimeout(function () {
                    //Recursively calling spotPosition method for lost addresses
                    spotPosition(address);
                }, 1000);
            }
        });
    }
    
respondido por el jagath 05.04.2015 - 12:36
0

Aquí está mi solución:

dependencias: Gmaps.js, jQuery

var Maps = function($) {
   var lost_addresses = [],
       geocode_count  = 0;

   var addMarker = function() { console.log('Marker Added!') };

   return {
     getGecodeFor: function(addresses) {
        var latlng;
        lost_addresses = [];
        for(i=0;i<addresses.length;i++) {
          GMaps.geocode({
            address: addresses[i],
            callback: function(response, status) {
              if(status == google.maps.GeocoderStatus.OK) {
                addMarker();
              } else if(status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
                lost_addresses.push(addresses[i]);
              }

               geocode_count++;
               // notify listeners when the geocode is done
               if(geocode_count == addresses.length) {
                 $.event.trigger({ type: 'done:geocoder' });
               }
            }
          });
        }
     },
     processLostAddresses: function() {
       if(lost_addresses.length > 0) {
         this.getGeocodeFor(lost_addresses);
       }
     }
   };
}(jQuery);

Maps.getGeocodeFor(address);

// listen to done:geocode event and process the lost addresses after 1.5s
$(document).on('done:geocode', function() {
  setTimeout(function() {
    Maps.processLostAddresses();
  }, 1500);
});
    
respondido por el Julio Betta 11.03.2014 - 20:53
0

No puede evitar el límite de geocodificación de google map: debe obtener y almacenar LatLng informaciones.

Si tiene varias ciudades y no desea obtener LatLng manualmente, puede usar un tiempo de espera para llamar progresivamente a geocoder.geocode y guardar información dentro de un objeto javascript y luego usar console.log(JSON.stringify(object, null, 4)); para obtener en la consola un bonito Objeto impreso, entonces, por ejemplo

            var locationsPosition = {
                    "Johannesburg - ZA": {
                        "lat": -26.2041028,
                        "lng": 28.047305100000017
                    },
                    "Chennai - IN": {
                        "lat": 13.0826802,
                        "lng": 80.27071840000008
                    }
             }

así que ahora puedes agregar un marcador usando locationsPosition["Johannesburg - ZA"].lat y .lng

    
respondido por el Andrea Bisello 23.10.2015 - 08:53
-1

Parece que necesitas hacer una de dos cosas:

  1. cambie su proceso para almacenar en caché los resultados si no obtiene tantos que son únicos por día; o
  2. actualice a un servicio que le permita procesar más por día

Como la gente ha mencionado muchas veces, pero no tengo experiencia personal, parece que una licencia de Google para 100k por día es aproximadamente $ 10k USD por año.

Si desea el mismo nivel de precisión o mejor (y no tiene que pagar ese precio), puede probar nuestro servicio. Por mucho menos que Google (casi una décima parte del costo), podría obtener un límite mucho mayor que 2500 (nuestro límite más bajo es 25k / día).

Por supuesto, usar nuestra API de geocodificación pagada no es la solución que recomendaría para ser 100% honesto, aunque es un mal sentido comercial Admítelo así. En su lugar, sugeriría ir con la opción 1, optimizar su servicio al caché. A menos que necesite más de 2500 geocodificaciones ÚNICAS por día.

Descargo de responsabilidad : trabajo para Geocode Farm (el servicio que estoy recomendando).

    
respondido por el Geocode.Farm Staff 26.06.2016 - 00:49

Lea otras preguntas en las etiquetas