Help needed - in-memory creation of shapefile

Mar 2, 2015 at 12:40 AM
Host is Access 2003 with latest MapWinGIS. I can successfully create a point shapefile from an Access table of Lat/Long coordinates. Now trying to populate attributes associated with each point. At the end of the creation loop, I send the shapefile to a disk file to check for success. The attributes are never updated to the values that are set from the Access table. Likewise, checking an individual point in break mode by executing debug.print sf.cellvalue(1,1) shows a null value.

I've tried all combinations of sf.StartEditingTable (and Shapes) and sf.StopEditingTable with no success. I must be missing something...

My code follows. Suggestions appreciated.

Also, there may be an error with the Shapefile.StopEditingTable documentation. The documentation indicates there are three parameters, but IntelliSense only shows two parameters, and VBA does not run with three parameters.

Private Sub cmdCreateShapefile_Click()
Dim sf As New MapWinGIS.Shapefile
Dim newPt As MapWinGIS.Point
Dim newShp As MapWinGIS.Shape
Dim bSuccess As Boolean
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim i As Long
Dim iIDIndex As Integer
Const FILENAME As String = "d:\temp\test.shp"
bSuccess = sf.CreateNewWithShapeID("", SHP_POINT)
sf.EditAddField "ConsolID", INTEGER_FIELD, 0, 10
iIDIndex = sf.Table.FieldIndexByName("ConsolID")

Set db = CurrentDb
Set rs = db.OpenRecordset("qryGPSTableSELECTED", dbOpenDynaset, dbSeeChanges)
i = 0
' sf.StartEditingShapes
' sf.StartEditingTable
With rs
    Do While Not (.EOF)
        Set newPt = New MapWinGIS.Point
        Set newShp = New MapWinGIS.Shape
        newPt.X = !long
        newPt.Y = !Lat
        newShp.Create SHP_POINT
        newShp.InsertPoint newPt, 0
        sf.EditInsertShape newShp, i
        sf.EditCellValue iIDIndex, i, !ConsolidatedID

        .MoveNext
        i = i + 1
    Loop
End With

sf.StopEditingTable True

rs.Close
Set rs = Nothing
Set db = Nothing

sf.Projection = tkMapProjection.PROJECTION_WGS84
Frame0.axmap.TileProvider = tkTileProvider.OpenStreetMap
i = Frame0.axmap.AddLayer(sf, True)
' MsgBox "Index: " & i & vbCrLf & sf.NumShapes
Frame0.axmap.ZoomToLayer (i)

KillShapeFile FILENAME
sf.SaveAs FILENAME
End Sub
Coordinator
Mar 2, 2015 at 11:05 AM
First remark.
EditAddField already returns the index of the newly created field (http://www.mapwindow.org/documentation/mapwingis4.9/group__shapefile__table.html#ga158f17d91bd1ffc32332fd64df224be5)

And most methods return false when something went wrong. With sf.ErrorMsg[sf.LastErrorCode] (C# code) you can check what the problem is.

And if you are going to call sf.EditAddField you need to start with sf.StartEditingShapes(true, null); (C# code) and end with sf.StopEditingShapes and check its return value.

When I look at this sample (http://www.mapwindow.org/documentation/mapwingis4.9/_minimal_distance_8cs-example.html) it seems you don't need to call sf.StopEditingShapes because it is an in-memory shapefile.

I hope it helps.

Paul
Mar 2, 2015 at 4:02 PM
Thanks Paul. Made the changes that you suggested, but still no success. Tried both in-memory shapefile and on-disk shapefile. Neither method populates the dbf field with the required information. Furthermore, with the in-memory version, the StopEditingShapes returns a False value, but the LastErrorCode is zero, so there is no error message. I presume this is expected behaviour with in-memory version With the on-disk version, StopEditingShapes is successful.

I tried checking the value immediately after writing into the table, and found puzzling results. Should I be able to read back the value immediately, or is there some additional step required? Excerpt from my code:

bSuccess = sf.EditCellValue(iIDIndex, i, !ConsolidatedID) ' always successful
var = sf.CellValue(iIDIndex, i) ' always empty

I appreciate the help!
Developer
Mar 3, 2015 at 7:13 AM
Hi, I checked the code of Table.EditCellValue. I think the behavior may be possible if the data type of the field in your dataset isn't one of the expected ones. So please try to set the value to explicitly typed variable before writing it to dataset:
Dim id as Integer      ' or other data type
' ...
id = !ConsolidatedID     ' perhaps some casting is needed
bSuccess = sf.EditCellValue(iIDIndex, i, id)
Also in initial code try to check the error code of the table class after inserting the value:
sf.Table.get_ErrorMsg(sf.Table.LastErrorCode)
I guess something may be reported there in case of failure.

Hope it helps,
Sergei
Mar 3, 2015 at 5:02 PM
Sergei

Thanks! That was clue enough for me to find the problem. Final solution was to create a Variant variable, copy the field value into the new variable, and use the Variant as the parameter for EditCellValue. It was insufficient to cast the field value (using CVar); had to use an explicit Variant variable.

Some other points I learned along the way:

The field in the recordset is a LongInteger and the field in the shapefile is FIELD_INTEGER. I thought they would be compatible. Tried casting my variable into Integer, but its value is too large for Integer so that was not a solution in this case.

I thought I may have made a mistake in the Precision and Width definitions in the EditAddField statement, so I reversed their order. With the values in the "wrong" order, the code still generates a valid FieldIndex number and the LastErrorCode value is zero. However, the resulting DBF does not contain the field (as it should with a faulty field definition). Seems like a bug - it should raise an error code if the field can't be created.

The EditCellValue statement did not raise an error (LastErrorCode = 0), and it returns a TRUE value, even when it fails to write the value into the table. However, when I single-stepped through the statement, it jumped into the callback error function, which is where I actually saw the incompatibility with the variable types. I think it should return an error code if it fails to write into the table.

StopEditingShapes returns a False value, but with a LastErrorCode=0 when used on an in-memory shapefile. Probably debatable, but I think returning a True value would be more appropriate in this situation.

Making slow and steady progress on getting my program operational! Thanks again for the help.

Jack