【问题标题】:Inaccurate Google Maps Elevation Service response when splitting a too large path分割过大路径时 Google Maps Elevation Service 响应不准确
【发布时间】:2016-04-05 19:18:30
【问题描述】:

这是一个有点详细的问题,所以让我先解释一下情况,然后是我的实现,最后是这个问题,这样你就明白了。

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

TLDR;

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

简介;

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

实施;

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

  • 为了获得最佳详细信息,我使用 512 查询 ElevationService 每批样本;
  • 并且因为 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 个或更少航点的问题。

问题;

我面临的问题是:

  • 高程数据并不总是遵循路线的完整路径,这意味着图表中的最后一个高程点距离路线终点(远);
  • 高程数据有时会以随机顺序显示,就好像 Promise 似乎还没有等待下一个任务执行一样;
  • 海拔数据并不总是遵循给定的LatLng overview_path 在给定批次中提供(见屏幕截图);
  • 高程距离数据很多。有时跨越多个公里,同时请求 512 个样本以获得均匀匹配的批次大小,每批次最多 200 个LatLngs。

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

非常感谢您的帮助

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

感谢阅读和回复!

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

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

当我注意到方向结果中的不一致行为时,我已经能够解决一些问题。这是由一个明显的原因引起的:异步调用不是“承诺”安排的,所以有时顺序是正确的,大多数时候不是。起初我没有注意到这一点,因为标记显示正确(缓存)。

解决高程距离问题

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

沿途海拔数据不显示的问题

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

待解决的问题:海拔数据并不总是完整的

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

当返回 Over Query Limit 错误时,如何重新触发同一个链?我试图不再解决相同的功能,而只是触发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();
            }
        });
    });
}

【问题讨论】:

    标签: javascript google-maps-api-3 batch-processing map-directions google-elevation-api


    【解决方案1】:

    嗯,你要处理多少分。你能发布路径,所以也许其他人可以在他们自己的应用程序中测试它。 您是否尝试使用 Douglas-Peuker 或类似方法减少路径点。 您是否尝试过其他应用程序,例如免费的“Routeconverter”(与 HGT 一起使用),看看您是否获得了更好的结果。您是否需要直接/动态的高程点?可以选择使用其他免费的海拔服务。也许您必须将海拔点读回您的路线点,以便您可以整理出不需要的点。

    只有一些想法,英语很糟糕 - 恐怕。 祝你好运,莱因哈德

    【讨论】:

    • 感谢您的回复,但我已经将问题缩小到可能的原因。但我喜欢提到 Ramer Douglas Peucker,我以前没听说过;)
    【解决方案2】:

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

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

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

    1. 请注意,当通过多个点时,高程数据会变得更加粗糙。要获得一个点最准确的高程值,应该单独查询它。
    2. 如果 Google 无法在您请求的精确位置获得精确的海拔测量值,该服务将使用四个最近的位置进行插值并返回平均值。
    3. 与位置请求一样,路径参数指定一组纬度和经度值。然而,与位置请求不同的是,路径指定了一组有序的顶点。路径请求不是返回顶点处的高程数据,而是沿路径长度进行采样,其中每个样本彼此等距。
    4. Google Maps Elevation API 以尽可能高的准确度返回单点查询数据。涉及多个位置的批量查询可能会返回精度较低的数据。

    【讨论】:

    • 感谢您的回复。但我不(完全)同意你的看法。在 Google Maps API 的范围内,它沿路线提供了出色的高程数据。所以;当我批量处理这些请求以在给定边界内执行调用时,预计会有提升服务响应。我只连接结果以在图表中显示多个响应。在我看来这应该是可以实现的。
    【解决方案3】:

    在这个 SO 问题的帮助下,最后一个问题也得到了解决:How to re-run a javascript promise when failed?。所以如果jfriend00 回复了这个问题,我可以将赏金奖励给他,因为这是最终帮助我的诀窍。

    为了确保函数 resolves 在状态 OKretriesOVER_QUERY_LIMITrejectany 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);
        });
    }
    

    【讨论】:

      猜你喜欢
      • 2021-06-02
      • 2012-10-12
      • 1970-01-01
      • 2016-07-22
      • 2015-12-21
      • 2011-09-11
      • 1970-01-01
      • 2016-05-14
      • 1970-01-01
      相关资源
      最近更新 更多