Hello Paul,
Jerry and i figured out, that the ocx has some problems when dealing with Shapefile records that contain empty Shapes, = ShapeType == SHP_NULLSHAPE.
When ShapeFile.FastMode is turned on, and a shapefile gets saved, these records get corrupted, and the ShapeFile.HasInvalidShapes returns TRUE afterwards.
Additionally, - and i’m a bit astonished that Jerry didn’t incorporate these changes, i found out, that the Shape-Validation-Functions in the code, do not handle SHP_NULLSHAPE-Type-Objects correctly.
If you look at the Function “ValidateBasics” in the File Shape.cpp, a Shape-Object gets marked invalid when generally:
if (_shp->get_PointCount() == 0)
{
errMsg = "Shape hasn't got points";
failedCheck = NoPoints;
return false;
}
…but this is always true for a SHP_NULLSHAPE-Object.
I changed this code for my personal usage to:
ShpfileType shptype = ShapeUtility::Convert2D(_shp->get_ShapeType());
if ((_shp->get_PointCount() == 0) && (shptype != SHP_NULLSHAPE))
{
errMsg = "Shape hasn't got points";
failedCheck = NoPoints;
return false;
}
→ that makes more sense from my point of view.
In the same file, right below this function, the function “get_IsValid” is defined and there is also a flaw:
Every Shape of any Shape-Type gets checked if valid, but you don’t have to do that for an
object of SHP_NULLSHAPE, so i changed that function to:
// *************************************************************
// get_IsValid
// *************************************************************
// Checking validity of the geometry
STDMETHODIMP CShape::get_IsValid(VARIANT_BOOL* retval)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
* retval = VARIANT_FALSE;
ShpfileType shptype = ShapeUtility::Convert2D(_shp->get_ShapeType());
if (shptype != SHP_NULLSHAPE)
{
ShapeValidityCheck validityCheck;
if (!ValidateBasics(validityCheck, _isValidReason)) {
return S_OK;
}
// -----------------------------------------------
// check through GEOS (common for both modes)
// -----------------------------------------------
GEOSGeom hGeosGeom = GeosConverter::ShapeToGeom(this);
if (hGeosGeom == NULL)
{
_isValidReason = "Failed to convert to GEOS geometry";
return S_OK;
}
if (!GeosHelper::IsValid(hGeosGeom))
{
char* buffer = GeosHelper::IsValidReason(hGeosGeom);
_isValidReason = buffer;
GeosHelper::Free(buffer);
}
else
{
*retval = VARIANT_TRUE;
}
GeosHelper::DestroyGeometry(hGeosGeom);
}
else
{
*retval = VARIANT_TRUE;
}
return S_OK;
}
---------------------
In the Function “WriteShp” in the Source-File ShapeFile_ReadWrite.cpp
you can find the problem with SHP_NULLSHAPE records (somewhere around line 1265):
In the original code, this write operation fails with SHP_NULLSHAPE-records,
because the calculation of the number of nodes fails and leads to an error:
// disk based mode
// disk-based + fast data (probably a bit faster procedure can be devised)
// in-memory mode (COM points)
ShpfileType shptype = wrapper->get_ShapeType();
ShpfileType shptype2D = wrapper->get_ShapeType2D();
numPoints = wrapper->get_PointCount();
numParts = wrapper->get_PartCount();
//Write the Record
if( shptype2D == SHP_NULLSHAPE )
{
int ishptype = shptype;
fwrite(&shptype, sizeof(int), 1, shp);
}
so i changed it to this, so that these calculations only take place,
if the shape is not of type SHP_NULLSHAPE:
// disk based mode
// disk-based + fast data (probably a bit faster procedure can be devised)
// in-memory mode (COM points)
ShpfileType shptype = wrapper->get_ShapeType();
ShpfileType shptype2D = wrapper->get_ShapeType2D();
if ( shptype != SHP_NULLSHAPE )
{
numPoints = wrapper->get_PointCount();
numParts = wrapper->get_PartCount();
}
//Write the Record
if( shptype == SHP_NULLSHAPE || shptype2D == SHP_NULLSHAPE )
{
int ishptype = shptype;
fwrite(&shptype, sizeof(int), 1, shp);
}
With these modifications and FastMode = off, I had no problems since.
thanks and have a nice day
Stefan