PVOID - Шейпфайлы ESRI

Шейпфайлы ESRI

Однажды при обработке геоданных мне приходилось иметь дело с форматом файла *.shp, который называют шейпфайлом. Файл *.shp - это только один из трех необходимых компонентов шейпфайла, потому что каждой геометрической фигуре соответствует также запись в таблице DBASE III (*.dbf) и запись в индексном файле с расширением *.shx, хранящий смещения записей в файле *.shp. Все файлы должны находиться в одном каталоге и иметь одно имя.

  Готовые шейпфайлы с геоданными можно загрузить, например, с ресурса gis-lab.info либо создать свои шейпфайлы в редакторе.

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

  В шейпфайлах можно хранить любые свои фигуры, а значит можно использовать их в своем ПО, если конечно парсер шейпфайла не окажется сложнее остальной програмы.)) Касательно таблицы: в Сети достаточно много ресурсов с описанием формата DBF, а также готовых парсеров. Что же до shp, то у меня возникали определенные вопросы при чтении файла. Для опытных программистов это не будет проблемой, но тогда длина воссозданной мной по документации структуры заголовка файла никак не хотела равняться 100 байтам, хотя и должна была. Загвоздка была в том, что по умолчанию выравнивание членов структур было установлено компилятором по границе 8 байт, потому и размер структуры был кратным восьми - 104 байта. Отличная статья на эту тему имеется на Хабре. Вылечить это можно как из кода директивой #pragma pack(push, N), установив N равным 4, 2 или 1, так и в настройках компилятора, в этом случае изменение будет касаться всего проекта.

 Не совсем ясным является суть использования как big-endian, так и little-endian порядка байт в структурах, что создает необходимость совершать определенные телодвижения. Преобразование порядка байт можно произвести при помощи функций htonl() / ntohl() и им подобных как на Windows, так и на Linux.

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

 Отсюда возникает резонный вопрос: как, имея только координаты точек, определить направление обхода полигона? Для этого случая применяется вычисление ориентированной площади полигона. Эта площадь помимо самого значения площади имеет также и знак, то есть может быть отрицательной в том случае, если направление обхода выбрано по часовой стрелке. Пример вычисления ориентированной площади:

#include <iostream>

using namespace std;

struct Point2d // Точка с вещественными координатами
{
    double x;
    double y;
    Point2d(): x(0.0), y(0.0) {};
    Point2d(double _x, double _y): x(_x), y(_y) {};
};

double OrientedArea( Point2d* pts, unsigned num )
{
    if( num <= 2 ) return 0; // Не менее трех точек в многоугольнике

    double sum=0.0, area=0.0;
    for ( unsigned i=0; i<num-1; i++ )
    {
        sum += (pts[i].x*pts[i+1].y - pts[i].y*pts[i+1].x);
    }
    sum += pts[num-1].x*pts[0].y - pts[num-1].y*pts[0].x;
    area = sum/2.0;

    return area;
};//OrientedArea()

int main()
{
    // Египетский треугольник.
    // Вершины заданы по часовой стрелке
    Point2d pointArrayCW[]  = {{0.0,0.0},{0.0,3.0},{4.0,0.0}};
    // Вершины заданы против часовой стрелки
    Point2d pointArrayCCW[] = {{0.0,0.0},{4.0,0.0},{0.0,3.0}};  

    double aCW  = OrientedArea(pointArrayCW, sizeof(pointArrayCW)/sizeof(Point2d));
    double aCCW = OrientedArea(pointArrayCCW, sizeof(pointArrayCCW)/sizeof(Point2d));

    cout << "clockwise: " << aCW << endl;
    cout << "counterclockwise: " << aCCW << endl;
    cin.get();
    return 0;
}

Получим:

  clockwise: -6
  counterclockwise: 6