Point Feature Attribute List Modification
Hi,
I am trying to add 2 attributes to every point in a points only shape. To do that I loop through the points:
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:

Any idea?
Thanks in advance
Oscar Gomez
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:

Any idea?
Thanks in advance
Oscar Gomez
Comments
Thanks,
Mike
Global Mapper Support
support@globalmapper.com
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
attListPtr = Marshal.AllocHGlobal(Marshal.SizeOf(att[0]) * newAttListSize);
because Marshal.SizeOf(att[0]) => 8
and newAttListSize => 5
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
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
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
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")
{
}
}
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