Чтение аттрибутов из шейпфайла (SHP/DBX) через .NET обертку над shapelib

by Dmitry [dimaka] Pavlov 22. July 2010 22:43

В посте о программировании MapServer средствами .NET (C#) я упоминал .NET обертку (скачать ZIP) над библиотекой shapelib, которую я использовал для чтения и записи атрибутивной информации шейпфайла (SHP) и координат, хранящихся в файле DBF.

Приведенный ниже код для считывания атрибутов и координат может служить примером для операций считывания (запись через shapelib также возможна, но в этом посте не рассматривается) и, возможно, поможет сэкономить вам время при написании собственного кода:

/// <summary>
/// Чтение аттрибутивной информации в виде DataTable.
/// </summary>
/// <param name="shapeFilePath">The shape file path.</param>
/// <returns></returns>
public static DataTable ReadAttributes(string shapeFilePath)
{
    // Table to store the data
    DataTable attributes = new DataTable("Attributes");

    // -------------------------------------------------------------------
    // Чтение аттрибутивной информациии из DBF файла
    // -------------------------------------------------------------------
    string dbxFilePath = Path.ChangeExtension(shapeFilePath, ".dbf");
    IntPtr hDbf = ShapeLib.DBFOpen(dbxFilePath, FileAccessMode.BinaryOpenForReadWrite);

    if (!hDbf.Equals(IntPtr.Zero))
    {
        // verify the table structure
        int recCount = ShapeLib.DBFGetRecordCount(hDbf);
        int fieldCount = ShapeLib.DBFGetFieldCount(hDbf);
        DataColumn[] columns = new DataColumn[fieldCount + 1];

        // получаем информацию о столбцах
        ShapeLib.DBFFieldType[] fieldTypes = new ShapeLib.DBFFieldType[fieldCount];
        string[] fieldNames = new string[fieldCount];
        int fieldWidth = 0;
        int numDecimals = 0;

        columns[0] = new DataColumn(PseudoColumnName, typeof (int));
        for (int iField = 0; iField < fieldCount; iField++)
        {
            StringBuilder sb = new StringBuilder();
            fieldTypes[iField] = ShapeLib.DBFGetFieldInfo(hDbf, iField, sb, ref fieldWidth, ref numDecimals);
            fieldNames[iField] = sb.ToString();

            string columnName = fieldNames[iField];
            Type columnType = ConvertType(fieldTypes[iField]);
            int fieldIndex = ShapeLib.DBFGetFieldIndex(hDbf, columnName);

            columns[fieldIndex + 1] = new DataColumn(columnName, columnType);
        }

        // Добавляем реальные столбцы 
        attributes.Columns.AddRange(columns);

        // получаем информацию о записях
        for (int iShape = 0; iShape < recCount; iShape++)
        {
            DataRow row = attributes.NewRow();

            // заполняем псевдо индекс
            row[0] = iShape;

            for (int iField = 0; iField < fieldCount; iField++)
            {
                int columnIndex = iField + 1; // с учетом псевдо индекса
                if (ShapeLib.DBFIsAttributeNULL(hDbf, iShape, iField) != 0)
                {
                    // Значения нет - присваиваем NULL 
                    row[columnIndex] = DBNull.Value;
                }
                else
                {
                    // Значение есть - присваиваем значение нужного типа
                    switch (fieldTypes[iField])
                    {
                        case (ShapeLib.DBFFieldType.FTString):
                            row[columnIndex] = ShapeLib.DBFReadStringAttribute(hDbf, iShape, iField);
                            break;
                        case (ShapeLib.DBFFieldType.FTDouble):
                            row[columnIndex] = ShapeLib.DBFReadDoubleAttribute(hDbf, iShape, iField);
                            break;
                        case (ShapeLib.DBFFieldType.FTLogical):
                            row[columnIndex] = ShapeLib.DBFReadLogicalAttribute(hDbf, iShape, iField);
                            break;
                        case (ShapeLib.DBFFieldType.FTInteger):
                            row[columnIndex] = ShapeLib.DBFReadIntegerAttribute(hDbf, iShape, iField);
                            break;
                        case (ShapeLib.DBFFieldType.FTDate):
                            row[columnIndex] = ShapeLib.DBFReadDateAttribute(hDbf, iShape, iField);
                            break;
                        default: // case (ShapeLib.DBFFieldType.FTInvalid): 
                            row[columnIndex] = DBNull.Value;
                            break;
                    }
                }
            }

            // Добавлем новую строку в таблицу
            attributes.Rows.Add(row);
        }
        // release resources
        ShapeLib.DBFClose(hDbf);
    }

    return attributes;
}

/// <summary>
/// Чтение координат слоя из SHP - шейп файла (пока не используется)
/// </summary>
/// <param name="shapeFilePath">Путь к шейпфайлу.</param>
/// <param name="objectIndex">Индект объекта в шейпфайле.</param>
/// <returns>Координаты слоя шейп файла в виде DataTable.</returns>
public static DataTable ReadCoordinates(string shapeFilePath, int objectIndex)
{
    DataTable coordinates = new DataTable();

    IntPtr hShp = ShapeLib.SHPOpen(shapeFilePath, FileAccessMode.BinaryOpenForReadWrite);
    if (!hShp.Equals(IntPtr.Zero))
    {
        //            - можно прочитать в цикле для каждого объекта в аттирбутивной информации
        //            - в этом примере метод получает индекс объекта параметром
        //            - для получения координат в цикле по всем объектам из шейп-файла - надо убрать 
        //            - комментарии и изменить сигнатуру метода

        // get shape info and verify shapes were created correctly
        // double[] minB = new double[4];
        // double[] maxB = new double[4];
        // int nEntities = 0;
        // ShapeLib.ShapeType shapeType = 0;
        // ShapeLib.SHPGetInfo(hShp, ref nEntities, ref shapeType, minB, maxB);

        // for (int objectIndex = 0; objectIndex < nEntities; i++) 
        {
            // test SHPReadObject on the first shape
            int iShape = objectIndex;
            IntPtr pshpObj = ShapeLib.SHPReadObject(hShp, iShape);

            // Get the SHPObject associated with our IntPtr pshpObj
            // We create a new SHPObject in managed code, then use Marshal.PtrToStructure
            // to copy the unmanaged memory pointed to by pshpObj into our managed copy.
            ShapeLib.SHPObject shpObj = new ShapeLib.SHPObject();
            Marshal.PtrToStructure(pshpObj, shpObj);

            int nVertices = shpObj.nVertices;
            coordinates.Columns.Add("X", typeof (double));
            coordinates.Columns.Add("Y", typeof (double));
            coordinates.Columns.Add("Z", typeof (double));
            coordinates.Columns.Add("M", typeof (double));

            double[] xCoord = new double[nVertices];
            double[] yCoord = new double[nVertices];
            double[] zCoord = new double[nVertices];
            double[] mCoord = new double[nVertices];

            // Recover the vertices for this shape. Use Marshal.Copy to copy the memory pointed 
            // to by shpObj.padfX and shpObj.padfX (each an IntPtr) to a actual array.
            Marshal.Copy(shpObj.padfX, xCoord, 0, nVertices);
            Marshal.Copy(shpObj.padfY, yCoord, 0, nVertices);
            Marshal.Copy(shpObj.padfZ, zCoord, 0, nVertices);
            Marshal.Copy(shpObj.padfM, mCoord, 0, nVertices);

            // read vertices - XYZM per data row in table
            for (int v = 0; v < nVertices; v++)
            {
                coordinates.Rows.Add(xCoord[v], yCoord[v], zCoord[v], mCoord[v]);
            }

            // free resources
            // The .NET GC should clean up shpObj, but we'll force the cleanup anyway...
            ShapeLib.SHPDestroyObject(pshpObj);
        }

        ShapeLib.SHPClose(hShp);
    }
    return coordinates;
}

Tags:

.NET | Coding | GIS | по-русски

Comments

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading



Calendar

<<  September 2010  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

View posts in large calendar