分割过大路径时 Google 地图高程服务响应不准确

Inaccurate Google Maps Elevation Service response when splitting a too large path

这是一个有点细节的问题,所以让我先解释一下情况,然后是我的实现,最后是问题,这样你就可以最好地理解了。

自 4 月 4 日起添加了更新,问题缩小为一个未决问题,请参阅此问题的底部以获取最新信息。

TLDR;

我有一条从 Google 地图方向 API 返回的很长的路线,并且想要该路线的海拔图。太糟糕了,它不起作用,因为它是通过 GET 请求的,并且 URL 最大长度是 2.048 个字符,超出了。我分开了请求;使用 Promises 保证正确的处理顺序;但是完整路线的高程数据并不总是完整的,并不总是以正确的顺序显示,并不总是遵循给定的路径,并且高程位置有时跨越几公里。

简介;

尝试为 Google Maps DirectionsService 响应创建海拔图我遇到了路线太长的问题(这似乎与距离无关,而不是每个 overview_path).这是由于通过 GET 请求 ElevationService 并且 URL 的最大长度为 2048 个字符。这个问题是described on SO here as well.

执行;

我想我会比 Google 更聪明(不是真的,但至少试图找到一种方法来解决它),拆分 DirectionsService 返回的路径(overview_path 属性) 分成批次并连接结果(elevations 由 ElevationService 方法 getElevationsAlongPath 返回)。

虽然 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 个或更少航路点的问题 waypoints。

问题;

我面临的问题是:

我认为使用 Promises 对 ElevationService 进行批处理(在使用 setTimtout 计时之前)可以解决我的所有问题,但我解决的唯一问题是不超过 2.048 个字符的请求 URL 并面临上述新问题。

非常感谢帮助

另外我想放一个 250 代表。马上就这个问题悬赏,但目前这是不可能的。因此,请随时回复,因为我稍后可以添加赏金并将其奖励给解决所描述问题的答案。 250 个代表。已授予赏金以表达我对您为我指明正确方向的感谢。

感谢阅读和回复!

4 月 4 日更新,留下 1 个未决问题(据我所知)

解决了随机顺序的海拔问题

当我注意到方向结果中的行为不一致时,我已经能够解决一些问题。这是由于一个明显的原因造成的:异步调用未被安排 "Promised",因此有时顺序是正确的,但大多数时候却不是。一开始我没有注意到这一点,因为标记显示正确(缓存)。

已解决海拔间距问题

显示高程数据的 div 只有 300 像素宽并且包含许多数据点。由于宽度如此之小,我根本无法将鼠标悬停在足够多的点上,从而导致触发彼此相距较远的高程点。

高程数据未沿路线显示的问题

不知何故,我也解决了这个问题,但我不确定更大的宽度或 "Promising" 方向顺序是否解决了这个问题。

未决问题:高程数据并不总是完整的

唯一剩下的问题是高程数据并不总是覆盖完整路径。我相信这是因为 Promising 逻辑中的错误,因为在控制台中记录一些消息告诉我海拔图是在并非所有 Promise-then 都已完成的点绘制的,我认为这是由于在 Over 时重新触发批处理调用造成的Google 映射 API.

返回查询限制错误

如何在返回超过查询限制错误时重新启动同一个链?我试过不再解析相同的函数,只是触发 setTimeout(...),但 Promise 似乎无法解析重新触发的批处理,因为它不再获得超过查询限制。目前我是这样设置的(方向和高度):

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

实现部分提出的顾虑、旁注和列举的问题都在Google Maps Elevation API中涵盖。完整的文档提供了一个简单的界面来查询地球上的位置以获取高程数据,并将解决您遇到的所有问题,例如高程请求、参数使用、指定位置、路径和高程响应。

对于您在介绍中讨论的问题,Google Maps Elevation API 具有标准和高级使用限制。执行这些限制是为了防止滥用 Google 地图海拔 API。 Google Maps Elevation API Usage Limits 为您提供有关使用限制和增加配额选项的详细信息。

可能解决您的问题的文档中的附加说明:

  1. Note that elevation data becomes more coarse when multiple points are passed. To obtain the most accurate elevation value for a point, it should be queried independently.
  2. In those cases where Google does not possess exact elevation measurements at the precise location you request, the service will interpolate and return an averaged value using the four nearest locations.
  3. As with positional requests, the path parameter specifies a set of latitude and longitude values. Unlike a positional request, however, the path specifies an ordered set of vertices. Rather than return elevation data at the vertices, path requests are sampled along the length of the path, where each sample is equidistant from each other.
  4. The Google Maps Elevation API returns data for single point queries of the highest accuracy possible. Batch queries involving multiple locations may return data with less accuracy.

嗯嗯,你要处理多少点。你能发布路径吗,所以也许其他人可以在他们自己的应用程序中测试它。 您是否尝试使用 Douglas-Peuker 或类似方法减少路径点。 您是否尝试过其他应用程序,例如免费的 "Routeconverter"(与 HGT 配合使用)以查看是否获得更好的结果。你需要高程点 direct/on 苍蝇吗?使用其他免费高程服务是否是一种选择。也许您必须将高程点读回到您的路线点,以便您可以整理出不需要的点。

只是一些想法,英语不好 - 恐怕。 祝你好运,莱因哈特

最后剩下的问题也已在这个 SO 问题的帮助下得到解决:How to re-run a javascript promise when failed?. So if jfriend00 回复这个问题我可以将赏金奖励给他,因为这是最终帮助我解决问题的技巧。

确保函数 在状态 OK 解析 OVER_QUERY_LIMIT 和 [=19] 重试 =]reject at 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);
    });
}