From cf193d93854e6a13b391eef2199fb9ea1edf930b Mon Sep 17 00:00:00 2001 From: Yohan Cabion <105045349+yohancabion@users.noreply.github.com> Date: Fri, 22 Mar 2024 14:19:33 +0100 Subject: [PATCH] Handle setting geometry column name for GDAL 3.6+ (#123) Co-authored-by: Yohan Cabion --- errors.go | 4 ++++ godal.cpp | 23 +++++++++++++++++++++++ godal.go | 29 ++++++++++++++++++++++++----- godal.h | 2 ++ godal_test.go | 25 ++++++++++++++++++++++++- options.go | 13 +++++++++++++ 6 files changed, 90 insertions(+), 6 deletions(-) diff --git a/errors.go b/errors.go index d90775b..093ac34 100644 --- a/errors.go +++ b/errors.go @@ -134,6 +134,7 @@ func ErrLogger(fn ErrorHandler) interface { SetNoDataOption SetScaleOffsetOption SetGeoTransformOption + SetGeometryColumnNameOption SetProjectionOption SetSpatialRefOption SieveFilterOption @@ -241,6 +242,9 @@ func (ec errorCallback) setFillnodataOpt(o *fillnodataOpts) { func (ec errorCallback) setGeojsonOpt(o *geojsonOpts) { o.errorHandler = ec.fn } +func (ec errorCallback) setGeometryColumnNameOpt(o *setGeometryColumnNameOpts) { + o.errorHandler = ec.fn +} func (ec errorCallback) setGeometryTransformOpt(o *geometryTransformOpts) { o.errorHandler = ec.fn } diff --git a/godal.cpp b/godal.cpp index d1f7951..26d6a5f 100644 --- a/godal.cpp +++ b/godal.cpp @@ -816,6 +816,29 @@ void godalLayerSetFeature(cctx *ctx, OGRLayerH layer, OGRFeatureH feat) { godalUnwrap(); } +void godalLayerSetGeometryColumnName(cctx *ctx, OGRLayerH layer, char *name) { + godalWrap(ctx); +#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 6, 0) + OGRGeomFieldDefnH fieldWithNewName = OGR_GFld_Create(name, OGRwkbGeometryType(0)); + OGRErr gret = OGR_L_AlterGeomFieldDefn(layer, 0, fieldWithNewName, ALTER_GEOM_FIELD_DEFN_NAME_FLAG); + if(gret!=0){ + forceOGRError(ctx,gret); + } +#else + CPLError(CE_Failure, CPLE_NotSupported, "OGR_L_AlterGeomFieldDefn is only supported in GDAL version >= 3.6"); +#endif + godalUnwrap(); +} + +void godalFeatureSetGeometryColumnName(cctx *ctx, OGRFeatureH feat, char *name) { + godalWrap(ctx); + OGRGeomFieldDefnH gfdef = OGR_F_GetGeomFieldDefnRef(feat, 0); + if(gfdef != nullptr){ + OGR_GFld_SetName(gfdef, name); + } + godalUnwrap(); +} + void godalFeatureSetGeometry(cctx *ctx, OGRFeatureH feat, OGRGeometryH geom) { godalWrap(ctx); OGRErr gret = OGR_F_SetGeometry(feat,geom); diff --git a/godal.go b/godal.go index c0c9f93..30f25c1 100644 --- a/godal.go +++ b/godal.go @@ -2862,13 +2862,18 @@ func (f *Feature) SetGeometry(geom *Geometry, opts ...SetGeometryOption) error { } // SetGeometryColumnName set the name of feature first geometry field. -func (f *Feature) SetGeometryColumnName(name string) { +// Deprecated when running with GDAL 3.6+, use SetGeometryColumnName on Layer instead. +// No more supported when running with GDAL 3.9+. +func (f *Feature) SetGeometryColumnName(name string, opts ...SetGeometryColumnNameOption) error { + so := &setGeometryColumnNameOpts{} + for _, o := range opts { + o.setGeometryColumnNameOpt(so) + } cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) - gfdef := C.OGR_F_GetGeomFieldDefnRef(f.handle, C.int(0)) - if gfdef != nil { - C.OGR_GFld_SetName(gfdef, (*C.char)(unsafe.Pointer(cname))) - } + cgc := createCGOContext(nil, so.errorHandler) + C.godalFeatureSetGeometryColumnName(cgc.cPointer(), f.handle, (*C.char)(cname)) + return cgc.close() } // SetFID set feature identifier @@ -3271,6 +3276,20 @@ func (layer Layer) DeleteFeature(feat *Feature, opts ...DeleteFeatureOption) err return cgc.close() } +// SetGeometryColumnName set the name of feature first geometry field. +// Only supported when running with GDAL 3.6+. +func (layer Layer) SetGeometryColumnName(name string, opts ...SetGeometryColumnNameOption) error { + so := &setGeometryColumnNameOpts{} + for _, o := range opts { + o.setGeometryColumnNameOpt(so) + } + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + cgc := createCGOContext(nil, so.errorHandler) + C.godalLayerSetGeometryColumnName(cgc.cPointer(), layer.handle(), (*C.char)(cname)) + return cgc.close() +} + // CreateLayer creates a new vector layer // // Available CreateLayerOptions are diff --git a/godal.h b/godal.h index 6c4bc78..f7d5a79 100644 --- a/godal.h +++ b/godal.h @@ -104,6 +104,8 @@ extern "C" { void godalLayerCreateFeature(cctx *ctx, OGRLayerH layer, OGRFeatureH feat); OGRFeatureH godalLayerNewFeature(cctx *ctx, OGRLayerH layer, OGRGeometryH geom); void godalLayerDeleteFeature(cctx *ctx, OGRLayerH layer, OGRFeatureH feat); + void godalLayerSetGeometryColumnName(cctx *ctx, OGRLayerH layer, char *name); + void godalFeatureSetGeometryColumnName(cctx *ctx, OGRFeatureH feat, char *name); void godalFeatureSetGeometry(cctx *ctx, OGRFeatureH feat, OGRGeometryH geom); void godalFeatureSetFieldInteger(cctx *ctx, OGRFeatureH feat, int fieldIndex, int value); void godalFeatureSetFieldInteger64(cctx *ctx, OGRFeatureH feat, int fieldIndex, long long value); diff --git a/godal_test.go b/godal_test.go index 170e624..97244bc 100644 --- a/godal_test.go +++ b/godal_test.go @@ -2677,6 +2677,11 @@ func TestLayerModifyFeatures(t *testing.T) { ds, _ := Open("testdata/test.geojson") //read-only defer ds.Close() l := ds.Layers()[0] + + ehc := eh() + err := l.SetGeometryColumnName("error", ErrLogger(ehc.ErrorHandler)) // can't set geometry colum name on geojson + assert.Error(t, err) + for { ff := l.NextFeature() if ff == nil { @@ -2696,6 +2701,15 @@ func TestLayerModifyFeatures(t *testing.T) { dsm, _ := ds.VectorTranslate("", []string{"-of", "Memory"}) defer dsm.Close() l = dsm.Layers()[0] + + err = l.SetGeometryColumnName("no_error_after_3.6") + runtimeVersion := Version() + if runtimeVersion.Major() <= 3 && runtimeVersion.Minor() < 6 { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + for { ff := l.NextFeature() if ff == nil { @@ -3066,7 +3080,16 @@ func TestFeatureAttributes(t *testing.T) { assert.NoError(t, err) fc, _ := lyr.FeatureCount() assert.Equal(t, fc, 1) - nf.SetGeometryColumnName("no_error") + + ehc = eh() + err = nf.SetGeometryColumnName("no_error_before_3_9", ErrLogger(ehc.ErrorHandler)) + runtimeVersion := Version() + if runtimeVersion.Major() <= 3 && runtimeVersion.Minor() < 9 { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + nf.SetFID(99999999999) attrs := nf.Fields() intCol := attrs["intCol"] diff --git a/options.go b/options.go index 3beb34b..bcb85c4 100644 --- a/options.go +++ b/options.go @@ -578,6 +578,7 @@ type updateFeatureOpts struct { type UpdateFeatureOption interface { setUpdateFeatureOpt(o *updateFeatureOpts) } + type deleteFeatureOpts struct { errorHandler ErrorHandler } @@ -590,6 +591,18 @@ type DeleteFeatureOption interface { setDeleteFeatureOpt(o *deleteFeatureOpts) } +type setGeometryColumnNameOpts struct { + errorHandler ErrorHandler +} + +// SetGeometryColumnNameOption is an option passed to Layer.SetGeometryColumnName() or Feature.SetGeometryColumnName() +// +// Available options are: +// - ErrLogger +type SetGeometryColumnNameOption interface { + setGeometryColumnNameOpt(o *setGeometryColumnNameOpts) +} + type geometryTransformOpts struct { errorHandler ErrorHandler }