From 7d0dafa2d6c41e594217b784e19c0e463c062bce Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 14 Aug 2025 17:33:21 +0200 Subject: Add a NOT NULL contraint on geometry columns when creating the output layer. When the output driver supports altering GeomFieldDefn's nullable flag after the fact. PostgreSQL supports that. GPKG doesn't, but one can use GEOMETRY_NULLABLE=NO in layer creation options instead. See https://gdal.org/en/stable/development/rfc/rfc53_ogr_notnull_default.html This commit partially reverts 2c3ee79cb434fc4cf315ee3a6a526156053d76c4. --- .pylintrc | 2 +- import_source.py | 31 +++++++++++++++++++++---------- webmap-import | 9 +++++++++ 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/.pylintrc b/.pylintrc index b87e779..6b3e91d 100644 --- a/.pylintrc +++ b/.pylintrc @@ -2,7 +2,7 @@ max-args = 20 max-positional-arguments = 20 max-locals = 50 -max-branches = 25 +max-branches = 30 max-statements = 100 max-nested-blocks = 10 max-module-lines = 1250 diff --git a/import_source.py b/import_source.py index 1948f7b..f40cd38 100644 --- a/import_source.py +++ b/import_source.py @@ -117,7 +117,7 @@ def createOutputLayer(ds : gdal.Dataset, logging.info('Creating new destination layer "%s"', layername) geom_type = options['geometry-type'] - lco = options.get('options', []) + lco = options.get('options', None) drv = ds.GetDriver() if geom_type != ogr.wkbNone and drv.ShortName == 'PostgreSQL': @@ -137,16 +137,16 @@ def createOutputLayer(ds : gdal.Dataset, lco = [] lco = ['dim=' + dim] + lco # prepend dim= - geom_field = ogr.GeomFieldDefn() - geom_field.SetType(geom_type) - geom_field.SetNullable(False) # XXX doesn't do anything?? + kwargs = { 'geom_type': geom_type } if srs is not None: - geom_field.SetSpatialRef(srs) - logging.debug('CreateLayer("%s", geom_type="%s"%s%s)', - layername, ogr.GeometryTypeToName(geom_type), - ', srs="' + geom_field.GetSpatialRef().GetName() + '"' if srs is not None else '', - ', options=' + str(lco) if lco is not None else '') - lyr = ds.CreateLayerFromGeomFieldDefn(layername, geom_field, lco if lco is not None else []) + kwargs['srs'] = srs + if lco is not None: + kwargs['options'] = lco + logging.debug('CreateLayer(%s, geom_type="%s"%s%s)', layername, + ogr.GeometryTypeToName(geom_type), + ', srs="' + kwargs['srs'].GetName() + '"' if 'srs' in kwargs else '', + ', options=' + str(kwargs['options']) if 'options' in kwargs else '') + lyr = ds.CreateLayer(layername, **kwargs) if lyr is None: raise RuntimeError(f'Could not create destination layer "{layername}"') @@ -217,6 +217,17 @@ def createOutputLayer(ds : gdal.Dataset, raise RuntimeError(f'Could not create field "{fldName}"') logging.debug('Added field "%s" to output layer "%s"', fldName, layername) + if lyr.TestCapability(ogr.OLCAlterGeomFieldDefn): + # it appears using .CreateLayerFromGeomFieldDefn() on a a non-nullable + # GeomFieldDefn doesn't do anything, so we alter it after the fact instead + # (GPKG doesn't support this, use GEOMETRY_NULLABLE=NO in layer creation + # options instead) + flags = drv.GetMetadataItem(gdal.DMD_ALTER_GEOM_FIELD_DEFN_FLAGS) + if flags is not None and 'nullable' in flags.lower().split(' '): + geom_field = ogr.GeomFieldDefn(None, geom_type) + geom_field.SetNullable(False) + lyr.AlterGeomFieldDefn(0, geom_field, ogr.ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG) + # sync before calling StartTransaction() so we're not trying to rollback changes # on a non-existing table lyr.SyncToDisk() diff --git a/webmap-import b/webmap-import index b4552d5..a68d60e 100755 --- a/webmap-import +++ b/webmap-import @@ -256,6 +256,15 @@ def processOutputLayer(ds : gdal.Dataset, raise RuntimeError(f'Output layer "{layername}" has no working ' 'CreateFeature() method') + geom_field_idx = 0 + geom_field = lyr.GetLayerDefn().GetGeomFieldDefn(geom_field_idx) + logging.debug('Geometry column #%d: name="%s\", type="%s", srs=%s, nullable=%s', + geom_field_idx, geom_field.GetName(), + ogr.GeometryTypeToName(geom_field.GetType()), + '-' if geom_field.GetSpatialRef() is None + else '"' + geom_field.GetSpatialRef().GetName() + '"', + bool(geom_field.IsNullable())) + sources = layerdef['sources'] if not (lyrcache is None or force or areSourceFilesNewer(layername, sources=sources, -- cgit v1.2.3