谷歌地图高程服务在拆分过大路径时响应不准确

8

这是一个相当详细的问题,让我先解释一下情况,然后介绍我的实现以及最后的问题,这样你就可以更好地理解了。

截至4月4日,更新了内容,问题已被缩小到一项未解决的问题,请查看问题底部的最新信息。

TLDR;

我从Google Maps Directions API获得了一个长路线,并希望为该路线创建一个高程图。但很遗憾,它无法正常工作,因为它是通过GET请求获取的,而URL的最大长度为2048个字符,此长度已超过。我将请求分开;使用Promises确保了正确的处理顺序;但完整路线的高程数据并不总是完整的,也不总是按正确顺序显示,也不总是沿着给定路径走,并且间隔有时跨越几公里。

简介;

尝试为Google Maps DirectionsService响应创建一个高程图,我遇到了一个太长的路线问题(这似乎与距离无关,而是与每个overview_path的LatLngs数量有关)。这是由于ElevationService是通过GET请求的,而URL的最大长度为2048个字符引起的。这个问题在这里也有描述

实现;

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

  • 为了获得最佳详细级别,我使用每个批次512个样本查询ElevationService;
  • 由于ElevationService将样本分散在路径的长度上,因此我设置了每个批次的最大LatLng数量,并检查需要处理完整路径的批次数(totalBatches=overview_path.length/maxBatchSize);
  • 最后,在我的Directions结果中获得均匀分布,以尝试获得完整路线的相等详细级别(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仍在等待下一个任务执行一样;
  • 高程数据并未始终按照给定的批次中提供的overview_path中的LatLng依次显示(请参见屏幕截图);
  • 相邻高程距离数据很多。有时要跨越数公里,而请求512个样本以匹配均匀的批处理大小,每个批处理最多具有200个LatLng

高程数据没有沿着它被告知要跟踪的overview_path进行跟踪

我发现使用Promise(在setTimtout之前计时)对ElevationService进行批处理将解决我的所有问题,但我只解决了一项问题,即不超过2.048个char的请求URL,并且面临上述新问题。

非常感谢您的帮助

此外,我想提前放置250 rep。悬赏问题,但当前不可能。因此,请随意回复,因为我以后可以添加赏金并授予解决所述问题的答案。我已经授予250 rep。赏金,显示我对您指导我正确方向的赞赏。

感谢您的阅读和回复!

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

处理高程随机排序问题

当我注意到路线结果的不一致行为时,我能够解决一些问题。这是由于异步调用未被“Promised”以安排,因此有些情况下顺序是正确的,大多数情况下则不是。一开始我没有注意到这一点,因为标记显示正确(缓存)。

解决相邻高程距离问题

显示高程数据的div宽度仅为300px,包含了许多数据点。由于这么小的宽度,我根本无法悬停在足够的点上引发距离彼此更远的高程点。

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

不知何时何处我也解决了此问题,但我不确定较大的宽度或“Promising”方向顺序是否解决了此问题。

待处理问题:高程数据不完整

唯一剩下的问题是高程数据并不总是覆盖整个路径。我认为这是 Promising 逻辑中的错误,因为在控制台记录了一些信息后,发现高程图表在未完成所有 Promise-then 的情况下绘制,我认为这是由于 Google Maps API 返回 Over Query Limit 错误时重新触发批量调用导致的。

当出现 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个回答

1

嗯,你需要处理多少个点呢。你能发布路径吗,这样其他人就可以在自己的应用程序中测试它了。 你是否尝试过使用Douglas-Peuker或类似的方法来减少路径点数? 你是否尝试过其他应用程序,比如免费的“Routeconverter”(它可以使用HGT),看看是否能获得更好的结果?你需要直接/即时获取高程点吗?使用其他免费的高程服务是否可行?也许你需要将高程点读回到你的路径点中,以便筛选出不需要的点。

只是一些想法,用糟糕的英语表达 - 我很抱歉。 祝你好运,Reinhard


谢谢您的回复,但我已经将问题缩小到可能的原因了。但是我喜欢提到Ramer Douglas Peucker算法,我以前没有听说过 ;) - Ben Fransen

0

最后一个问题也在这个SO问题的帮助下得到了解决:如何在失败时重新运行JavaScript Promise?。所以如果jfriend00回答了这个问题,我可以把赏金给他,因为那就是最终帮助我的技巧。

为了确保函数在状态OK解决,在OVER_QUERY_LIMIT重试,并在任何其他状态拒绝,我必须将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);
    });
}

0

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

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

文档中的其他注意事项可能会解决您的问题:

请注意,当传递多个点时,海拔数据会变得更加粗糙。为了获得一个点的最准确的海拔值,应该单独查询它。
在那些谷歌没有精确的海拔测量数据的情况下,服务将进行插值并返回使用四个最近位置的平均值。
与位置请求一样,路径参数指定一组纬度和经度值。然而,与位置请求不同的是,路径指定了一个有序的顶点集。路径请求不是在顶点处返回高程数据,而是沿着路径长度进行采样,每个采样点之间等距离采样。
Google Maps Elevation API 返回单个点查询的最高精度数据。涉及多个位置的批量查询可能会返回精度较低的数据。

谢谢您的回复。但我不完全同意您的观点。在Google Maps API的范围内,它提供了沿路线的优秀高程数据。因此,当我批量请求以在给定边界内进行调用时,预期会得到一个高程服务响应。我只需将结果连接起来以在图表中显示多个响应。在我看来,这应该是可以实现的。 - Ben Fransen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接