Неточный ответ 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 предоставляют подробную информацию об ограничениях на использование и о возможности увеличения квоты.

Дополнительные примечания из документации, которые могут решить ваши проблемы:

  1. Обратите внимание, что данные высот становятся более грубыми при прохождении нескольких точек. Чтобы получить наиболее точное значение высоты для точки, она должна запрашиваться независимо.
  2. В тех случаях, когда Google не располагает точными измерениями высоты в том месте, которое вы запрашиваете, служба интерполирует и возвращает усредненное значение, используя четыре ближайших местоположения.
  3. Как и для позиционных запросов, параметр path указывает набор значений широты и долготы. В отличие от позиционного запроса, путь указывает упорядоченный набор вершин. Вместо того, чтобы возвращать данные высот в вершинах, запросы пути выбираются по длине пути, где каждая выборка равноудалена друг от друга.
  4. API Google Maps Elevation возвращает данные для отдельных запросов с максимально возможной точностью. Пакетные запросы, включающие несколько местоположений, могут возвращать данные с меньшей точностью.