Point Feature Attribute List Modification

ikabottikabott Global Mapper UserPosts: 38Trusted User
edited February 2014 in SDK
Hi,

I am trying to add 2 attributes to every point in a points only shape. To do that I loop through the points:
//Loop on the vector layer  points
IntPtr pointPtr;
IntPtr attListPtr = new IntPtr();
GM_Point_t pointXY;
GM_PointFeature_t point = new GM_PointFeature_t();
GM_VectorFeature_t feature = new GM_VectorFeature_t();
float elev;
ushort newAttListSize;
for (uint pointID = 0; pointID < tempInfo.mNumPoints; pointID++)
{
    pointPtr = GlobalMapperDLL.GM_GetPointFeature(bldp, pointID);
    point = (GM_PointFeature_t)Marshal.PtrToStructure(pointPtr, point.GetType());
    pointXY = point.mPos;
    feature = point.mFeatureInfo;

    //Check it is inside the grid bounds
    if ((pointXY.mX > CurrentViewRect.mMaxX) || (pointXY.mX < CurrentViewRect.mMinX) || (pointXY.mY > CurrentViewRect.mMaxY) || (pointXY.mY < CurrentViewRect.mMinY))
        continue;

    //Get point elevation from grid
    LastGMError = GlobalMapperDLL.GM_GetLocationElevation(elevGrid, pointXY.mX, pointXY.mY, out elev);

    if (LastGMError != GlobalMapperDLL.GM_Error_t32.GM_Error_None)
    {
        Console.WriteLine("Error in point "+pointID+": "+LastGMError.ToString()+" (" + DateTime.Now.TimeOfDay.ToString() + ") ...");
        continue;
    }

    //Retrieve points attribute
    float relelev = 0;
    GM_AttrValue_t[] att = new GM_AttrValue_t[feature.mNumAttrs + 2];
    for (int attNum = 0; attNum < feature.mNumAttrs; attNum++)
    {
        IntPtr attPtr = (IntPtr)((UInt32)feature.mAttrList + attNum * 8);
        att[attNum] = (GM_AttrValue_t)Marshal.PtrToStructure(attPtr, att[attNum].GetType());
        if (att[attNum].mName == "RELELEV") relelev = float.Parse(att[attNum].mVal);
    }
    att[feature.mNumAttrs].mName = "Down";
    att[feature.mNumAttrs].mVal  = elev.ToString("F2");
    att[feature.mNumAttrs+1].mName = "Top";
    att[feature.mNumAttrs+1].mVal = (elev+relelev).ToString("F2");
    newAttListSize = Convert.ToUInt16(feature.mNumAttrs + 2);
    attListPtr = Marshal.AllocHGlobal(Marshal.SizeOf(att[0]) * newAttListSize);
    Marshal.StructureToPtr(att[0], attListPtr, false);
    try
    {
        LastGMError = GlobalMapperDLL.GM_SetFeatureAttrList(bldp, GlobalMapperDLL.GM_FeatureClassType_t8.GM_FeatureClass_Point, pointID, attListPtr, newAttListSize);
    }
    catch (Exception e)
    {
        Console.WriteLine("Error in point "+att[2].mVal+"("+pointID.ToString("D5")+"): " + e.Message);
        errors++;
    }
    noErrors++;

    GlobalMapperDLL.GM_FreePointFeature(pointPtr);
}

GlobalMapperDLL.GM_VectorExportFlags_t32 exFlag = GlobalMapperDLL.GM_VectorExportFlags_t32.GM_VectorExportFlags_ExportAll | GlobalMapperDLL.GM_VectorExportFlags_t32.GM_VectorExportFlags_ExportAttrs;
LastGMError = GlobalMapperDLL.GM_ExportVector( outputPath2 + "bldpExt.shp", GlobalMapperDLL.GM_VectorExportFormat_t32.GM_Export_Shapefile, bldp, ref CurrentViewRect , exFlag, IntPtr.Zero); 

With this implementation, around 9% of the point features throw an "Access to forbidden memory" when calling 'GM_SetFeatureAttrList'.

Even those features not failing to set the attributes, show wrong when the exported shape is loaded in GM. It looks like there is around 12 fields of strange characters:
errorPoint.jpg

Any idea?

Thanks in advance
Oscar Gomez

Comments

  • global_mapperglobal_mapper Administrator Posts: 17,238
    edited December 2009
    Do you get some kind of error running your code or are you just providing some sample code?

    Thanks,

    Mike
    Global Mapper Support
    support@globalmapper.com
  • ikabottikabott Global Mapper User Posts: 38Trusted User
    edited December 2009
    I don't know if you read the post before it was finished. I have recently edited to complete the information.

    I am getting a "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." in GM_SetFeatureAttrList for more or less 9% of the features (9800 of 12000). The number changes from run to run, but the wrong results when loading the shape in GM are in all the features.

    Oscar
  • ikabottikabott Global Mapper User Posts: 38Trusted User
    edited December 2009
    I don't know if it is of any help, but when loading the resulting shape into GM, the list of attributes has 41 rows. One of this rows is ELEVATION, which is not in the attribute list, but more a Z value for the point, which means the att. list has 40 rows. Thjs might have relation with:

    attListPtr = Marshal.AllocHGlobal(Marshal.SizeOf(att[0]) * newAttListSize);

    because Marshal.SizeOf(att[0]) => 8
    and newAttListSize => 5

    Oscar
  • global_mapperglobal_mapper Administrator Posts: 17,238
    edited December 2009
    Oscar,

    I'm not a very proficient C# programmer, but I wonder about the following line:

    attListPtr = Marshal.AllocHGlobal(Marshal.SizeOf(att[0]) * newAttListSize)

    Can you confirm that Marshal.SizeOf(att[0]) is returning the correct size of 8? I'm also not very confident about how exactly Marshal.StructureToPtr(att[0], attListPtr, false) works when copying from a C# array to your memory buffer, especially when the memory buffer also contains char* pointers. Perhaps you need to actually loop through the array and setup each char* pointer for the attribute array individually?

    Thanks,

    Mike
    Global Mapper Support
    support@globalmapper.com
  • ikabottikabott Global Mapper User Posts: 38Trusted User
    edited December 2009
    Hi Mike,

    I can confirm that Marshal.SizeOf(att[0]) returns 8.

    About the second subject, I don't really know what do you mean by "loop through the array and setup each char* pointer".

    Thanks
    Oscar
  • global_mapperglobal_mapper Administrator Posts: 17,238
    edited December 2009
    Oscar,

    By looping through the list, I mean doing something like the following pseudo-code:

    for ( int i = 0; i < newAttListSize; i++ )
    {
    // Get pointers to attribute name and value locations in allocated memory buffer
    IntPtr attNamePtr = (IntPtr)((UInt32)attListPtr + i * 8);
    IntPtr attValVtr = (IntPtr)((UInt32)attListPtr + i * 8 + 4);

    // Allocate memory buffers to hold string values for name and value using
    // Marshal.AllocHGlobal

    // Copy string data from C# array structure to new memory buffers

    // Assign memory buffer pointers to *attNamePtr and *attNameVal

    }

    I'm not a seasoned C# programmer so I'm not sure how you can easily do such things, but there has to be something like a memory copy command or something in C#, right?

    Thanks,

    Mike
    Global Mapper Support
    support@globalmapper.com
  • jharding@mtaonline.netjharding@mtaonline.net Global Mapper User Posts: 7
    edited February 2014
    I am trying to get access to a point shape attribute list but am having problems. I am using the below code and keep getting a number and some random symbols in the .mName please help.

    Thanks in advance.


    IntPtr theInfoPtr;


    IntPtr tempPointFeature;


    for (Int32 j = 0; j < Convert.ToInt32(theLayerInfo.mNumPoints); j++)

    {

    theInfoPtr =
    GlobalMapperDLL.GM_GetPointFeature(theLayerHandle, (uint)j);

    myPointStyle = (
    GM_PointStyle_t)Marshal.PtrToStructure(theInfoPtr, myPointStyle.GetType());


     

    tempPointFeature =
    GlobalMapperDLL.GM_GetPointFeature(theLayerHandle, (uint)j);

    myPointFeature = (
    GM_PointFeature_t)Marshal.PtrToStructure(tempPointFeature, myPointFeature.GetType());

    feature = myPointFeature.mFeatureInfo;

    pointXY = myPointFeature.mPos;




    GM_AttrValue_t[] att = new GM_AttrValue_t[feature.mNumAttrs];

    for (int attNum = 0; attNum < feature.mNumAttrs; attNum++)

    {




    IntPtr attPtr = (IntPtr)((UInt32)feature.mAttrList + (attNum * 8));

    att[attNum] = (
    GM_AttrValue_t)Marshal.PtrToStructure(attPtr, att[attNum].GetType());



    string tempString = new string(att[attNum].mName);



    if (tempString == "ELEV")

    {




    }

    }
  • global_mapperglobal_mapper Administrator Posts: 17,238
    edited February 2014
    I'm not sure what the first call to GM_GetPointFeature is for where you assign to a point style and you also need a GM_FreePointFeature call at the end of the loop to free the memory.

    Those are unrelated though. As this comes up, I have updated the GM_VectorFeature_t declaration in GlobalMapperDLLWrapper.cs to have a getAttrValueList function that does all of this for you. Replace the existing calls GM_AttrValue_t and GM_VectorFeature_t declarations in GlobalMapperDLLWrapper.cs with the following:

    // This type is used to represent a single attribute value pair
    unsafe public struct GM_AttrValueCharPtr_t
    {
    public char* mName; // Name of the attribute
    public char* mVal; // Value of the attribute
    };

    // This type is used to represent a single attribute value pair
    unsafe public struct GM_AttrValueIntPtr_t
    {
    public IntPtr mName; // Name of the attribute
    public IntPtr mVal; // Value of the attribute
    };

    // This type is used to represent a single attribute value pair
    public struct GM_AttrValue_t
    {
    public String mName; // Name of the attribute
    public String mVal; // Value of the attribute
    };

    // This type is used as the base for any vector feature information
    public struct GM_VectorFeature_t
    {
    // Get a list of extracted attribute values
    public GM_AttrValue_t[] getAttrValueList()
    {
    // Allocate array to hold extracted attribute values
    GM_AttrValue_t[] theAttrList = new GM_AttrValue_t[mNumAttrs];

    // Extract from pointer list
    for (UInt64 i = 0; i < mNumAttrs; i++)
    {
    // Get attribute value with pointers to name and value
    IntPtr theAttrPtr = (IntPtr)((UInt64)mAttrList + i * 2 * (UInt64)IntPtr.Size);
    GM_AttrValueIntPtr_t theAttrValPtr = new GM_AttrValueIntPtr_t();
    theAttrValPtr = (GM_AttrValueIntPtr_t)Marshal.PtrToStructure(theAttrPtr, typeof(GM_AttrValueIntPtr_t));

    // Extract name and value
    theAttrList.mName = Marshal.PtrToStringAnsi(theAttrValPtr.mName);
    theAttrList.mVal = Marshal.PtrToStringAnsi(theAttrValPtr.mVal);
    }

    return ( theAttrList );
    }

    // Member data
    public string mName; // Name of the feature
    public string mDesc; // Description of the feature
    public UInt16 mClass; // Global Mapper classification assigned to the feature
    public IntPtr mAttrList; // List of attributes associated with feature (pointer to GM_AttrValueCharPtr_t/GM_AttrValueIntPtr_t array)
    public UInt16 mNumAttrs; // Number of attributes in mAttrList
    };

    Then you can just call tempPointFeature.mFeatureInfo.getAttrValueList() to get the extracted string values.

    Thanks,

    Mike
    Global Mapper Guru
    geohelp@bluemarblegeo.com
    Blue Marble Geographics for Coordinate Conversion, Image Reprojection and Vector Translation
Sign In or Register to comment.