Как я могу Геокодировать 20 адресов без получения ответа на ограничение по запросу?
используя Google Geocoder v3, если я попытаюсь геокодировать 20 адресов, я получаю OVER_QUERY_LIMIT, если я не разнесу их на ~1 секунду, но затем потребуется 20 секунд, прежде чем все мои маркеры будут размещены.
есть ли другой способ сделать это, кроме хранения координат заранее?
6 ответов:
нет, на самом деле нет другого способа : если у вас есть много мест и вы хотите отобразить их на карте, лучшим решением является :
- получить широту + долготу, используя геокодер, когда местоположение создается
- храните их в своей базе данных, рядом с адресом
- и использовать эти сохраненные широта + долгота, когда вы хотите, чтобы отобразить карту.
Это, конечно, учитывая, что у вас намного меньше создание / изменение локаций, чем у вас есть консультации локаций.
Да, это означает, что вам придется сделать немного больше работы при сохранении местоположения - но это также означает:
- вы сможете осуществлять поиск по географическим координатам
- т. е. "Я хочу список точек, которые находятся рядом с тем, где я сейчас"
- отображение карты будет намного быстрее
- даже с более чем 20 места на нем
- О, и еще (последнее, но не менее) : это будет работать ;-)
- вы с меньшей вероятностью достигнете предела X вызовов геокодера за N секунд.
- и вы с меньшей вероятностью достигнете предела вызовов геокодера Y в день.
на самом деле вам не нужно ждать полной секунды для каждого запроса. Я обнаружил, что если я жду 200 миллисекунд между каждым запросом, я могу избежать ответа OVER_QUERY_LIMIT, и пользовательский опыт сносен. С помощью этого решения вы можете загрузить 20 элементов за 4 секунды.
$(items).each(function(i, item){ setTimeout(function(){ geoLocate("my address", function(myLatlng){ ... }); }, 200 * i); }
к сожалению, это ограничение сервиса Google maps.
в настоящее время я работаю над приложением, используя функцию геокодирования, и я сохраняю каждый уникальный адрес для каждого пользователя. Я генерирую информацию об адресе (город, улица, штат и т. д.) На основе информации, возвращенной Google maps, а затем сохраняю информацию lat/long в базе данных. Это избавляет вас от необходимости перекодировать вещи и дает вам хороший формат адреса.
другая причина вы хотите сделать это потому, что есть лимит на количество адресов, которые могут быть геокодированы с определенного IP-адреса. Вы не хотите, чтобы ваше приложение не для человека по этой причине.
Я столкнулся с той же проблемой, пытаясь геокодирования 140 адресов.
мой обходной путь был добавление usleep(100000) для каждого цикла следующего запроса геокодирования. Если статус запроса OVER_QUERY_LIMIT, usleep увеличивается на 50000 и запрос повторяется, и так далее.
и причины все полученные данные (lat/long) хранятся в XML-файле, чтобы не запускать запрос каждый раз, когда страница загружается.
EDIT:
забыл сказать, что это решение находится в чистом js, единственное, что вам нужно, это браузер, который поддерживает обещания https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Promise
для тех, кто все еще должен выполнить такое, я написал свое собственное решение, которое сочетает в себе обещания с тайм-аутами.
код:
/* 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); } }); } };
обратите внимание, что это просто часть большего библиотека, которую я написал для обработки Google maps, поэтому комментарии могут быть запутанными.
использование довольно просто, однако подход немного отличается: вместо циклического и разрешения одного адреса за раз вам нужно будет передать массив адресов в класс, и он будет обрабатывать поиск сам по себе, возвращая обещание, которое при разрешении возвращает массив, содержащий все разрешенные (и неразрешенные) адреса.
пример:
var myAmazingGeo = new Geolocalizer(); var locations = ["Italy","California","Dragons are thugs...","China","Georgia"]; myAmazingGeo.Localize(locations).then(function(res){ console.log(res); });
Я только что протестировал Google Geocoder и получил ту же проблему, что и у вас. Я заметил, что я получаю статус OVER_QUERY_LIMIT только один раз каждые 12 запросов Поэтому я жду 1 секунду (это минимальная задержка, чтобы ждать) Это замедляет приложение, но меньше, чем ожидание 1 секунды каждый запрос
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 }
С помощью основного метода холдона:
private void holdOn(long delay) { try { Thread.sleep(delay); } catch (InterruptedException ex) { // ignore } }
надеюсь, что это помогает