From 20b19fea7110eae0702b08e0944b5c7049f6ddb5 Mon Sep 17 00:00:00 2001 From: Adnan Hemani Date: Tue, 19 Dec 2023 02:23:03 -0600 Subject: [PATCH] Add additional test coverage --- datascience/maps.py | 31 ++--- tests/test_maps.py | 218 +++++++++++++-------------------- tests/us-states-zipped.json.gz | Bin 0 -> 29062 bytes 3 files changed, 99 insertions(+), 150 deletions(-) create mode 100644 tests/us-states-zipped.json.gz diff --git a/datascience/maps.py b/datascience/maps.py index e47f931e..fcd6fc48 100644 --- a/datascience/maps.py +++ b/datascience/maps.py @@ -217,19 +217,16 @@ def _autozoom(self): # remove the following with new Folium release # rough approximation, assuming max_zoom is 18 import math - try: - lat_diff = bounds['max_lat'] - bounds['min_lat'] - lon_diff = bounds['max_lon'] - bounds['min_lon'] - area, max_area = lat_diff*lon_diff, 180*360 - if area: - factor = 1 + max(0, 1 - self._width/1000)/2 + max(0, 1-area**0.5)/2 - zoom = math.log(area/max_area)/-factor - else: - zoom = self._default_zoom - zoom = max(1, min(18, round(zoom))) - attrs['zoom_start'] = zoom - except ValueError as e: - raise Exception('Check that your locations are lat-lon pairs', e) + lat_diff = bounds['max_lat'] - bounds['min_lat'] + lon_diff = bounds['max_lon'] - bounds['min_lon'] + area, max_area = lat_diff*lon_diff, 180*360 + if area: + factor = 1 + max(0, 1 - self._width/1000)/2 + max(0, 1-area**0.5)/2 + zoom = math.log(area/max_area)/-factor + else: + zoom = self._default_zoom + zoom = max(1, min(18, round(zoom))) + attrs['zoom_start'] = zoom return attrs @@ -856,7 +853,7 @@ def polygons(self): """ if self.type == 'Polygon': polygons = [self._geojson['geometry']['coordinates']] - elif self.type == 'MultiPolygon': + else: # self.type == "MultiPolygon" polygons = self._geojson['geometry']['coordinates'] return [ [ [_lat_lons_from_geojson(s) for s in ring ] for @@ -980,11 +977,7 @@ def get_coordinates(table, replace_columns=False, remove_nans=False): table = table.with_columns("lat", lat, "lon", lon) table = table.drop(index_name) if replace_columns: - for label in ["county", "city", "zip code", "state"]: - try: - table = table.drop(label) - except KeyError: - pass + table = table.drop(["county", "city", "zip code", "state"]) if remove_nans: table = table.where("lat", are.below(float("inf"))) # NaNs are not considered to be smaller than infinity return table diff --git a/tests/test_maps.py b/tests/test_maps.py index b62b7c49..de9b8e63 100644 --- a/tests/test_maps.py +++ b/tests/test_maps.py @@ -93,9 +93,23 @@ def test_map_copy(states): # and copy is returning a true copy assert map1 is not map2 -########## +def test_map_overlay_undefined_feature(): + marker1 = ds.Marker(51.514, -0.132) + marker2 = ds.Marker(52.514, -0.132) + marker1_map = ds.Map(marker1) + unchanged_map = marker1_map.overlay(marker2) + assert len(unchanged_map._features), 1 + assert len(unchanged_map._folium_map._children.keys()), 1 + marker2_map = ds.Map(marker2) + changed_map = marker1_map.overlay(marker2_map) + assert len(changed_map._features), 1 + assert len(unchanged_map._folium_map._children.keys()), 2 + + + +############# # ds.Marker # -########## +############# def test_marker_html(): @@ -138,41 +152,6 @@ def test_marker_map_table(): assert markers[1].lat_lon[1], -2 assert markers[2].lat_lon[1], -3 -def test_circle_map_table(): - lat_init, lon_init, area_init, color_scale_init = 51, -8, 10, 10 - lats, lons, areas, color_scales = [], [], [], [] - for i in range(8): - lats.append(lat_init+i) - lons.append(lon_init+i) - areas.append((area_init + 10*i)**2*math.pi) - color_scales.append(color_scale_init + 10*i) - color_scales[-1] = 1000000 - labels = ['A', 'B', 'C'] - t = ds.Table().with_columns('A', lats, 'B', lons, 'areas', areas, 'color_scale', color_scales) - markers = ds.Circle.map_table(t, include_color_scale_outliers=False) - - for i in range(8): - assert markers[i]._attrs['radius'], 10 + 10*i - - # Call the map_table method and check if percentiles and outliers are calculated correctly - assert markers._attrs['colorbar_scale'], [10.0, 23.125, 36.25, 49.375, 62.5, 75.625, 88.75, 101.875, 115.0] - - assert [markers[i]._attrs['color'] for i in range(8)], ['#340597', '#340597', '#7008a5', '#a32494', '#cf5073', '#cf5073', '#ee7c4c', '#f4e82d'] - - -def test_circle_html(): - """ Tests that a Circle can be rendered. """ - ds.Circle(51.514, -0.132).show() - - -def test_circle_map(): - """ Tests that Circle.map generates a map """ - lats = [51, 52, 53] - lons = [-1, -2, -3] - labels = ['A', 'B', 'C'] - ds.Circle.map(lats, lons).show() - ds.Circle.map(lats, lons, labels).show() - def test_marker_copy(): lat, lon = 51, 52 a = ds.Marker(lat, lon) @@ -201,6 +180,10 @@ def test_icon_args_icon_present(): marker = ds.Marker(0, 0, color='blue', marker_icon='info-sign', icon='custom-icon') assert marker._folium_kwargs['icon'].options['icon'], 'info-sign' +def test_user_tampered_marker_icon_attributes(): + marker = ds.Marker(0, 0, color='#ff0000') + del marker._attrs["marker_icon"] + assert marker._folium_kwargs['icon'].options['icon'], 'circle' def test_geojson(): # Create a Marker instance with known values @@ -245,49 +228,58 @@ def test_convert_point_no_name(): assert converted_marker.lat_lon, (54.32, 98.76) assert not converted_marker._attrs['popup'] -# def test_percentile_and_outlier_lines(): -# # Create a list of dictionaries to represent the table data -# data = [ -# {"latitudes": 1, "longitudes": 4, "color_scale": 10}, -# {"latitudes": 2, "longitudes": 5, "color_scale": 20}, -# {"latitudes": 3, "longitudes": 6, "color_scale": 30}, -# ] - -# # Call the map_table method and check if percentiles and outliers are calculated correctly -# markers = ds.Marker.map_table(data, include_color_scale_outliers=False) -# assert markers[0].colorbar_scale, [10, 20, 30] -# assert markers[0].outlier_min_bound, 10 -# assert markers[0].outlier_max_bound, 30 - -# def test_return_colors(): -# # Create a list of dictionaries to represent the table data -# data = [ -# {"latitudes": 1, "longitudes": 4, "color_scale": 10}, -# {"latitudes": 2, "longitudes": 5, "color_scale": 20}, -# {"latitudes": 3, "longitudes": 6, "color_scale": 30}, -# ] - -# # Call the map_table method -# markers = ds.Marker.map_table(data, include_color_scale_outliers=False) - -# # Call the interpolate_color method with a value that should use the last color -# last_color = markers[0].interpolate_color(["#340597", "#7008a5", "#a32494"], [10, 20], 25) -# assert last_color, "#a32494" ########## -# Region # +# Circle # ########## +def test_line_color_handling(): + # Create a Circle instance with line_color attribute + circle = ds.Circle(37.8, -122, line_color='red') + # Call the _folium_kwargs method to get the attributes + attrs = circle._folium_kwargs + # Check that 'line_color' attribute has been transferred to 'color' + assert attrs['color'], 'red' -def test_region_html(states): - states['CA'].show() +def test_circle_map_table(): + lat_init, lon_init, area_init, color_scale_init = 51, -8, 10, 10 + lats, lons, areas, color_scales = [], [], [], [] + for i in range(8): + lats.append(lat_init+i) + lons.append(lon_init+i) + areas.append((area_init + 10*i)**2*math.pi) + color_scales.append(color_scale_init + 10*i) + color_scales[-1] = 1000000 + labels = ['A', 'B', 'C'] + t = ds.Table().with_columns('A', lats, 'B', lons, 'areas', areas, 'color_scale', color_scales) + markers = ds.Circle.map_table(t, include_color_scale_outliers=False) + for i in range(8): + assert markers[i]._attrs['radius'], 10 + 10*i -def test_geojson(states): - """ Tests that geojson returns the original US States data """ - data = json.load(open('tests/us-states.json', 'r')) - geo = states.geojson() - assert data == geo, '{}\n{}'.format(data, geo) + # Call the map_table method and check if percentiles and outliers are calculated correctly + assert markers._attrs['colorbar_scale'], [10.0, 23.125, 36.25, 49.375, 62.5, 75.625, 88.75, 101.875, 115.0] + + assert [markers[i]._attrs['color'] for i in range(8)], ['#340597', '#340597', '#7008a5', '#a32494', '#cf5073', '#cf5073', '#ee7c4c', '#f4e82d'] + + +def test_circle_html(): + """ Tests that a Circle can be rendered. """ + ds.Circle(51.514, -0.132).show() + + +def test_circle_map(): + """ Tests that Circle.map generates a map """ + lats = [51, 52, 53] + lons = [-1, -2, -3] + labels = ['A', 'B', 'C'] + ds.Circle.map(lats, lons).show() + ds.Circle.map(lats, lons, labels).show() + +def test_user_tampered_circle_color_attributes(): + circle = ds.Circle(51.514, -0.132) + del circle._attrs["color"] + assert "color" not in circle._folium_kwargs ########## @@ -345,11 +337,15 @@ def test_color_with_ids(states): # GeoJSON # ########### -def test_read_geojson_with_dict(states): +def test_read_geojson_with_dict(): data = {'type': 'FeatureCollection', 'features': []} map_data = ds.Map.read_geojson(data) assert isinstance(map_data, ds.Map) +def test_read_geojson_with_gz_file(): + map_data = ds.Map.read_geojson('tests/us-states-zipped.json.gz') + assert isinstance(map_data, ds.Map) + def test_read_geojson_features_with_valid_data(): data = { 'type': 'FeatureCollection', @@ -423,46 +419,18 @@ def test_read_geojson_features_with_invalid_geometry_type(): assert features['1'] is None ########## -# Circle # +# Region # ########## -def test_line_color_handling(): - # Create a Circle instance with line_color attribute - circle = ds.Circle(37.8, -122, line_color='red') - # Call the _folium_kwargs method to get the attributes - attrs = circle._folium_kwargs - # Check that 'line_color' attribute has been transferred to 'color' - assert attrs['color'], 'red' -def test_region_type_property_polygon(): - # Create a GeoJSON object for a Polygon - geojson = { - "type": "Feature", - "geometry": { - "type": "Polygon", - "coordinates": [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]] - } - } - # Create a Region instance with the GeoJSON object - region = ds.Region(geojson) - # Assert that the type property returns "Polygon" - assert region.type, "Polygon" +def test_region_html(states): + states['CA'].show() -def test_region_type_property_multipolygon(): - # Create a GeoJSON object for a MultiPolygon - geojson = { - "type": "Feature", - "geometry": { - "type": "MultiPolygon", - "coordinates": [ - [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]], - [[[2, 2], [2, 3], [3, 3], [3, 2], [2, 2]]] - ] - } - } - # Create a Region instance with the GeoJSON object - region = ds.Region(geojson) - # Assert that the type property returns "MultiPolygon" - assert region.type, "MultiPolygon" + +def test_geojson(states): + """ Tests that geojson returns the original US States data """ + data = json.load(open('tests/us-states.json', 'r')) + geo = states.geojson() + assert data == geo, '{}\n{}'.format(data, geo) def test_polygon_type(): # Test if polygons property returns the correct structure for 'Polygon' type @@ -485,31 +453,15 @@ def test_polygon_type(): } region_polygon = ds.Region(geojson_polygon) region_multi_polygon = ds.Region(geojson_multi_polygon) + assert region_polygon.type, "Polygon" + assert region_multi_polygon.type, "MultiPolygon" + + # Test if polygons property returns the correct structure for 'Polygon' type polygons = region_polygon.polygons assert len(polygons), 1 assert len(polygons[0]), 1 # One polygon assert len(polygons[0][0]), 5 # Five points (closed ring) -def test_multi_polygon_type(): - geojson_polygon = { - "type": "Feature", - "geometry": { - "type": "Polygon", - "coordinates": [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]] - } - } - geojson_multi_polygon = { - "type": "Feature", - "geometry": { - "type": "MultiPolygon", - "coordinates": [ - [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]], - [[[2, 2], [3, 2], [3, 3], [2, 3], [2, 2]]] - ] - } - } - region_polygon = ds.Region(geojson_polygon) - region_multi_polygon = ds.Region(geojson_multi_polygon) # Test if polygons property returns the correct structure for 'MultiPolygon' type polygons = region_multi_polygon.polygons assert len(polygons), 2 # Two polygons @@ -559,6 +511,10 @@ def test_geojson_with_id(): assert updated_geojson["geometry"], geojson_data["geometry"] assert updated_geojson["properties"], geojson_data["properties"] +################### +# get_coordinates # +################### + def test_remove_nonexistent_county_column(): # Create a table without the "county" column data = {'city': ['City1', 'City2'], 'state': ['State1', 'State2']} diff --git a/tests/us-states-zipped.json.gz b/tests/us-states-zipped.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..289d6a9c0c44cf6c638cf7f83be8de2a5da6994b GIT binary patch literal 29062 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