diff --git a/autotest/ogr/data/flatgeobuf/test_ogr_flatgeobuf_singlepart_mls_new.fgb b/autotest/ogr/data/flatgeobuf/test_ogr_flatgeobuf_singlepart_mls_new.fgb new file mode 100644 index 000000000000..4a1482c1f1fc Binary files /dev/null and b/autotest/ogr/data/flatgeobuf/test_ogr_flatgeobuf_singlepart_mls_new.fgb differ diff --git a/autotest/ogr/ogr_flatgeobuf.py b/autotest/ogr/ogr_flatgeobuf.py index a37597cc044c..60bb994b3b16 100644 --- a/autotest/ogr/ogr_flatgeobuf.py +++ b/autotest/ogr/ogr_flatgeobuf.py @@ -41,12 +41,6 @@ pytestmark = pytest.mark.require_driver("FlatGeobuf") -############################################################################### -@pytest.fixture(autouse=True, scope="module") -def module_disable_exceptions(): - with gdaltest.disable_exceptions(): - yield - ############################################################################### @pytest.fixture(autouse=True, scope="module") @@ -648,11 +642,10 @@ def test_ogr_flatgeobuf_huge_number_of_columns(): lyr.CreateField(ogr.FieldDefn("col%d" % i, ogr.OFTInteger)) == ogr.OGRERR_NONE ), i - with gdal.quiet_errors(): - assert ( - lyr.CreateField(ogr.FieldDefn("col65536", ogr.OFTInteger)) - == ogr.OGRERR_FAILURE - ) + with pytest.raises( + Exception, match="Cannot create features with more than 65536 columns" + ): + lyr.CreateField(ogr.FieldDefn("col65536", ogr.OFTInteger)) f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (0 0)")) for i in range(65536): @@ -765,7 +758,8 @@ def test_ogr_flatgeobuf_editing(): assert lyr.TestCapability(ogr.OLCDeleteFeature) == 1 assert lyr.DeleteFeature(1) == 0 - assert lyr.DeleteFeature(1) == ogr.OGRERR_NON_EXISTING_FEATURE + with pytest.raises(Exception, match="Non existing feature"): + lyr.DeleteFeature(1) assert lyr.TestCapability(ogr.OLCReorderFields) == 1 # assert lyr.ReorderFields([0, 1]) == 0 assert lyr.DeleteField(1) == 0 @@ -797,8 +791,8 @@ def test_ogr_flatgeobuf_editing(): f = ogr.Feature(lyr.GetLayerDefn()) f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (1 1)")) - with gdal.quiet_errors(): - assert lyr.CreateFeature(f) != ogr.OGRERR_NONE + with pytest.raises(Exception, match="not supported on read-only layer"): + lyr.CreateFeature(f) ogr.GetDriverByName("FlatGeobuf").DeleteDataSource("/vsimem/test.fgb") assert not gdal.VSIStatL("/vsimem/test.fgb") @@ -871,8 +865,27 @@ def test_ogr_flatgeobuf_read_invalid_geometries(filename): with gdal.quiet_errors(): ds = gdal.OpenEx(filename) lyr = ds.GetLayer(0) - for f in lyr: - pass + with pytest.raises(Exception, match="Fatal error parsing feature"): + for f in lyr: + pass + + +############################################################################### +# Check that we can read multilinestrings with a single part, without the +# "ends" array (cf https://github.com/OSGeo/gdal/issues/10774) + + +@pytest.mark.parametrize( + "filename", + [ + "data/flatgeobuf/test_ogr_flatgeobuf_singlepart_mls_new.fgb", + ], +) +def test_ogr_flatgeobuf_read_singlepart_mls_new(filename): + with gdal.OpenEx(filename) as ds: + lyr = ds.GetLayer(0) + f = lyr.GetNextFeature() + ogrtest.check_feature_geometry(f, "MULTILINESTRING ((0 0,1 1))") ############################################################################### @@ -959,8 +972,8 @@ def test_ogr_flatgeobuf_coordinate_epoch_custom_wkt(): def test_ogr_flatgeobuf_invalid_output_filename(): ds = ogr.GetDriverByName("FlatGeobuf").CreateDataSource("/i_do/not_exist/my.fgb") - with gdal.quiet_errors(): - assert ds.CreateLayer("foo") is None + with pytest.raises(Exception, match="Failed to create"): + ds.CreateLayer("foo") ############################################################################### @@ -1208,12 +1221,11 @@ def test_ogr_flatgeobuf_issue_7401(): f.SetGeometry(ogr.CreateGeometryFromWkt("POINT (0 0)")) lyr.CreateFeature(f) f = ogr.Feature(lyr.GetLayerDefn()) - lyr.CreateFeature(f) + with pytest.raises( + Exception, match="NULL geometry not supported with spatial index" + ): + lyr.CreateFeature(f) ds = None - assert ( - gdal.GetLastErrorMsg() - == "ICreateFeature: NULL geometry not supported with spatial index" - ) ogr.GetDriverByName("FlatGeobuf").DeleteDataSource("/vsimem/test.fgb") assert not gdal.VSIStatL("/vsimem/test.fgb") diff --git a/ogr/ogrsf_frmts/flatgeobuf/geometryreader.cpp b/ogr/ogrsf_frmts/flatgeobuf/geometryreader.cpp index 45d18518adab..60c370144390 100644 --- a/ogr/ogrsf_frmts/flatgeobuf/geometryreader.cpp +++ b/ogr/ogrsf_frmts/flatgeobuf/geometryreader.cpp @@ -115,22 +115,31 @@ OGRMultiPoint *GeometryReader::readMultiPoint() OGRMultiLineString *GeometryReader::readMultiLineString() { - const auto pEnds = m_geometry->ends(); - if (pEnds == nullptr) - return CPLErrorInvalidPointer("MultiLineString ends data"); + const auto ends = m_geometry->ends(); auto mls = std::make_unique(); - m_offset = 0; - for (uint32_t i = 0; i < pEnds->size(); i++) + if (ends == nullptr || ends->size() < 2) { - const auto e = pEnds->Get(i); - if (e < m_offset) - return CPLErrorInvalidLength("MultiLineString"); - m_length = e - m_offset; - const auto ls = readSimpleCurve(); - if (ls == nullptr) + m_length = m_length / 2; + const auto part = readSimpleCurve(); + if (part == nullptr) return nullptr; - mls->addGeometryDirectly(ls); - m_offset = e; + mls->addGeometryDirectly(part); + } + else + { + m_offset = 0; + for (uint32_t i = 0; i < ends->size(); i++) + { + const auto e = ends->Get(i); + if (e < m_offset) + return CPLErrorInvalidLength("MultiLineString"); + m_length = e - m_offset; + const auto ls = readSimpleCurve(); + if (ls == nullptr) + return nullptr; + mls->addGeometryDirectly(ls); + m_offset = e; + } } return mls.release(); }