平滑 GPS 跟踪的路线坐标

Posted

技术标签:

【中文标题】平滑 GPS 跟踪的路线坐标【英文标题】:Smoothing GPS tracked route-coordinates 【发布时间】:2013-04-20 14:09:04 【问题描述】:

我有一些我记录的坐标数据。不幸的是,他们似乎不是很好。他们有时会跳过地图。所以现在我正在寻找一些可以使路线看起来更真实的扁平化或过滤算法。

目前我唯一的过滤器是计算一秒钟内可能行驶的最大米数(在公共汽车、汽车或步行中),并将它们与坐标进行比较,然后将它们扔掉,这在时间范围内是不可能的。因此,如果一个人一秒钟能走 2.5 米,而我有两个相距 10 米的坐标,并且它们在两秒钟内被记录下来,我会尝试找到它们并将它们扔掉。这有点帮助。

这是代码:

filters.max_possible_travel = function(data) 
    //http://en.wikipedia.org/wiki/Preferred_walking_speed
    //I switched to 16, as the route was made by driving with a bus...
    var maxMetersPerSec = 16,
        i, m, last, result = [];

    for(i=0;i<data.length;i++) 
        m = data[i];
        if (last) 
            // seconds between current and last coord
            var diff = (m.created.getTime() - last.created.getTime()) / 1000;
            // the maximum amount of meters a person,bus,car etc can make per sec.
            var maxDistance = diff * maxMetersPerSec;
            // the actual distance traveled
            var traveledDistance = google.maps.geometry.spherical.computeDistanceBetween(last.googLatLng, m.googLatLng);

            if (traveledDistance > maxDistance) 
                continue;
             else 
                result.push(m);
            
        
        last = m;
    
    return result;
;

为了让您更轻松,我创建了这个小提琴,它已经实现了我的第一个过滤器,并且还让您能够添加新过滤器。

http://jsfiddle.net/z4hB7/7/

我还有一些其他的想法:

扔掉特定半径内的所有坐标。如果您只站几分钟,这最终会消除一些令人不安的坐标 将所有坐标按 n 秒帧分组,并尝试确定此块中最相关的坐标。不幸的是,我不知道如何:(

所以我认为这是一个非常有趣的问题,我希望你能理解我所说的一切。我感谢你们的任何帮助!

编辑:我发现了一些关于线性最小二乘法和卡尔曼滤波器的信息。我很喜欢它,但因为我绝对不是数学专家,我会很感激这方面的任何帮助。

编辑 2 进展 :) 我实现了 @geocodezip 向我推广的 DouglasPeucker 算法。单独的算法并不能解决所有问题,但是结合我当前的“max_possible_travel”它看起来几乎完美。如果我稍微玩一下第二个参数,它会变得很有趣。请查看新的小提琴并确保检查过滤器“walkfilter”和“gdouglaspeucker”。 http://jsfiddle.net/z4hB7/8/

【问题讨论】:

【参考方案1】:

你可以试试Douglas Peuker algorithm

Ramer-Douglas-Peucker 算法是一种用于减少由一系列点近似的曲线中的点数的算法。

至少有one implementation for the Google Maps API v3

perl implementation

来自Bill Chadwick's site的javascript实现代码:

/* Stack-based Douglas Peucker line simplification routine 
   returned is a reduced google.maps.LatLng array 
   After code by  Dr. Gary J. Robinson,
   Environmental Systems Science Centre,
   University of Reading, Reading, UK
*/

function GDouglasPeucker (source, kink)
/* source[] Input coordinates in google.maps.LatLngs    */
/* kink in metres, kinks above this depth kept  */
/* kink depth is the height of the triangle abc where a-b and b-c are two consecutive line segments */

    var n_source, n_stack, n_dest, start, end, i, sig;    
    var dev_sqr, max_dev_sqr, band_sqr;
    var x12, y12, d12, x13, y13, d13, x23, y23, d23;
    var F = ((Math.PI / 180.0) * 0.5 );
    var index = new Array(); /* aray of indexes of source points to include in the reduced line */
    var sig_start = new Array(); /* indices of start & end of working section */
    var sig_end = new Array();  

    /* check for simple cases */

    if ( source.length < 3 ) 
        return(source);    /* one or two points */

    /* more complex case. initialize stack */

    n_source = source.length;
    band_sqr = kink * 360.0 / (2.0 * Math.PI * 6378137.0);  /* Now in degrees */
    band_sqr *= band_sqr;
    n_dest = 0;
    sig_start[0] = 0;
    sig_end[0] = n_source-1;
    n_stack = 1;

    /* while the stack is not empty  ... */
    while ( n_stack > 0 )

        /* ... pop the top-most entries off the stacks */

        start = sig_start[n_stack-1];
        end = sig_end[n_stack-1];
        n_stack--;

        if ( (end - start) > 1 )  /* any intermediate points ? */        

                /* ... yes, so find most deviant intermediate point to
                       either side of line joining start & end points */                                   

            x12 = (source[end].lng() - source[start].lng());
            y12 = (source[end].lat() - source[start].lat());
            if (Math.abs(x12) > 180.0) 
                x12 = 360.0 - Math.abs(x12);
            x12 *= Math.cos(F * (source[end].lat() + source[start].lat()));/* use avg lat to reduce lng */
            d12 = (x12*x12) + (y12*y12);

            for ( i = start + 1, sig = start, max_dev_sqr = -1.0; i < end; i++ )                                    

                x13 = (source[i].lng() - source[start].lng());
                y13 = (source[i].lat() - source[start].lat());
                if (Math.abs(x13) > 180.0) 
                    x13 = 360.0 - Math.abs(x13);
                x13 *= Math.cos (F * (source[i].lat() + source[start].lat()));
                d13 = (x13*x13) + (y13*y13);

                x23 = (source[i].lng() - source[end].lng());
                y23 = (source[i].lat() - source[end].lat());
                if (Math.abs(x23) > 180.0) 
                    x23 = 360.0 - Math.abs(x23);
                x23 *= Math.cos(F * (source[i].lat() + source[end].lat()));
                d23 = (x23*x23) + (y23*y23);

                if ( d13 >= ( d12 + d23 ) )
                    dev_sqr = d23;
                else if ( d23 >= ( d12 + d13 ) )
                    dev_sqr = d13;
                else
                    dev_sqr = (x13 * y12 - y13 * x12) * (x13 * y12 - y13 * x12) / d12;// solve triangle

                if ( dev_sqr > max_dev_sqr  )
                    sig = i;
                    max_dev_sqr = dev_sqr;
                
            

            if ( max_dev_sqr < band_sqr )   /* is there a sig. intermediate point ? */
                /* ... no, so transfer current start point */
                index[n_dest] = start;
                n_dest++;
            
            else
                /* ... yes, so push two sub-sections on stack for further processing */
                n_stack++;
                sig_start[n_stack-1] = sig;
                sig_end[n_stack-1] = end;
                n_stack++;
                sig_start[n_stack-1] = start;
                sig_end[n_stack-1] = sig;
            
        
        else
                /* ... no intermediate points, so transfer current start point */
                index[n_dest] = start;
                n_dest++;
        
    

    /* transfer last point */
    index[n_dest] = n_source-1;
    n_dest++;

    /* make return array */
    var r = new Array();
    for(var i=0; i < n_dest; i++)
        r.push(source[index[i]]);
    return r;


【讨论】:

以上是关于平滑 GPS 跟踪的路线坐标的主要内容,如果未能解决你的问题,请参考以下文章

Libgdx/Java:将 GPS 转换为像素坐标后,路线旋转 90°

跟踪公交GPS点是否在路上 - Google Maps API

在 android 模拟器中模拟 GPS 路线

在地图上绘制路线

Android java从sqlite计算距离许多坐标[重复]

为啥 GPS 会报告疯狂的经度数字?