Skip to content

Commit

Permalink
Fix for #616 - import .shp as MULTI-LINETRING, -POLYGON
Browse files Browse the repository at this point in the history
Shapefiles don't distinguish between single- and multi- versions of
these geometry types - so we promote the column to the multi-type on
import in case there are any multi- instances in that column.
  • Loading branch information
olsen232 committed May 9, 2022
1 parent f4db98f commit 792fa4b
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 4 deletions.
51 changes: 48 additions & 3 deletions kart/tabular/ogr_import_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import click
from osgeo import gdal, ogr

from kart import crs_util
from kart import crs_util, ogr_util
from kart.exceptions import (
NO_IMPORT_SOURCE,
NO_TABLE,
Expand All @@ -17,7 +17,6 @@
NotYetImplemented,
)
from kart.geometry import ogr_to_gpkg_geom
from kart.ogr_util import get_type_value_adapter
from kart.output_util import dump_json_output
from kart.utils import chunk, ungenerator

Expand Down Expand Up @@ -342,7 +341,13 @@ def _get_primary_key_value(self, ogr_feature, name):
@property
@functools.lru_cache(maxsize=1)
def field_adapter_map(self):
return {col.name: get_type_value_adapter(col.data_type) for col in self.schema}
return {
col.name: self._get_type_value_adapter(col.name, col.data_type)
for col in self.schema
}

def _get_type_value_adapter(self, name, v2_type):
return ogr_util.get_type_value_adapter(v2_type)

@ungenerator(dict)
def _ogr_feature_to_kart_feature(self, ogr_feature):
Expand Down Expand Up @@ -579,8 +584,14 @@ def _get_v2_geometry_type(self, geom_fd):
# 'Line String' --> 'LineString' --> 'LINESTRING'
v2_type = ogr.GeometryTypeToName(ogr_geom_type).replace(" ", "").upper()

if self._should_promote_to_multi(name, v2_type):
v2_type = f"MULTI{v2_type}"

return f"{v2_type} {z}{m}".strip()

def _should_promote_to_multi(self, name, v2_geom_type):
return False

def _schema_from_db(self):
pk_col = self.pk_column_schema
pk_cols = [pk_col] if pk_col else []
Expand All @@ -593,6 +604,10 @@ def _schema_from_db(self):


class ESRIShapefileImportSource(OgrTableImportSource):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.force_promote_geom_columns = {}

def _should_import_as_numeric(self, ogr_type, ogr_width, ogr_precision):
if not super()._should_import_as_numeric(ogr_type, ogr_width, ogr_precision):
return False
Expand All @@ -614,6 +629,36 @@ def _should_import_as_numeric(self, ogr_type, ogr_width, ogr_precision):
return False
return True

def _should_promote_to_multi(self, name, v2_geom_type):
# Shapefiles don't distinguish between single- and multi- versions of these geometry types -
# so we promote the column to the multi-type on import in case there are any multi- instances in that column.
if v2_geom_type in ("LINESTRING", "POLYGON"):
forced_type = f"MULTI{v2_geom_type}"
self.force_promote_geom_columns[name] = forced_type
return True
return False

def _get_type_value_adapter(self, name, v2_type):
if name in self.force_promote_geom_columns:
forced_type = self.force_promote_geom_columns[name]
if forced_type == "MULTILINESTRING":
return adapt_ogr_force_multilinestring
elif forced_type == "MULTIPOLYGON":
return adapt_ogr_force_multipolygon
return super()._get_type_value_adapter(name, v2_type)


def adapt_ogr_force_multilinestring(value):
if value is None:
return value
return ogr_util.adapt_ogr_geometry(ogr.ForceToMultiLineString(value))


def adapt_ogr_force_multipolygon(value):
if value is None:
return value
return ogr_util.adapt_ogr_geometry(ogr.ForceToMultiPolygon(value))


def postgres_url_to_ogr_conn_str(url):
"""
Expand Down
4 changes: 3 additions & 1 deletion tests/test_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,8 @@ def test_import_from_non_gpkg(
g = f.GetGeometryRef()
if g:
g.AssignSpatialReference(src_layer.GetSpatialRef())
if table == H.POLYGONS.LAYER:
g = ogr.ForceToMultiPolygon(g)
expected_feature["geom"] = ogr_to_gpkg_geom(g)

assert normalise_feature(got_feature) == expected_feature
Expand Down Expand Up @@ -444,7 +446,7 @@ def test_shp_import_meta(
{
"name": "geom",
"dataType": "geometry",
"geometryType": "POLYGON",
"geometryType": "MULTIPOLYGON",
"geometryCRS": "EPSG:4167",
},
{"name": "date_adjus", "dataType": "date"},
Expand Down

0 comments on commit 792fa4b

Please sign in to comment.