Неточный ответ Google Maps Elevation Service при разбиении слишком большого пути
Это немного вопрос с некоторым уровнем детализации, поэтому позвольте мне сначала объяснить ситуацию, затем мою реализацию и, наконец, вопрос, чтобы вы лучше поняли.
По состоянию на 4 апреля добавлено обновление, и проблемы сужены до одной нерешенной проблемы, см. Нижнюю часть этого вопроса для получения актуальной информации.
TLDR;
Я получил длинный маршрут, возвращенный из API Google Maps Directions, и хочу получить карту высот для этого маршрута. Жаль, что он не работает, потому что он запрашивается через GET, а максимальная длина URL составляет 2,048 символов, которые превышаются. Я разделил запросы; гарантирует правильную обработку заказа с помощью Promises; но данные Evelation не всегда полны для полного маршрута, не всегда отображаются в правильном порядке, не всегда следуют заданному пути, а местоположение между возвышениями иногда превышает несколько километров.
Вступление;
Пытаясь создать диаграмму высот для ответа Google Maps DirectionsService, я столкнулся с проблемой слишком длинных маршрутов (похоже, это не связано с расстоянием, а не с количеством LatLngs на обзорный путь). Это вызвано тем, что ElevationService запрашивается через GET
и максимальная длина URL составляет 2048 символов. Эта проблема также описана здесь.
Реализация;
Я подумал, что был бы умнее Google (не совсем, но, по крайней мере, пытаясь найти способ обойти это), чтобы разделить путь, возвращаемый DirectionsService (overview_path
собственности) на партии и объединить результаты (elevations
возвращается методом ElevationService getElevationsAlongPath
).
- Чтобы получить наилучший уровень детализации, я запрашиваю сервис ElevationService с 512 выборками на пакет;
- и потому что ElevationService распределяет выборки по длине пути, я установил максимальное количество
LatLng
и проверьте, сколько пакетов требуется для обработки полного пути (totalBatches = overview_path.length / maxBatchSize
); - и, наконец, получить равномерный разброс по моим маршрутам, в результате попытаться получить равный уровень детализации для всего маршрута (
batchSize = Math.ceil(overview_path.length / totalBatches)
).
В то время как ElevationService работает асинхронно, я проверяю, что все запросы обрабатываются в правильном порядке с помощью других пользователей SO, которые сначала использовали setTimout, а теперь работают с Promises.
Мой код
var maxBatchSize = 200;
var currentBatch = 0;
var promise = Promise.resolve();
var totalElevationBatches = Math.ceil(directions.routes[0].overview_path.length / maxBatchSize);
var batchSize = Math.ceil(directions.routes[0].overview_path.length / totalElevationBatches);
while(currentBatch < totalElevationBatches) {
promise = addToChain(promise, currentBatch, batchSize);
currentBatch++;
}
promise.then(function() {
drawRouteElevationChart(); // this uses the routeElevations to draw an AreaChart
});
function getRouteElevationChartDataBatchPromise(batch, batchSize) {
return new Promise(function(resolve, reject) {
var elevator = new google.maps.ElevationService();
var thisBatchPath = [];
for (var j = batch * batchSize; j < batch * batchSize + batchSize; j++) {
if (j < directions.routes[0].overview_path.length) {
thisBatchPath.push(directions.routes[0].overview_path[j]);
} else {
break;
}
}
elevator.getElevationAlongPath({
path: thisBatchPath,
samples: 512
}, function (elevations, status) {
if (status != google.maps.ElevationStatus.OK) {
if(status == google.maps.ElevationStatus.OVER_QUERY_LIMIT) {
console.log('Over query limit, retrying in 250ms');
resolve(setTimeout(function() {
getRouteElevationChartDataBatchPromise(batch, batchSize);
}, 250));
} else {
reject(status);
}
} else {
routeElevations = routeElevations.concat(elevations);
resolve();
}
});
});
}
function addToChain(chain, batch, batchSize){
return chain.then(function(){
console.log('Promise add to chain for batch: ' + batch);
return getRouteElevationChartDataBatchPromise(batch, batchSize);
});
}
Примечание;
Я также пакетирую запрос DirectionService для устранения ограничения 8 путевых точек, которое есть у службы, но я могу подтвердить, что это не проблема, поскольку я также сталкиваюсь с проблемой с 8 или меньшим количеством путевых точек.
Проблема;
Проблемы, с которыми я сталкиваюсь:
- Данные высотных отметок не всегда соответствуют полному пути маршрута, а это означает, что последняя точка высот на карте (далеко) от конца маршрута;
- Данные о высоте иногда отображаются в случайном порядке, как будто кажется, что обещания все еще не ожидали выполнения следующей задачи;
- Данные по высоте не всегда следуют приведенным
LatLng
изoverview_path
предоставляется в данной партии (см. скриншот); - Данные по расстоянию между высотами много. Иногда охватывает несколько километров, запрашивая 512 образцов для равномерно подобранного размера партии с максимальным значением 200
LatLng
с за партию.
Я полагал, что пакетирование ElevationService с использованием Promises (и до синхронизации с setTimtout) решит все мои проблемы, но единственная проблема, которую я решил, - это не превышение URL-адреса запроса на 2.048 символов и столкновение с вышеописанными новыми проблемами.
Помощь очень ценится
Также я хотел бы поставить 250 респ. щедрость по этому вопросу прямо сейчас, но это невозможно в данный момент. Поэтому, пожалуйста, не стесняйтесь отвечать, так как позже я смогу добавить награду и наградить ее ответом, который решает описанные проблемы. 250 респ. Награда была вручена, чтобы показать мою признательность за то, что вы указали мне правильное направление.
Спасибо за чтение и ответ!
Обновлено 4 апреля, оставив 1 незавершенный вопрос (насколько я могу судить на данный момент)
Проблема с возвышениями в случайном порядке решена
Мне удалось решить некоторые проблемы, когда я заметил несогласованное поведение в результатах указаний. Это было вызвано очевидной причиной: асинхронные вызовы не были "обещаны", чтобы быть запланированными, поэтому в некоторых случаях порядок был правильным, в большинстве случаев это было не так. Сначала я этого не заметил, потому что маркеры отображались правильно (кэшировались).
Проблема с расстоянием между высотами решена
Div, отображающий данные высот, имел ширину всего 300 пикселей и содержал много точек данных. Из-за такой малой ширины я просто не мог парить над достаточным количеством точек, что приводило к появлению точек возвышения, которые расположены дальше друг от друга.
Проблема с данными о высоте, не отображаемыми по маршруту
Каким-то образом где-то вниз по линии я также решил эту проблему, но я не уверен, что большая ширина или "Перспективный" порядок направлений решил эту проблему.
Нерешенная проблема: данные высот не всегда полны
Единственная оставшаяся проблема заключается в том, что данные о высоте не всегда покрывают полный путь. Я полагаю, что это связано с ошибкой в логике Promising, поскольку регистрация некоторых сообщений в консоли говорит мне, что диаграмма высот рисуется в точке, где не все Promise-then завершены, и я думаю, что это вызвано повторным вызовом пакетного вызова, когда Over Ошибка лимита запросов возвращается API Карт Google.
Как я могу перефразировать ту же цепочку, когда возвращается ошибка Over Query Limit? Я пытался не разрешать ту же функцию снова, а просто запустить setTimeout(...)
, но в этом случае Promise, похоже, не разрешает обновленный пакет в тот момент, когда он больше не получает лимит Over Query Limit. В настоящее время это то, как я настроил это (для обоих направлений и высоты):
function getRouteElevationChartDataBatchPromise(batch, batchSize) {
return new Promise(function(resolve, reject) {
var elevator = new google.maps.ElevationService();
var thisBatchPath = [];
for (var j = batch * batchSize; j < batch * batchSize + batchSize; j++) {
if (j < directions.routes[0].overview_path.length) {
thisBatchPath.push(directions.routes[0].overview_path[j]);
} else {
break;
}
}
elevator.getElevationAlongPath({
path: thisBatchPath,
samples: 512
}, function (elevations, status) {
if (status != google.maps.ElevationStatus.OK) {
if(status == google.maps.ElevationStatus.OVER_QUERY_LIMIT) {
console.log('ElevationService: Over Query Limit, retrying in 200ms');
resolve(setTimeout(function() {
getRouteElevationChartDataBatchPromise(batch, batchSize);
}, 200));
} else {
reject(status);
}
} else {
console.log('Elevations Count: ' + elevations.length);
routeElevations = routeElevations.concat(elevations);
resolve();
}
});
});
}
3 ответа
Последняя оставшаяся проблема также была решена с помощью этого SO вопроса: Как повторно запустить обещание JavaScript, если не удалось?, Так что, если jfriend00 на этот вопрос, я могу присудить ему награду, так как в конечном итоге это помогло мне.
Чтобы убедиться, что функция разрешается в статусе OK
повторяется в OVER_QUERY_LIMIT
и отклонить в any other status
Мне пришлось поместить логику Promise в функцию и вызвать эту функцию, вот так:
function getRouteElevationChartDataBatchPromise(batch, batchSize) {
return new Promise(function(resolve, reject) {
function run(batch, batchSize) {
var elevator = new google.maps.ElevationService();
var thisBatchPath = [];
for (var j = batch * batchSize; j < batch * batchSize + batchSize; j++) {
if (j < directions.routes[0].overview_path.length) {
thisBatchPath.push(directions.routes[0].overview_path[j]);
} else {
break;
}
}
elevator.getElevationAlongPath({
path: thisBatchPath,
samples: 512
}, function (elevations, status) {
if(status == google.maps.ElevationStatus.OK) {
routeElevations = routeElevations.concat(elevations);
resolve();
} else if (status == google.maps.ElevationStatus.OVER_QUERY_LIMIT) {
setTimeout(function () {
run(batch, batchSize);
}, 200);
} else {
reject(status);
}
});
}
run(batch, batchSize);
});
}
Мммм, сколько очков вы должны справиться. Можете ли вы опубликовать путь, так что, может быть, другие смогут проверить его в своих приложениях. Вы пытались уменьшить точки пути с помощью Дугласа-Пейкера или аналогичных методов. Вы пробовали другие приложения, такие как бесплатный "Routeconverter" (который работает с HGT), чтобы увидеть, если вы получите лучшие результаты. Вам нужны точки возвышения прямо / на лету? Будет ли использование других бесплатных услуг возвышения вариант. Возможно, вам придется считывать точки возвышения обратно к точкам маршрута, чтобы вы могли отсортировать нежелательные точки.
Только немного подумав, на плохом английском - боюсь. Удачи Рейнхард
Проблемы, затронутые в части реализации, дополнительных замечаниях и перечисленных проблемах, рассматриваются в Google Maps Elevation API. Полная документация предоставляет простой интерфейс для запроса местоположений на Земле для получения данных о высоте и будет решать все возникшие проблемы, такие как запросы на повышение, использование параметров, указание местоположений, путей и ответов на повышение.
Что касается вопросов, обсуждаемых в вашем введении, Google Maps Elevation API имеет стандартные и премиальные ограничения использования. Эти ограничения применяются для предотвращения злоупотребления Google Maps Elevation API. Ограничения на использование Google Maps Elevation API предоставляют подробную информацию об ограничениях на использование и о возможности увеличения квоты.
Дополнительные примечания из документации, которые могут решить ваши проблемы:
- Обратите внимание, что данные высот становятся более грубыми при прохождении нескольких точек. Чтобы получить наиболее точное значение высоты для точки, она должна запрашиваться независимо.
- В тех случаях, когда Google не располагает точными измерениями высоты в том месте, которое вы запрашиваете, служба интерполирует и возвращает усредненное значение, используя четыре ближайших местоположения.
- Как и для позиционных запросов, параметр path указывает набор значений широты и долготы. В отличие от позиционного запроса, путь указывает упорядоченный набор вершин. Вместо того, чтобы возвращать данные высот в вершинах, запросы пути выбираются по длине пути, где каждая выборка равноудалена друг от друга.
- API Google Maps Elevation возвращает данные для отдельных запросов с максимально возможной точностью. Пакетные запросы, включающие несколько местоположений, могут возвращать данные с меньшей точностью.