ENH: write GeoDataFrame with mixed geometry types (#870)

This commit is contained in:
Géraud
2019-04-17 18:01:50 +02:00
committed by Joris Van den Bossche
parent ab5945bee6
commit 0fbc7ef3f7
5 changed files with 758 additions and 42 deletions
+1
View File
@@ -9,6 +9,7 @@ __pycache__/
# Distribution / packaging
.Python
env/
venv*/
build/
dist/
eggs/
+54 -15
View File
@@ -149,27 +149,66 @@ def infer_schema(df):
if df.empty:
raise ValueError("Cannot write empty DataFrame to file.")
geom_type = _common_geom_type(df)
# Since https://github.com/Toblerity/Fiona/issues/446 resolution,
# Fiona allows a list of geometry types
geom_types = _geometry_types(df)
schema = {'geometry': geom_type, 'properties': properties}
schema = {'geometry': geom_types, 'properties': properties}
return schema
def _common_geom_type(df):
# Need to check geom_types before we write to file...
# Some (most?) providers expect a single geometry type:
# Point, LineString, or Polygon
geom_types = df.geometry.geom_type.unique()
def _geometry_types(df):
"""
Determine the geometry types in the GeoDataFrame for the schema.
"""
if _FIONA18:
# Starting from Fiona 1.8, schema submitted to fiona to write a gdf
# can have mixed geometries:
# - 3D and 2D shapes can coexist in inferred schema
# - Shape and MultiShape types can (and must) coexist in inferred
# schema
geom_types_2D = df[~df.geometry.has_z].geometry.geom_type.unique()
geom_types_2D = [gtype for gtype in geom_types_2D if gtype is not None]
geom_types_3D = df[df.geometry.has_z].geometry.geom_type.unique()
geom_types_3D = ["3D " + gtype for gtype in geom_types_3D
if gtype is not None]
geom_types = geom_types_3D + geom_types_2D
from os.path import commonprefix
# use reversed geom types and commonprefix to find the common suffix,
# then reverse the result to get back to a geom type
geom_type = commonprefix([g[::-1] for g in geom_types if g])[::-1]
if not geom_type:
else:
# Before Fiona 1.8, schema submitted to write a gdf should have
# one single geometry type whenever possible:
# - 3D and 2D shapes cannot coexist in inferred schema
# - Shape and MultiShape can not coexist in inferred schema
geom_types = _geometry_types_back_compat(df)
if len(geom_types) == 0:
# Default geometry type supported by Fiona
# (Since https://github.com/Toblerity/Fiona/issues/446 resolution)
return 'Unknown'
if df.geometry.has_z.any():
geom_type = "3D " + geom_type
if len(geom_types) == 1:
geom_types = geom_types[0]
return geom_type
return geom_types
def _geometry_types_back_compat(df):
"""
for backward compatibility with Fiona<1.8 only
"""
unique_geom_types = df.geometry.geom_type.unique()
unique_geom_types = [
gtype for gtype in unique_geom_types if gtype is not None]
# merge single and Multi types (eg Polygon and MultiPolygon)
unique_geom_types = [
gtype for gtype in unique_geom_types
if not gtype.startswith('Multi') or gtype[5:] not in unique_geom_types]
if df.geometry.has_z.any():
# declare all geometries as 3D geometries
unique_geom_types = ["3D " + type for type in unique_geom_types]
# by default, all geometries are 2D geometries
return unique_geom_types
+16 -27
View File
@@ -5,8 +5,9 @@ from distutils.version import LooseVersion
import os
import numpy as np
from shapely.geometry import Point, Polygon, box
import fiona
from shapely.geometry import Point, Polygon, box
import geopandas
from geopandas import GeoDataFrame, read_file
@@ -96,26 +97,32 @@ def test_to_file_bool(tmpdir, driver, ext):
assert_geodataframe_equal(result, df, check_column_type=False)
def test_to_file_with_point_z(tmpdir):
@pytest.mark.parametrize(
'ext, driver', [('shp', 'ESRI Shapefile'), ('geojson', 'GeoJSON')])
def test_to_file_with_point_z(tmpdir, ext, driver):
"""Test that 3D geometries are retained in writes (GH #612)."""
tempfilename = os.path.join(str(tmpdir), 'test_3Dpoint.shp')
tempfilename = os.path.join(str(tmpdir), 'test_3Dpoint.' + ext)
point3d = Point(0, 0, 500)
point2d = Point(1, 1)
df = GeoDataFrame({'a': [1, 2]}, geometry=[point3d, point2d], crs={})
df.to_file(tempfilename)
df = GeoDataFrame({'a': [1, 2]}, geometry=[point3d, point2d],
crs={'init': 'epsg:4326'})
df.to_file(tempfilename, driver=driver)
df_read = GeoDataFrame.from_file(tempfilename)
assert_geoseries_equal(df.geometry, df_read.geometry)
def test_to_file_with_poly_z(tmpdir):
@pytest.mark.parametrize(
'ext, driver', [('shp', 'ESRI Shapefile'), ('geojson', 'GeoJSON')])
def test_to_file_with_poly_z(tmpdir, ext, driver):
"""Test that 3D geometries are retained in writes (GH #612)."""
tempfilename = os.path.join(str(tmpdir), 'test_3Dpoly.shp')
tempfilename = os.path.join(str(tmpdir), 'test_3Dpoly.' + ext)
poly3d = Polygon([[0, 0, 5], [0, 1, 5], [1, 1, 5], [1, 0, 5]])
poly2d = Polygon([[0, 0], [0, 1], [1, 1], [1, 0]])
df = GeoDataFrame({'a': [1, 2]}, geometry=[poly3d, poly2d], crs={})
df.to_file(tempfilename)
df = GeoDataFrame({'a': [1, 2]}, geometry=[poly3d, poly2d],
crs={'init': 'epsg:4326'})
df.to_file(tempfilename, driver=driver)
df_read = GeoDataFrame.from_file(tempfilename)
assert_geoseries_equal(df.geometry, df_read.geometry)
@@ -132,24 +139,6 @@ def test_to_file_types(tmpdir, df_points):
df.to_file(tempfilename)
def test_to_file_mixed_types(tmpdir):
""" Test that mixed geometry types raise error when writing to file """
tempfilename = os.path.join(str(tmpdir), 'test.shp')
s = GeoDataFrame({'geometry': [Point(0, 0),
Polygon([(0, 0), (1, 0), (1, 1)])]})
# Exception type is different for different `fiona` versions
with pytest.raises((ValueError, RuntimeError)):
s.to_file(tempfilename)
def test_to_file_geojson_mixed_types(tmpdir):
""" Test that mixed geometry types can be saved as GeoJSON (GH #827) """
tempfilename = os.path.join(str(tmpdir), 'test.geojson')
s = GeoDataFrame({'geometry': [Point(0, 0),
Polygon([(0, 0), (1, 0), (1, 1)])]})
s.to_file(tempfilename, driver='GeoJSON')
def test_to_file_empty(tmpdir):
input_empty_df = GeoDataFrame()
tempfilename = os.path.join(str(tmpdir), 'test.shp')
@@ -0,0 +1,337 @@
import os
import shutil
import sys
import tempfile
from enum import Enum
from shapely.geometry import Point, Polygon, MultiPolygon, MultiPoint, \
LineString, MultiLineString
import geopandas
from geopandas import GeoDataFrame
from geopandas.io.file import _FIONA18
import pytest
from geopandas.testing import assert_geodataframe_equal
# Credit: Polygons below come from Montreal city Open Data portal
# http://donnees.ville.montreal.qc.ca/dataset/unites-evaluation-fonciere
city_hall_boundaries = Polygon((
(-73.5541107525234, 45.5091983609661),
(-73.5546126200639, 45.5086813829106),
(-73.5540185061397, 45.5084409343852),
(-73.5539986525799, 45.5084323044531),
(-73.5535801792994, 45.5089539203786),
(-73.5541107525234, 45.5091983609661)
))
vauquelin_place = Polygon((
(-73.5542465586147, 45.5081555487952),
(-73.5540185061397, 45.5084409343852),
(-73.5546126200639, 45.5086813829106),
(-73.5548825850032, 45.5084033554357),
(-73.5542465586147, 45.5081555487952)
))
city_hall_walls = [
LineString((
(-73.5541107525234, 45.5091983609661),
(-73.5546126200639, 45.5086813829106),
(-73.5540185061397, 45.5084409343852)
)),
LineString((
(-73.5539986525799, 45.5084323044531),
(-73.5535801792994, 45.5089539203786),
(-73.5541107525234, 45.5091983609661)
))
]
city_hall_entrance = Point(-73.553785, 45.508722)
city_hall_balcony = Point(-73.554138, 45.509080)
city_hall_council_chamber = Point(-73.554246, 45.508931)
point_3D = Point(-73.553785, 45.508722, 300)
# *****************************************
# TEST TOOLING
class _Fiona(Enum):
below_1_8 = 'fiona_below_1_8'
above_1_8 = 'fiona_above_1_8'
class _ExpectedError:
def __init__(self, error_type, error_message_match):
self.type = error_type
self.match = error_message_match
class _ExpectedErrorBuilder:
def __init__(self, composite_key):
self.composite_key = composite_key
def to_raise(self, error_type, error_match):
_expected_exceptions[self.composite_key] = _ExpectedError(error_type,
error_match)
def _expect_writing(gdf, ogr_driver, fiona_version):
return _ExpectedErrorBuilder(
_composite_key(gdf, ogr_driver, fiona_version)
)
def _composite_key(gdf, ogr_driver, fiona_version):
return frozenset([id(gdf), ogr_driver, fiona_version.value])
def _expected_error_on(gdf, ogr_driver, is_fiona_above_1_8):
if is_fiona_above_1_8:
composite_key = _composite_key(gdf, ogr_driver, _Fiona.above_1_8)
else:
composite_key = _composite_key(gdf, ogr_driver, _Fiona.below_1_8)
return _expected_exceptions.get(composite_key, None)
# *****************************************
# TEST CASES
_geodataframes_to_write = []
_expected_exceptions = {}
# ------------------
# gdf with Points
gdf = GeoDataFrame(
{'a': [1, 2]},
crs={'init': 'epsg:4326'},
geometry=[city_hall_entrance, city_hall_balcony]
)
_geodataframes_to_write.append(gdf)
# ------------------
# gdf with MultiPoints
gdf = GeoDataFrame(
{'a': [1, 2]},
crs={'init': 'epsg:4326'},
geometry=[
MultiPoint([
city_hall_balcony,
city_hall_council_chamber]),
MultiPoint([
city_hall_entrance,
city_hall_balcony,
city_hall_council_chamber]
)]
)
_geodataframes_to_write.append(gdf)
# ------------------
# gdf with Points and MultiPoints
gdf = GeoDataFrame(
{'a': [1, 2]},
crs={'init': 'epsg:4326'},
geometry=[
MultiPoint([city_hall_entrance, city_hall_balcony]),
city_hall_balcony
]
)
_geodataframes_to_write.append(gdf)
# 'ESRI Shapefile' driver supports writing LineString/MultiLinestring and
# Polygon/MultiPolygon but does not mention Point/MultiPoint
# see https://www.gdal.org/drv_shapefile.html
for driver in ('ESRI Shapefile', 'GPKG'):
_expect_writing(gdf, driver, _Fiona.below_1_8).to_raise(
ValueError,
"Record's geometry type does not match collection schema's geometry "
"type: 'MultiPoint' != 'Point'"
)
_expect_writing(gdf, 'ESRI Shapefile', _Fiona.above_1_8).to_raise(
RuntimeError,
"Failed to write record"
)
# ------------------
# gdf with LineStrings
gdf = GeoDataFrame(
{'a': [1, 2]},
crs={'init': 'epsg:4326'},
geometry=city_hall_walls
)
_geodataframes_to_write.append(gdf)
# ------------------
# gdf with MultiLineStrings
gdf = GeoDataFrame(
{'a': [1, 2]},
crs={'init': 'epsg:4326'},
geometry=[
MultiLineString(city_hall_walls),
MultiLineString(city_hall_walls)
]
)
_geodataframes_to_write.append(gdf)
# ------------------
# gdf with LineStrings and MultiLineStrings
gdf = GeoDataFrame(
{'a': [1, 2]},
crs={'init': 'epsg:4326'},
geometry=[MultiLineString(city_hall_walls), city_hall_walls[0]]
)
_geodataframes_to_write.append(gdf)
_expect_writing(gdf, 'GPKG', _Fiona.below_1_8).to_raise(
ValueError,
"Record's geometry type does not match collection schema's geometry "
"type: 'MultiLineString' != 'LineString'"
)
# ------------------
# gdf with Polygons
gdf = GeoDataFrame(
{'a': [1, 2]},
crs={'init': 'epsg:4326'},
geometry=[city_hall_boundaries, vauquelin_place]
)
_geodataframes_to_write.append(gdf)
# ------------------
# gdf with MultiPolygon
gdf = GeoDataFrame(
{'a': [1]},
crs={'init': 'epsg:4326'},
geometry=[MultiPolygon((city_hall_boundaries, vauquelin_place))]
)
_geodataframes_to_write.append(gdf)
# ------------------
# gdf with Polygon and MultiPolygon
gdf = GeoDataFrame(
{'a': [1, 2]},
crs={'init': 'epsg:4326'},
geometry=[
MultiPolygon((city_hall_boundaries, vauquelin_place)),
city_hall_boundaries
]
)
_geodataframes_to_write.append(gdf)
_expect_writing(gdf, 'GPKG', _Fiona.below_1_8).to_raise(
ValueError,
"Record's geometry type does not match collection schema's geometry "
"type: 'MultiPolygon' != 'Polygon'"
)
# ------------------
# gdf with null geometry and Point
gdf = GeoDataFrame(
{'a': [1, 2]},
crs={'init': 'epsg:4326'},
geometry=[None, city_hall_entrance]
)
_geodataframes_to_write.append(gdf)
# ------------------
# gdf with null geometry and 3D Point
gdf = GeoDataFrame(
{'a': [1, 2]},
crs={'init': 'epsg:4326'},
geometry=[None, point_3D]
)
_geodataframes_to_write.append(gdf)
# ------------------
# gdf with null geometries only
gdf = GeoDataFrame(
{'a': [1, 2]},
crs={'init': 'epsg:4326'},
geometry=[None, None]
)
_geodataframes_to_write.append(gdf)
# ------------------
# gdf with all shape types mixed together
gdf = GeoDataFrame(
{'a': [1, 2, 3, 4, 5, 6]},
crs={'init': 'epsg:4326'},
geometry=[
MultiPolygon((city_hall_boundaries, vauquelin_place)),
city_hall_entrance,
MultiLineString(city_hall_walls),
city_hall_walls[0],
MultiPoint([city_hall_entrance, city_hall_balcony]),
city_hall_balcony
]
)
_geodataframes_to_write.append(gdf)
# Not supported by 'ESRI Shapefile' driver
for driver in ('ESRI Shapefile', 'GPKG'):
_expect_writing(gdf, driver, _Fiona.below_1_8).to_raise(
AttributeError,
"'list' object has no attribute 'lstrip'"
)
_expect_writing(gdf, 'ESRI Shapefile', _Fiona.above_1_8).to_raise(
RuntimeError,
"Failed to write record"
)
# ------------------
# gdf with all 2D shape types and 3D Point mixed together
gdf = GeoDataFrame(
{'a': [1, 2, 3, 4, 5, 6, 7]},
crs={'init': 'epsg:4326'},
geometry=[
MultiPolygon((city_hall_boundaries, vauquelin_place)),
city_hall_entrance,
MultiLineString(city_hall_walls),
city_hall_walls[0],
MultiPoint([city_hall_entrance, city_hall_balcony]),
city_hall_balcony,
point_3D
]
)
_geodataframes_to_write.append(gdf)
# Not supported by 'ESRI Shapefile' driver
for driver in ('ESRI Shapefile', 'GPKG'):
_expect_writing(gdf, driver, _Fiona.below_1_8).to_raise(
AttributeError,
"'list' object has no attribute 'lstrip'"
)
_expect_writing(gdf, 'ESRI Shapefile', _Fiona.above_1_8).to_raise(
RuntimeError,
"Failed to write record"
)
@pytest.fixture(params=_geodataframes_to_write)
def geodataframe(request):
return request.param
@pytest.fixture(params=[
'GeoJSON', 'ESRI Shapefile',
pytest.param('GPKG', marks=pytest.mark.skipif(
(sys.version_info < (3, 0)) and sys.platform.startswith('win'),
reason="GPKG tests failing on AppVeyor for Python 2.7"))
])
def ogr_driver(request):
return request.param
def test_to_file_roundtrip(tmpdir, geodataframe, ogr_driver):
output_file = os.path.join(str(tmpdir), 'output_file')
expected_error = _expected_error_on(geodataframe, ogr_driver, _FIONA18)
if expected_error:
with pytest.raises(expected_error.type, match=expected_error.match):
geodataframe.to_file(output_file, driver=ogr_driver)
else:
geodataframe.to_file(output_file, driver=ogr_driver)
reloaded = geopandas.read_file(output_file)
check_column_type = 'equiv'
if sys.version_info[0] < 3:
# do not check column types in python 2 (mixed string/unicode)
check_column_type = False
assert_geodataframe_equal(geodataframe, reloaded,
check_column_type=check_column_type)
+350
View File
@@ -0,0 +1,350 @@
from collections import OrderedDict
from shapely.geometry import Point, Polygon, MultiPolygon, MultiPoint, \
LineString, MultiLineString
from geopandas import GeoDataFrame
from geopandas.io.file import infer_schema, _FIONA18
# Credit: Polygons below come from Montreal city Open Data portal
# http://donnees.ville.montreal.qc.ca/dataset/unites-evaluation-fonciere
city_hall_boundaries = Polygon((
(-73.5541107525234, 45.5091983609661),
(-73.5546126200639, 45.5086813829106),
(-73.5540185061397, 45.5084409343852),
(-73.5539986525799, 45.5084323044531),
(-73.5535801792994, 45.5089539203786),
(-73.5541107525234, 45.5091983609661)
))
vauquelin_place = Polygon((
(-73.5542465586147, 45.5081555487952),
(-73.5540185061397, 45.5084409343852),
(-73.5546126200639, 45.5086813829106),
(-73.5548825850032, 45.5084033554357),
(-73.5542465586147, 45.5081555487952)
))
city_hall_walls = [
LineString((
(-73.5541107525234, 45.5091983609661),
(-73.5546126200639, 45.5086813829106),
(-73.5540185061397, 45.5084409343852)
)),
LineString((
(-73.5539986525799, 45.5084323044531),
(-73.5535801792994, 45.5089539203786),
(-73.5541107525234, 45.5091983609661)
))
]
city_hall_entrance = Point(-73.553785, 45.508722)
city_hall_balcony = Point(-73.554138, 45.509080)
city_hall_council_chamber = Point(-73.554246, 45.508931)
point_3D = Point(-73.553785, 45.508722, 300)
linestring_3D = LineString((
(-73.5541107525234, 45.5091983609661, 300),
(-73.5546126200639, 45.5086813829106, 300),
(-73.5540185061397, 45.5084409343852, 300)
))
polygon_3D = Polygon((
(-73.5541107525234, 45.5091983609661, 300),
(-73.5535801792994, 45.5089539203786, 300),
(-73.5541107525234, 45.5091983609661, 300)
))
def test_infer_schema_only_points():
df = GeoDataFrame(
geometry=[city_hall_entrance, city_hall_balcony]
)
assert infer_schema(df) == {
'geometry': 'Point',
'properties': OrderedDict()
}
def test_infer_schema_points_and_multipoints():
df = GeoDataFrame(
geometry=[
MultiPoint([city_hall_entrance, city_hall_balcony]),
city_hall_balcony
]
)
if _FIONA18:
assert infer_schema(df) == {
'geometry': ['MultiPoint', 'Point'],
'properties': OrderedDict()
}
else:
assert infer_schema(df) == {
'geometry': 'Point',
'properties': OrderedDict()
}
def test_infer_schema_only_multipoints():
df = GeoDataFrame(
geometry=[MultiPoint([
city_hall_entrance,
city_hall_balcony,
city_hall_council_chamber
])]
)
assert infer_schema(df) == {
'geometry': 'MultiPoint',
'properties': OrderedDict()
}
def test_infer_schema_only_linestrings():
df = GeoDataFrame(geometry=city_hall_walls)
assert infer_schema(df) == {
'geometry': 'LineString',
'properties': OrderedDict()
}
def test_infer_schema_linestrings_and_multilinestrings():
df = GeoDataFrame(
geometry=[
MultiLineString(city_hall_walls),
city_hall_walls[0]
]
)
if _FIONA18:
assert infer_schema(df) == {
'geometry': ['MultiLineString', 'LineString'],
'properties': OrderedDict()
}
else:
assert infer_schema(df) == {
'geometry': 'LineString',
'properties': OrderedDict()
}
def test_infer_schema_only_multilinestrings():
df = GeoDataFrame(geometry=[MultiLineString(city_hall_walls)])
assert infer_schema(df) == {
'geometry': 'MultiLineString',
'properties': OrderedDict()
}
def test_infer_schema_only_polygons():
df = GeoDataFrame(
geometry=[city_hall_boundaries, vauquelin_place]
)
assert infer_schema(df) == {
'geometry': 'Polygon',
'properties': OrderedDict()
}
def test_infer_schema_polygons_and_multipolygons():
df = GeoDataFrame(
geometry=[
MultiPolygon((city_hall_boundaries, vauquelin_place)),
city_hall_boundaries
]
)
if _FIONA18:
assert infer_schema(df) == {
'geometry': ['MultiPolygon', 'Polygon'],
'properties': OrderedDict()
}
else:
assert infer_schema(df) == {
'geometry': 'Polygon',
'properties': OrderedDict()
}
def test_infer_schema_only_multipolygons():
df = GeoDataFrame(
geometry=[
MultiPolygon((city_hall_boundaries, vauquelin_place))
]
)
assert infer_schema(df) == {
'geometry': 'MultiPolygon',
'properties': OrderedDict()
}
def test_infer_schema_multiple_shape_types():
df = GeoDataFrame(
geometry=[
MultiPolygon((city_hall_boundaries, vauquelin_place)),
city_hall_boundaries,
MultiLineString(city_hall_walls),
city_hall_walls[0],
MultiPoint([city_hall_entrance, city_hall_balcony]),
city_hall_balcony
]
)
if _FIONA18:
assert infer_schema(df) == {
'geometry': [
'MultiPolygon', 'Polygon',
'MultiLineString', 'LineString',
'MultiPoint', 'Point'
],
'properties': OrderedDict()
}
else:
assert infer_schema(df) == {
'geometry': [
'Polygon',
'LineString',
'Point'
],
'properties': OrderedDict()
}
def test_infer_schema_mixed_3D_shape_type():
df = GeoDataFrame(
geometry=[
MultiPolygon((city_hall_boundaries, vauquelin_place)),
city_hall_boundaries,
MultiLineString(city_hall_walls),
city_hall_walls[0],
MultiPoint([city_hall_entrance, city_hall_balcony]),
city_hall_balcony,
point_3D
]
)
if _FIONA18:
assert infer_schema(df) == {
'geometry': [
'3D Point',
'MultiPolygon', 'Polygon',
'MultiLineString', 'LineString',
'MultiPoint', 'Point'
],
'properties': OrderedDict()
}
else:
assert infer_schema(df) == {
'geometry': ['3D Polygon', '3D LineString', '3D Point'],
'properties': OrderedDict()
}
def test_infer_schema_mixed_3D_Point():
df = GeoDataFrame(geometry=[city_hall_balcony, point_3D])
if _FIONA18:
assert infer_schema(df) == {
'geometry': ['3D Point', 'Point'],
'properties': OrderedDict()
}
else:
assert infer_schema(df) == {
'geometry': '3D Point',
'properties': OrderedDict()
}
def test_infer_schema_only_3D_Points():
df = GeoDataFrame(geometry=[point_3D, point_3D])
assert infer_schema(df) == {
'geometry': '3D Point',
'properties': OrderedDict()
}
def test_infer_schema_mixed_3D_linestring():
df = GeoDataFrame(
geometry=[city_hall_walls[0], linestring_3D]
)
if _FIONA18:
assert infer_schema(df) == {
'geometry': ['3D LineString', 'LineString'],
'properties': OrderedDict()
}
else:
assert infer_schema(df) == {
'geometry': '3D LineString',
'properties': OrderedDict()
}
def test_infer_schema_only_3D_linestrings():
df = GeoDataFrame(geometry=[linestring_3D, linestring_3D])
assert infer_schema(df) == {
'geometry': '3D LineString',
'properties': OrderedDict()
}
def test_infer_schema_mixed_3D_Polygon():
df = GeoDataFrame(geometry=[city_hall_boundaries, polygon_3D])
if _FIONA18:
assert infer_schema(df) == {
'geometry': ['3D Polygon', 'Polygon'],
'properties': OrderedDict()
}
else:
assert infer_schema(df) == {
'geometry': '3D Polygon',
'properties': OrderedDict()
}
def test_infer_schema_only_3D_Polygons():
df = GeoDataFrame(geometry=[polygon_3D, polygon_3D])
assert infer_schema(df) == {
'geometry': '3D Polygon',
'properties': OrderedDict()
}
def test_infer_schema_null_geometry_and_2D_point():
df = GeoDataFrame(geometry=[None, city_hall_entrance])
# None geometry type is then omitted
assert infer_schema(df) == {
'geometry': 'Point',
'properties': OrderedDict()
}
def test_infer_schema_null_geometry_and_3D_point():
df = GeoDataFrame(geometry=[None, point_3D])
# None geometry type is then omitted
assert infer_schema(df) == {
'geometry': '3D Point',
'properties': OrderedDict()
}
def test_infer_schema_null_geometry_all():
df = GeoDataFrame(geometry=[None, None])
# None geometry type in then replaced by 'Unknown'
# (default geometry type supported by Fiona)
assert infer_schema(df) == {
'geometry': 'Unknown',
'properties': OrderedDict()
}