Есть ли алгоритм, подходящий для сжатия путевых точек GPS? - PullRequest
1 голос
/ 05 августа 2020

Я ищу любой алгоритм сжатия с потерями путевых точек GPS-трека с координатами в EPSG:4326 CRS (это обычные градусы, например (75.223423, -10.123123))

Для краткости после удаления метаинформации и упрощения с помощью алгоритма Рамера-Дугласа-Пекера у меня есть упорядоченная последовательность координат путевой точки, каждая путевая точка занимает 16 байтов (2 x 8-байтовых double).

Зная, что путевые точки упорядочены, а расстояние между путевыми точками в большинстве случаев меньше 0,01 ° ( ~ 1 км на экваторе ), я сделал предположение, что может быть какое-то сжатие с потерями алгоритм для таких последовательностей.

Не могли бы вы помочь мне выяснить это, пожалуйста.

UPD: По реальным трекам (~ 800 проанализировано) расстояние в градусах между точек показано ниже. P95 - 95-й процентиль всех расстояний.

LON    
    avg: 0,000334560520818109
    p95: 0,001239999999999240 # ~138 meters
    max: 0,307273900000000000
LAT    
    avg: 0,000221987685948093
    p95: 0,000839999999996621
    max: 0,309625799999999000

Ответы [ 3 ]

2 голосов
/ 05 августа 2020

Если вам нужно написать код самостоятельно, вот уловка для простой схемы «дельта» кодирования, которая позволяет избежать ненужной потери точности.

Идея состоит в том, что компрессор должен вычислять дельты НЕ между текущими данными точка и последний, но между текущей точкой данных и тем, что декомпрессор вычислит для последнего. Это означает, что ошибки квантования не складываются.

В качестве простого примера, который вы можете просмотреть / поэкспериментировать с приведенным здесь кодом c, который сжимает двойные числа в (start double) и последовательность чисел с плавающей запятой:

typedef struct
{   double  start;
    int n;
    float*  delta;
} compT;

compT   compress( int n, const double* data)
{
compT   c = (compT){ data[0], n-1, malloc( (n-1)*sizeof *c.delta)};
double  r = c.start;
    for( int i=1; i<n; ++i)
    {
    float   d = (float)(data[i]-r);
        c.delta[i-1] = d;
        r += d;
    }
    return c;
}

static  double* uncompress( compT c)
{
double* d = malloc( (c.n+1)*sizeof *d);
double  r = c.start;
    d[0] = r;
    for( int i=1; i<=c.n; ++i)
    {   r += c.delta[i-1];
        d[i] = r;
    }
    return d;
}
compT   bad_compress( int n, const double* data)
{
compT   c = (compT){ data[0], n-1, malloc( (n-1)*sizeof *c.delta)};
    for( int i=1; i<n; ++i)
    {
    float   d = (float)(data[i]-data[i-1]);
        c.delta[i-1] = d;
    }
    return c;
}

При использовании чисел с плавающей запятой нарастание ошибок квантования действительно заметно только на длинных (миллионах) последовательностях данных.

Однако, когда вы надеетесь сжать больше, эффекты более заметны. В приведенном ниже коде для дельт используется int_16t. Я использовал это в случаях, когда значения данных гарантированно составляли около 1 км, поэтому я использовал шкалу (в приведенном ниже коде) 16, чтобы можно было справиться с различиями в 2 км.

typedef struct
{   float   scale;
    double  start;
    int n;
    int16_t*    delta;
} compiT;

compiT  compressi( int n, const double* data, float scale)
{
compiT  c = (compiT){ scale, data[0], n-1, malloc( (n-1)*sizeof *c.delta)};
double  r = c.start;
    for( int i=1; i<n; ++i)
    {
    int16_t d = (int16_t)round(c.scale*(data[i]-r));
        c.delta[i-1] = d;
        r += ((double)d)/c.scale;
    }
    return c;
}

compiT  bad_compressi( int n, const double* data, float scale)
{
compiT  c = (compiT){ scale, data[0], n-1, malloc( (n-1)*sizeof *c.delta)};
    for( int i=1; i<n; ++i)
    {
    int16_t d = (int16_t)round(c.scale*(data[i]-data[i-1]));
        c.delta[i-1] = d;
    }
    return c;
}

static  double* uncompressi( compiT c)
{
double* d = malloc( (c.n+1)*sizeof *d);
double  r = c.start;
    d[0] = r;
    for( int i=1; i<=c.n; ++i)
    {
    double  delta = ((double)c.delta[i-1])/c.scale;
        r += delta;
        d[i] = r;
    }
    return d;
}

В прогонов произвольной длины максимальная ошибка (ie разница между исходными данными и распакованными сжатыми данными) составляла, как и должно быть, около 3 см, тогда как при использовании bad_compressor ошибки составляли около 0,5 м на прогонах 1000, 2,5 м при пробегах 10000

Конечно, если у вас нет гарантий ограниченности различий, вам нужно будет включить какой-то перезапуск.

2 голосов
/ 05 августа 2020

Вам не нужен восьмибайтовый формат с плавающей запятой для ожидаемого числа значащих цифр и для ограниченного диапазона возможных значений. Я бы начал с преобразования данных в последовательность целых чисел подходящей длины, которая может представлять точность и диапазон ваших значений. Похоже, что двух четырехбайтовых целых должно хватить. Здесь есть сжатие с коэффициентом два.

Затем замените каждую точку разницей этой точки и предыдущей, за исключением первой точки. Теперь целые числа должны быть меньше, что позволит любому компрессору без потерь общего назначения уменьшить это количество. Если разница составляет всего около 0,1 °, вы можете получить еще два множителя.

Это простой пример, где прогноз для этой точки является последней точкой. Если ваша последовательность точек представляет собой путь, вы можете сделать что-нибудь более сложное, где вы моделируете скорость. В этом случае вы распространяете последнюю точку, используя последнюю смоделированную скорость, и вычитаете ее из текущей точки. с точностью до 2-5 метров. Используя трехбайтовые целые числа, вы можете получить разрешение 2,4 метра на экваторе. Это обеспечивает уменьшение на 62,5% до дифференцирования и сжатия. Это также дает вам немного широты, поскольку она имеет половину диапазона долготы. Этот бит можно использовать для отметки, является ли это абсолютной координатой или предсказанной по последней координате.

0 голосов
/ 23 августа 2020

Я нашел существующее решение: есть формат данных TinyWKB или TWKB или Tiny Well-known Binary, который соответствует моим потребностям.

  • Он хранит примитивы геометрии (он поддерживает LineString для моих текущих потребностей).
  • Он хранит данные с использованием дельт.
  • Он поддерживает точность для чисел с плавающей запятой (от +2 до -2) .
  • Поддерживает ID-данные для геометрии.
  • Может быть записано с использованием модуля. Net для NetTopologySuite .

TinyWKB разработан на основе WKT -> WKB -> TWKB для хранения примитивов геометрии (Points, LineStrings, Polygons, MultiPoints, MultiLineStrings, MultiPolygons, GeometryCollections)

...