Repository: CartoDB/cartoframes
Pull request #1541 (2020-02-13 18:37:34)

Raise a suitable error when trying to visualise a GeoDataFrame with multiple geometries
Issue https://app.clubhouse.io/cartoteam/story/58603/raise-a-suitable-error-when-trying-to-visualise-a-geodataframe-with-multiple-geometries


Patch:
diff --git a/cartoframes/viz/source.py b/cartoframes/viz/source.py
index d152e33d..6272752d 100644
--- a/cartoframes/viz/source.py
+++ b/cartoframes/viz/source.py
@@ -3,11 +3,22 @@ from geopandas import GeoDataFrame
 
 from ..io.managers.context_manager import ContextManager
 from ..utils.geom_utils import set_geometry, has_geometry
-from ..utils.utils import encode_geodataframe, get_geodataframe_bounds, get_geodataframe_geom_type, \
-                          get_datetime_column_names
+from ..utils.utils import encode_geodataframe, get_geodataframe_bounds, \
+                          get_geodataframe_geom_type, get_datetime_column_names
 
 RFC_2822_DATETIME_FORMAT = "%a, %d %b %Y %T %z"
 
+VALID_GEOMETRY_TYPES = [
+    {'Point'},
+    {'MultiPoint'},
+    {'LineString'},
+    {'MultiLineString'},
+    {'LineString', 'MultiLineString'},
+    {'Polygon'},
+    {'MultiPolygon'},
+    {'Polygon', 'MultiPolygon'}
+]
+
 
 class SourceType:
     QUERY = 'Query'
@@ -75,6 +86,13 @@ class Source:
             else:
                 raise ValueError('No valid geometry found. Please provide an input source with ' +
                                  'a valid geometry or specify the "geom_col" param with a geometry column.')
+
+            # Checking the uniqueness of the geometry type
+            geometry_types = set(self.gdf.geom_type.unique())
+            if geometry_types not in VALID_GEOMETRY_TYPES:
+                raise ValueError('No valid geometry column types ({}), it has '.format(geometry_types) +
+                                 'to be one of the next type sets: {}.'.format(VALID_GEOMETRY_TYPES))
+
         else:
             raise ValueError('Wrong source input. Valid values are str and DataFrame.')
 


Test patch:
diff --git a/tests/unit/viz/test_source.py b/tests/unit/viz/test_source.py
index 42f8764d..febc7a12 100644
--- a/tests/unit/viz/test_source.py
+++ b/tests/unit/viz/test_source.py
@@ -7,6 +7,72 @@ from cartoframes.auth import Credentials
 from cartoframes.viz.source import Source
 from cartoframes.io.managers.context_manager import ContextManager
 
+POINT = {
+    "type": "Feature",
+    "geometry": {
+        "type": "Point",
+        "coordinates": [102.0, 0.5]
+    },
+    "properties": {
+        "prop0": "value0"
+    }
+}
+
+MULTIPOINT = {
+    "type": "Feature",
+    "geometry": {
+        "type": "MultiPoint",
+        "coordinates": [[102.0, 0.0], [103.0, 1.0]]
+    },
+    "properties": {
+        "prop0": "value0"
+    }
+}
+
+LINESTRING = {
+    "type": "Feature",
+    "geometry": {
+        "type": "LineString",
+        "coordinates": [[102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]]
+    },
+    "properties": {
+        "prop0": "value0"
+    }
+}
+
+MULTILINESTRING = {
+    "type": "Feature",
+    "geometry": {
+        "type": "MultiLineString",
+        "coordinates": [[[102.0, 0.0], [103.0, 1.0]], [[104.0, 0.0], [105.0, 1.0]]]
+    },
+    "properties": {
+        "prop0": "value0"
+    }
+}
+
+POLYGON = {
+    "type": "Feature",
+    "geometry": {
+        "type": "Polygon",
+        "coordinates": [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]]
+    },
+    "properties": {
+        "prop0": "value0"
+    }
+}
+
+MULTIPOLYGON = {
+    "type": "Feature",
+    "geometry": {
+        "type": "MultiPolygon",
+        "coordinates": [[[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0]]], [[[100.0, 1.0], [100.0, 0.0], [100.0, 0.0]]]]
+    },
+    "properties": {
+        "prop0": "value0"
+    }
+}
+
 
 def setup_mocks(mocker):
     mocker.patch.object(ContextManager, 'compute_query')
@@ -65,3 +131,43 @@ class TestSource(object):
 
         assert source.datetime_column_names == ['date_column']
         assert source.gdf.dtypes['date_column'] == np.object
+
+    @pytest.mark.parametrize('features', [
+        [POINT],
+        [MULTIPOINT],
+        [LINESTRING],
+        [MULTILINESTRING],
+        [LINESTRING, MULTILINESTRING],
+        [POLYGON],
+        [MULTIPOLYGON],
+        [POLYGON, MULTIPOLYGON]
+    ])
+    def test_different_geometry_types_source(self, features):
+        geojson = {
+            "type": "FeatureCollection",
+            "features": features
+        }
+        gdf = gpd.GeoDataFrame.from_features(geojson)
+        source = Source(gdf)
+
+        assert source.gdf.equals(gdf)
+
+    @pytest.mark.parametrize('features', [
+        [POINT, LINESTRING],
+        [POINT, POLYGON],
+        [LINESTRING, POLYGON],
+        [POINT, LINESTRING, POLYGON],
+        [MULTIPOINT, MULTILINESTRING, MULTIPOLYGON],
+        [POINT, MULTIPOINT]
+    ])
+    def test_different_geometry_types_source_fail(self, features):
+        geojson = {
+            "type": "FeatureCollection",
+            "features": features
+        }
+        gdf = gpd.GeoDataFrame.from_features(geojson)
+
+        with pytest.raises(ValueError) as e:
+            Source(gdf)
+
+        assert str(e.value).startswith('No valid geometry column types')