EditUpdateShape problem

Hi,

I’ve developed an ms access based application. In this application i create a new shapefile with “handmade” polygon objects, via three routines: one creates the shapefile on disk, - Shapefiletype POLYGON, one creates the polygon objects and one that loops through a table, gathers all affected records, and adds new records to the shapefile with the objects of routine 2.
This works perfectly, - i can load that shapefile in MapWinGis-OCX and in QGis, - no problem here.

Now i want to add a routine to update the shape-Object of specific Shapefile-Records.
For this i open the previously generated shapefile from disk, startEdtingShapes(true) → returns true, create a new polygon object with the same routine used to create the shapefile and use the .EditUpdateShape-Method to update the shape-object.

The EditUpdateShape-Method does not throw an error when updating the shape-object, and in this routine there is already a buildin check if the shapetypes of file and new object match, - but when i call shapefile.save() or .stopeditngshapes(true,true) to save the changes this way the error message i get is:
Shape type is incompatible with the shapefile type.

i took the source code of the current release version, created my own debug version and debuged this behaviour in vs 2019.
When .EditUpdateShape is entered the newShape-object gets passed correctly and is of type POLYGON with parts, points and a boundary, but when stopeditingshapes is called the routine VerifyMemShapes tells me for this updated shapeindex, that the object stored there is not present any
longer (no parts, no points, no extend, random numbers vor _shapeType) and so that routine fails.
The shapefile is disk based, and since the application can write / create that file, it’s obviously no rights-problem.
I tried it with fastmode on and off, no changes here.

I’m a bit lost here and happy about any idea that might help.

Cheers
Stefan

Hello Stefan.

I may or may not be able to reproduce this without your specific code, but we can start with a question.

Here’s the code leading up to the specific error you are seeing:

	shp->get_ShapeType(&shapetype);
	// MWGIS-91
	bool areEqualTypes = shapetype == _shpfiletype;
	if (!areEqualTypes){
		areEqualTypes = ShapeUtility::Convert2D(shapetype) == ShapeUtility::Convert2D(_shpfiletype);
	}
	shp->get_NumPoints(&numPoints);
	shp->get_NumParts(&numParts);

	if (shapetype != SHP_NULLSHAPE && !areEqualTypes)
	{	
		ErrorMessage(tkINCOMPATIBLE_SHAPE_TYPE);
		return FALSE;
	}

So, presuming that you are falling into the error where areEqualTypes = false, what type is showing for your shapeType following the call to get_ShapeType?

And it sounds like you’re also saying that numPoints and numParts are zero?

Could you post the code that creates the shape?

Thank you.
Jerry.

@jTati Are you still having this issue?

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

Thanks Stefan,

Your changes look fine. But I’m not a C++ developer :wink:

Can you make a pull request against the development branch?
Then I will ask @jerryfaust to review it and if it is OK we will merge your changes.

Hi Paul,

if someone can explain to me, how to create a pull request, i would like to do so.

Cheers

Does this document help: Creating an issue or pull request - GitHub Docs