def test_querylayer_time_category(self): """layer.QueryLayer time with categories""" querylayer = QueryLayer(self.query, time='timecol', color='colorcol') # category type querylayer.style_cols['colorcol'] = 'string' querylayer.style_cols['timecol'] = 'date' # if non-point geoms are present (or None), raise an error with self.assertRaises( ValueError, msg='cannot make torque map with non-point geometries'): querylayer._setup([BaseMap(), querylayer], 1) # pylint: disable=protected-access querylayer.geom_type = 'point' # normal behavior for point geometries querylayer._setup([BaseMap(), querylayer], 1) # pylint: disable=protected-access self.assertDictEqual( querylayer.scheme, dict(name='Antique', bin_method='', bins=[str(i) for i in range(1, 11)])) # expect category maps query self.assertRegexpMatches( querylayer.query, r'(?s)^SELECT\norig\.\*,\s__wrap\.' r'cf_value_colorcol\n.*GROUP\sBY.*orig\.' r'colorcol$') # cartocss should have cdb math mode self.assertRegexpMatches(querylayer.cartocss, r'.*CDB_Math_Mode\(cf_value_colorcol\).*')
def test_querylayer_time_errors(self): """layer.QueryLayer time option exceptions""" # time str column cannot be the_geom with self.assertRaises(ValueError, msg='time column cannot be `the_geom`'): QueryLayer(self.query, time='the_geom') # time dict must have a 'column' key with self.assertRaises(ValueError, msg='time dict must have a `column` key'): QueryLayer(self.query, time={'scheme': styling.armyRose(10)}) # pass an int as the time column with self.assertRaises(ValueError, msg='`time` key has to be a str or dict'): QueryLayer(self.query, time=7) with self.assertRaises(ValueError): querylayer = QueryLayer('select * from watermelon', time='seeds') querylayer.style_cols['seeds'] = 'string' querylayer.geom_type = 'point' querylayer._setup([BaseMap(), querylayer], 1) # pylint: disable=protected-access with self.assertRaises(ValueError): querylayer = QueryLayer('select * from watermelon', time='seeds') querylayer.style_cols['seeds'] = 'date' querylayer.geom_type = 'polygon' querylayer._setup([BaseMap(), querylayer], 1) # pylint: disable=protected-access
def test_querylayer_time_category(self): """layer.QueryLayer time with categories""" ql = QueryLayer(self.query, time='timecol', color='colorcol') # category type ql.style_cols['colorcol'] = 'string' ql.style_cols['timecol'] = 'date' # if non-point geoms are present (or None), raise an error with self.assertRaises( ValueError, msg='cannot make torque map with non-point geometries'): ql._setup([BaseMap(), ql], 1) ql.geom_type = 'point' # normal behavior for point geometries ql._setup([BaseMap(), ql], 1) self.assertDictEqual( ql.scheme, dict(name='Antique', bin_method='', bins=','.join(str(i) for i in range(1, 11)))) # expect category maps query self.assertRegexpMatches( ql.query, '^SELECT orig\.\*, ' '__wrap.cf_value_colorcol.* ' 'GROUP BY.*orig\.colorcol$') # cartocss should have cdb math mode self.assertRegexpMatches(ql.cartocss, '.*CDB_Math_Mode\(cf_value_colorcol\).*')
def test_basemap_invalid(self): """layer.Basemap exceptions on invalid source""" # Raise ValueError if invalid label is entered with self.assertRaises(ValueError): BaseMap(labels='watermelon') # Raise ValueError if custom URL is entered with self.assertRaises(ValueError): BaseMap(source='http://spinalmap.com/{z}/{x}/{y}.png') # Raise ValueError if non-supported style type is entered with self.assertRaises(ValueError): BaseMap(source='gulab_jamon')
def setUp(self): self.layers = [ BaseMap('dark'), Layer('cb_2013_puma10_500k', color='grey'), Layer('tweets_obama', color='yellow', size='favoritescount') ] self.layers_w_time = [ BaseMap('dark', labels='front'), Layer('acadia'), QueryLayer('select * from acadia limit 10', time='foo'), Layer('biodiversity'), BaseMap('dark', labels='front', only_labels=True) ]
def test_querylayer_get_cartocss(self): """layer.QueryLayer._get_cartocss""" qlayer = QueryLayer(self.query, size=dict(column='cold_brew', min=10, max=20)) self.assertRegexpMatches( qlayer._get_cartocss(BaseMap()), ('.*marker-width:\sramp\(\[cold_brew\],\srange\(10,20\),\s' 'quantiles\(5\)\).*'))
def setUp(self): # basemaps with baked-in labels self.dark_map_all = BaseMap(source='dark') self.light_map_all = BaseMap(source='light') # basemaps with no labels self.dark_map_no_labels = BaseMap(source='dark', labels=None) self.light_map_no_labels = BaseMap(source='light', labels=None) # labels with no basemaps self.dark_only_labels = BaseMap(source='dark', only_labels=True) self.light_only_labels = BaseMap(source='light', only_labels=True)
def test_querylayer_get_cartocss(self): """layer.QueryLayer._get_cartocss""" qlayer = QueryLayer(self.query, size=dict(column='cold_brew', min=10, max=20)) qlayer.geom_type = 'point' self.assertRegexpMatches( qlayer._get_cartocss(BaseMap()), # pylint: disable=protected-access (r'.*marker-width:\sramp\(\[cold_brew\],\srange\(10,20\),\s' r'quantiles\(5\)\).*')) # test line cartocss qlayer = QueryLayer(self.query) qlayer.geom_type = 'line' self.assertRegexpMatches( qlayer._get_cartocss(BaseMap()), # pylint: disable=protected-access r'^\#layer.*line\-width.*$') # test point, line, polygon for geom in ( 'point', 'line', 'polygon', ): styles = { 'point': r'marker\-fill', 'line': r'line\-color', 'polygon': r'polygon\-fill' } qlayer = QueryLayer(self.query, color='colname') qlayer.geom_type = geom self.assertRegexpMatches( qlayer._get_cartocss(BaseMap()), # pylint: disable=protected-access r'^\#layer.*{}.*\}}$'.format(styles[geom])) # geometry type should be defined with self.assertRaises(ValueError, msg='invalid geometry type'): querylayer = QueryLayer(self.query, color='red') querylayer.geom_type = 'notvalid' querylayer._get_cartocss(BaseMap()) # pylint: disable=protected-access
def test_querylayer_time_numeric(self): """layer.QueryLayer time with quantitative classification""" querylayer = QueryLayer(self.query, time='timecol', color='colorcol') # category type querylayer.style_cols['colorcol'] = 'number' querylayer.style_cols['timecol'] = 'date' querylayer.geom_type = 'point' # normal behavior for point geometries querylayer._setup([BaseMap(), querylayer], 1) # pylint: disable=protected-access self.assertDictEqual(querylayer.scheme, styling.mint(5)) # expect category maps query self.assertRegexpMatches(querylayer.query.strip(), r'^SELECT \*, colorcol as value ' r'.*_wrap$') # cartocss should have cdb math mode self.assertRegexpMatches(querylayer.cartocss, r'.*avg\(colorcol\).*')
def test_non_basemap_layers(self): """maps.non_basemap_layers""" nbm_layers = non_basemap_layers(self.layers) # ensure the layers are separated out correctly self.assertEqual(self.layers[1], nbm_layers[0], msg=('first non-basemap layer should be second layer ' 'in original layer list')) self.assertEqual(self.layers[2], nbm_layers[1], msg=('second non-basemap layer should be third layer ' 'in original layer list')) # correct number of layers passed out self.assertEqual(len(nbm_layers), 2) # If no data layers exist, non_basemap_layers should be an empty list nbm_no_layers = non_basemap_layers([BaseMap()]) self.assertEqual(len(nbm_no_layers), 0) # If nothing is passed nbm_nothing = non_basemap_layers([]) self.assertEqual(len(nbm_nothing), 0)
def map(self, layers=None, interactive=True, zoom=None, lat=None, lng=None, size=(800, 400), ax=None): """Produce a CARTO map visualizing data layers. Example: Create a map with two data layers, and one BaseMap layer. :: import cartoframes from cartoframes import Layer, BaseMap, styling cc = cartoframes.CartoContext(BASEURL, APIKEY) cc.map(layers=[BaseMap(), Layer('acadia_biodiversity', color={'column': 'simpson_index', 'scheme': styling.tealRose(7)}), Layer('peregrine_falcon_nest_sites', size='num_eggs', color={'column': 'bird_id', 'scheme': styling.vivid(10))], interactive=True) Args: layers (list, optional): List of one or more of the following: - Layer: cartoframes Layer object for visualizing data from a CARTO table. See `layer.Layer <#layer.Layer>`__ for all styling options. - BaseMap: Basemap for contextualizng data layers. See `layer.BaseMap <#layer.BaseMap>`__ for all styling options. - QueryLayer: Layer from an arbitrary query. See `layer.QueryLayer <#layer.QueryLayer>`__ for all styling options. interactive (bool, optional): Defaults to ``True`` to show an interactive slippy map. Setting to ``False`` creates a static map. zoom (int, optional): Zoom level of map. Acceptable values are usually in the range 0 to 19. 0 has the entire earth on a single tile (256px square). Zoom 19 is the size of a city block. Must be used in conjunction with ``lng`` and ``lat``. Defaults to a view to have all data layers in view. lat (float, optional): Latitude value for the center of the map. Must be used in conjunction with ``zoom`` and ``lng``. Defaults to a view to have all data layers in view. lng (float, optional): Longitude value for the center of the map. Must be used in conjunction with ``zoom`` and ``lat``. Defaults to a view to have all data layers in view. size (tuple, optional): List of pixel dimensions for the map. Format is ``(width, height)``. Defaults to ``(800, 400)``. Returns: IPython.display.HTML: Interactive maps are rendered in an ``iframe``, while static maps are rendered in ``img`` tags. """ # TODO: add layers preprocessing method like # layers = process_layers(layers) # that uses up to layer limit value error if not hasattr(IPython, 'display'): raise NotImplementedError('Nope, cannot display maps at the ' 'command line.') if layers is None: layers = [] elif not isinstance(layers, collections.Iterable): layers = [layers] else: layers = list(layers) if len(layers) > 8: raise ValueError('map can have at most 8 layers') if any([zoom, lat, lng]) != all([zoom, lat, lng]): raise ValueError('zoom, lat, and lng must all or none be provided') # When no layers are passed, set default zoom if ((len(layers) == 0 and zoom is None) or (len(layers) == 1 and layers[0].is_basemap)): [zoom, lat, lng] = [3, 38, -99] has_zoom = zoom is not None # Check basemaps, add one if none exist base_layers = [idx for idx, layer in enumerate(layers) if layer.is_basemap] if len(base_layers) > 1: raise ValueError('map can at most take 1 BaseMap layer') if len(base_layers) > 0: layers.insert(0, layers.pop(base_layers[0])) else: layers.insert(0, BaseMap()) # Check for a time layer, if it exists move it to the front time_layers = [idx for idx, layer in enumerate(layers) if not layer.is_basemap and layer.time] time_layer = layers[time_layers[0]] if len(time_layers) > 0 else None if len(time_layers) > 1: raise ValueError('Map can at most take 1 Layer with time ' 'column/field') if time_layer: raise NotImplementedError('Animated maps are not yet supported') if not interactive: raise ValueError('map cannot display a static image with a ' 'time_column') layers.append(layers.pop(time_layers[0])) # If basemap labels are on front, add labels layer basemap = layers[0] if basemap.is_basic() and basemap.labels == 'front': layers.append(BaseMap(basemap.source, labels=basemap.labels, only_labels=True)) # Setup layers for idx, layer in enumerate(layers): layer._setup(layers, idx) nb_layers = non_basemap_layers(layers) options = {'basemap_url': basemap.url} for idx, layer in enumerate(nb_layers): self._check_query(layer.query, style_cols=layer.style_cols) options['cartocss_' + str(idx)] = layer.cartocss options['sql_' + str(idx)] = layer.query params = { 'config': json.dumps(options), 'anti_cache': random.random(), } if has_zoom: params.update({'zoom': zoom, 'lat': lat, 'lon': lng}) options.update({'zoom': zoom, 'lat': lat, 'lng': lng}) else: options.update(self._get_bounds(nb_layers)) map_name = self._send_map_template(layers, has_zoom=has_zoom) api_url = '{base_url}api/v1/map'.format(base_url=self.base_url) static_url = ('{api_url}/static/named/{map_name}' '/{width}/{height}.png?{params}').format( api_url=api_url, map_name=map_name, width=size[0], height=size[1], params=urlencode(params)) html = '<img src="{url}" />'.format(url=static_url) # TODO: write this as a private method if interactive: netloc = urlparse(self.base_url).netloc domain = 'carto.com' if netloc.endswith('.carto.com') else netloc def safe_quotes(text, escape_single_quotes=False): """htmlify string""" if isinstance(text, str): safe_text = text.replace('"', """) if escape_single_quotes: safe_text = safe_text.replace("'", "\'") return safe_text.replace('True', 'true') return text config = { 'user_name': self.username, 'maps_api_template': self.base_url[:-1], 'sql_api_template': self.base_url[:-1], 'tiler_protocol': 'https', 'tiler_domain': domain, 'tiler_port': '80', 'type': 'torque' if time_layer else 'namedmap', 'named_map': { 'name': map_name, 'params': { k: safe_quotes(v, escape_single_quotes=True) for k, v in dict_items(options) }, }, } map_options = { 'filter': ['http', 'mapnik', 'torque'], 'https': True, } if time_layer: config.update({ 'order': 1, 'options': { 'query': time_layer.query, 'user_name': self.username, 'tile_style': time_layer.torque_cartocss, } }) config['named_map'].update({ 'layers': [{ 'layer_name': 't', }], }) map_options.update({ 'time_slider': True, 'loop': True, }) bounds = [] if has_zoom else [[options['north'], options['east']], [options['south'], options['west']]] content = self._get_iframe_srcdoc(config=config, bounds=bounds, options=options, map_options=map_options) img_html = html html = ( '<iframe srcdoc="{content}" width={width} height={height}>' ' Preview image: {img_html}' '</iframe>' ).format(content=safe_quotes(content), width=size[0], height=size[1], img_html=img_html) return IPython.display.HTML(html) else: try: import matplotlib.image as mpi import matplotlib.pyplot as plt except ImportError: warn('Matplotlib not detected. Saving image directly to disk') raise NotImplementedError raw_data = mpi.imread(static_url) f = plt.gcf() if ax is None: ax = plt.gca() ax.imshow(raw_data) ax.axis('off') return ax
class TestBaseMap(unittest.TestCase): """Tests for functions in keys module""" def setUp(self): # basemaps with baked-in labels self.dark_map_all = BaseMap(source='dark') self.light_map_all = BaseMap(source='light') # basemaps with no labels self.dark_map_no_labels = BaseMap(source='dark', labels=None) self.light_map_no_labels = BaseMap(source='light', labels=None) # labels with no basemaps self.dark_only_labels = BaseMap(source='dark', only_labels=True) self.light_only_labels = BaseMap(source='light', only_labels=True) def test_basemap_invalid(self): """layer.Basemap exceptions on invalid source""" # Raise ValueError if invalid label is entered with self.assertRaises(ValueError): BaseMap(labels='watermelon') # Raise ValueError if custom URL is entered with self.assertRaises(ValueError): BaseMap(source='http://spinalmap.com/{z}/{x}/{y}.png') # Raise ValueError if non-supported style type is entered with self.assertRaises(ValueError): BaseMap(source='gulab_jamon') def test_basemap_source(self): """layer.BaseMap with different sources and labels""" # ensure correct BaseMap urls are created # See URLs here: https://carto.com/location-data-services/basemaps/ self.assertEqual( self.dark_map_all.url, 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/' 'dark_all/{z}/{x}/{y}.png') self.assertEqual( self.light_map_all.url, 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/' 'light_all/{z}/{x}/{y}.png') self.assertEqual( self.dark_map_no_labels.url, 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/' 'dark_nolabels/{z}/{x}/{y}.png') self.assertEqual( self.light_map_no_labels.url, 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/' 'light_nolabels/{z}/{x}/{y}.png') self.assertEqual( self.light_only_labels.url, 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/' 'light_only_labels/{z}/{x}/{y}.png') self.assertEqual( self.dark_only_labels.url, 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/' 'dark_only_labels/{z}/{x}/{y}.png') # ensure self.is_basic() works as intended self.assertTrue(self.light_map_all.is_basic(), msg='is a basic carto basemap') self.assertTrue(self.dark_map_all.is_basic())
def test_line_styling(self): # pylint: disable=too-many-statements """layer.QueryLayer line styling""" linelayer = QueryLayer('select * from lines', size=5) linelayer.geom_type = 'line' linelayer._setup([BaseMap(), linelayer], 1) # pylint: disable=protected-access self.assertTrue('line-width: 5' in linelayer.cartocss) size = 'size_col' color = 'mag' linelayer = QueryLayer('select * from lines', size=size, color=color) linelayer.geom_type = 'line' linelayer.style_cols['mag'] = 'number' linelayer.style_cols['size_col'] = 'number' linelayer._setup([BaseMap(), linelayer], 1) # pylint: disable=protected-access self.assertTrue( 'line-width: ramp([size_col], range(1,5), quantiles(5))' in linelayer.cartocss) self.assertTrue( 'line-color: ramp([mag], cartocolor(Mint), quantiles(5), >)' in linelayer.cartocss) size = {'column': 'size_col'} color = 'mag' linelayer = QueryLayer('select * from lines', size=size, color=color) linelayer.geom_type = 'line' linelayer.style_cols['mag'] = 'number' linelayer.style_cols['size_col'] = 'number' linelayer._setup([BaseMap(), linelayer], 1) # pylint: disable=protected-access self.assertTrue( 'line-width: ramp([size_col], range(1,5), quantiles(5))' in linelayer.cartocss) self.assertTrue( 'line-color: ramp([mag], cartocolor(Mint), quantiles(5), >)' in linelayer.cartocss) size = {'column': 'size_col', 'range': (5, 10)} color = 'mag' linelayer = QueryLayer('select * from lines', size=size, color=color) linelayer.geom_type = 'line' linelayer.style_cols['mag'] = 'number' linelayer.style_cols['size_col'] = 'number' linelayer._setup([BaseMap(), linelayer], 1) # pylint: disable=protected-access self.assertTrue( 'line-width: ramp([size_col], range(5,10), quantiles(5))' in linelayer.cartocss) self.assertTrue( 'line-color: ramp([mag], cartocolor(Mint), quantiles(5), >)' in linelayer.cartocss) size = 1.5 color = 'mag' linelayer = QueryLayer('select * from lines', size=size, color=color) linelayer.geom_type = 'line' linelayer.style_cols['mag'] = 'number' linelayer.style_cols['size_col'] = 'number' linelayer._setup([BaseMap(), linelayer], 1) # pylint: disable=protected-access self.assertTrue('line-width: 1.5' in linelayer.cartocss) self.assertTrue( 'line-color: ramp([mag], cartocolor(Mint), quantiles(5), >)' in linelayer.cartocss) size = {'column': 'size_col', 'range': [2, 6]} color = {'column': 'mag', 'scheme': styling.sunset(7)} linelayer = QueryLayer('select * from lines', size=size, color=color) linelayer.geom_type = 'line' linelayer.style_cols['mag'] = 'number' linelayer.style_cols['size_col'] = 'number' linelayer._setup([BaseMap(), linelayer], 1) # pylint: disable=protected-access self.assertTrue( 'line-width: ramp([size_col], range(2,6), quantiles(5))' in linelayer.cartocss) self.assertTrue( 'line-color: ramp([mag], cartocolor(Sunset), quantiles(7), >)' in linelayer.cartocss) # size and color size = { 'column': 'size_col', 'range': [2, 6], 'bin_method': BinMethod.jenks } color = {'column': 'mag', 'scheme': styling.sunset(7)} linelayer = QueryLayer('select * from lines', size=size, color=color) linelayer.geom_type = 'line' linelayer.style_cols['mag'] = 'number' linelayer.style_cols['size_col'] = 'number' linelayer._setup([BaseMap(), linelayer], 1) # pylint: disable=protected-access self.assertTrue('line-width: ramp([size_col], range(2,6), jenks(5))' in linelayer.cartocss) self.assertTrue( 'line-color: ramp([mag], cartocolor(Sunset), quantiles(7), >)' in linelayer.cartocss) # category lines linelayer = QueryLayer('select * from lines', color={ 'column': 'mag', 'scheme': styling.antique(7) }) linelayer.geom_type = 'line' linelayer._setup([BaseMap(), linelayer], 1) # pylint: disable=protected-access self.assertTrue( 'line-color: ramp([mag], cartocolor(Antique), category(7), =)' in linelayer.cartocss)
class TestBaseMap(unittest.TestCase): # pylint: disable=too-many-instance-attributes """Tests for functions in keys module""" def setUp(self): # basemaps with baked-in labels self.dark_map_all = BaseMap(source='dark') self.light_map_all = BaseMap(source='light') self.voyager_labels_under = BaseMap(source='voyager') # basemaps with no labels self.dark_map_no_labels = BaseMap(source='dark', labels=None) self.light_map_no_labels = BaseMap(source='light', labels=None) self.voyager_map_no_labels = BaseMap(source='voyager', labels=None) # labels with no basemaps self.dark_only_labels = BaseMap(source='dark', only_labels=True) self.light_only_labels = BaseMap(source='light', only_labels=True) self.voyager_only_labels = BaseMap(source='voyager', only_labels=True) def test_basemap_repr(self): """layer.Basemap.__repr__""" self.assertEqual( self.dark_only_labels.__repr__(), 'BaseMap(source=dark, labels=back, only_labels=True)') def test_basemap_invalid(self): """layer.Basemap exceptions on invalid source""" # Raise ValueError if invalid label is entered with self.assertRaises(ValueError): BaseMap(labels='watermelon') # Raise ValueError if custom URL is entered with self.assertRaises(ValueError): BaseMap(source='http://spinalmap.com/{z}/{x}/{y}.png') # Raise ValueError if non-supported style type is entered with self.assertRaises(ValueError): BaseMap(source='gulab_jamon') def test_basemap_source(self): """layer.BaseMap with different sources and labels""" # ensure correct BaseMap urls are created # See URLs here: https://carto.com/location-data-services/basemaps/ self.assertEqual( self.dark_map_all.url, 'https://{s}.basemaps.cartocdn.com/rastertiles/' 'dark_all/{z}/{x}/{y}.png') self.assertEqual( self.light_map_all.url, 'https://{s}.basemaps.cartocdn.com/rastertiles/' 'light_all/{z}/{x}/{y}.png') self.assertEqual( self.voyager_labels_under.url, 'https://{s}.basemaps.cartocdn.com/rastertiles/' 'voyager_labels_under/{z}/{x}/{y}.png') self.assertEqual( self.dark_map_no_labels.url, 'https://{s}.basemaps.cartocdn.com/rastertiles/' 'dark_nolabels/{z}/{x}/{y}.png') self.assertEqual( self.light_map_no_labels.url, 'https://{s}.basemaps.cartocdn.com/rastertiles/' 'light_nolabels/{z}/{x}/{y}.png') self.assertEqual( self.voyager_map_no_labels.url, 'https://{s}.basemaps.cartocdn.com/rastertiles/' 'voyager_nolabels/{z}/{x}/{y}.png') self.assertEqual( self.light_only_labels.url, 'https://{s}.basemaps.cartocdn.com/rastertiles/' 'light_only_labels/{z}/{x}/{y}.png') self.assertEqual( self.dark_only_labels.url, 'https://{s}.basemaps.cartocdn.com/rastertiles/' 'dark_only_labels/{z}/{x}/{y}.png') self.assertEqual( self.voyager_only_labels.url, 'https://{s}.basemaps.cartocdn.com/rastertiles/' 'voyager_only_labels/{z}/{x}/{y}.png') # ensure self.is_basic() works as intended self.assertTrue(self.light_map_all.is_basic(), msg='is a basic carto basemap') self.assertTrue(self.dark_map_all.is_basic()) self.assertTrue(self.voyager_labels_under.is_basic(), msg='is a basic carto basemap')
def test_layer_setup_dataframe(self): """layer.Layer._setup()""" layer = Layer('cortado', source=self.coffee_temps) with self.assertRaises(NotImplementedError): layer._setup([BaseMap(), layer], 1) # pylint: disable=protected-access
def test_querylayer_colors(self): """layer.QueryLayer color options tests""" # no color options passed basic = QueryLayer(self.query) self.assertEqual(basic.color, None) # check valid dict color options dict_colors = [{ 'column': 'mandrill', 'scheme': styling.armyRose(7) }, { 'column': 'mercxx', 'scheme': { 'bin_method': 'equal', 'bins': 7, 'name': 'Temps' } }, { 'column': 'elephant', 'scheme': styling.redOr(10, bin_method='jenks') }] dict_colors_ans = ['mandrill', 'mercxx', 'elephant'] dict_colors_scheme = [{ 'name': 'ArmyRose', 'bins': 7, 'bin_method': 'quantiles' }, { 'name': 'Temps', 'bins': 7, 'bin_method': 'equal' }, { 'name': 'RedOr', 'bins': 10, 'bin_method': 'jenks' }] for idx, val in enumerate(dict_colors): qlayer = QueryLayer(self.query, color=val) self.assertEqual(qlayer.color, dict_colors_ans[idx]) self.assertEqual(qlayer.scheme, dict_colors_scheme[idx]) # check valid string color options str_colors = ('#FF0000', 'aliceblue', 'cookie_monster', 'big_bird') str_colors_ans = ('#FF0000', 'aliceblue', 'cookie_monster', 'big_bird') str_scheme_ans = (None, None, styling.mint(5), styling.antique(10)) for idx, color in enumerate(str_colors): qlayer = QueryLayer(self.query, color=color) qlayer.geom_type = 'point' if color == 'cookie_monster': qlayer.style_cols[color] = 'number' qlayer._setup([BaseMap(), qlayer], 1) # pylint: disable=protected-access elif color == 'big_bird': qlayer.style_cols[color] = 'string' qlayer._setup([BaseMap(), qlayer], 1) # pylint: disable=protected-access self.assertEqual(qlayer.color, str_colors_ans[idx]) self.assertEqual(qlayer.scheme, str_scheme_ans[idx]) with self.assertRaises(ValueError, msg='styling value cannot be a date'): qlayer = QueryLayer(self.query, color='datetime_column') qlayer.style_cols['datetime_column'] = 'date' qlayer._setup([BaseMap(), qlayer], 1) # pylint: disable=protected-access # Exception testing # color column cannot be a geometry column with self.assertRaises(ValueError, msg='color clumn cannot be a geometry column'): QueryLayer(self.query, color='the_geom') # color dict must have a 'column' key with self.assertRaises(ValueError, msg='color dict must have a `column` key'): QueryLayer(self.query, color={'scheme': styling.vivid(10)})