def setUp(self): # color schemes with all different names self.burg = styling.burg(bins=4) self.burgYl = styling.burgYl(bins=4) self.redOr = styling.redOr(bins=4) self.orYel = styling.orYel(bins=4) self.peach = styling.peach(bins=4) self.pinkYl = styling.pinkYl(bins=4) self.mint = styling.mint(bins=4) self.bluGrn = styling.bluGrn(bins=4) self.darkMint = styling.darkMint(bins=4) self.emrld = styling.emrld(bins=4) self.bluYl = styling.bluYl(bins=4) self.teal = styling.teal(bins=4) self.tealGrn = styling.tealGrn(bins=4) self.purp = styling.purp(bins=4) self.purpOr = styling.purpOr(bins=4) self.sunset = styling.sunset(bins=4) self.magenta = styling.magenta(bins=4) self.sunsetDark = styling.sunsetDark(bins=4) self.brwnYl = styling.brwnYl(bins=4) self.armyRose = styling.armyRose(bins=4) self.fall = styling.fall(bins=4) self.geyser = styling.geyser(bins=4) self.temps = styling.temps(bins=4) self.tealRose = styling.tealRose(bins=4) self.tropic = styling.tropic(bins=4) self.earth = styling.earth(bins=4) self.antique = styling.antique(bins=4) self.bold = styling.bold(bins=4) self.pastel = styling.pastel(bins=4) self.prism = styling.prism(bins=4) self.safe = styling.safe(bins=4) self.vivid = styling.vivid(bins=4)
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_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'] str_colors_ans = ['#FF0000', 'aliceblue', 'cookie_monster'] str_scheme_ans = [None, None, styling.mint(5)] for idx, color in enumerate(str_colors): qlayer = QueryLayer(self.query, color=color) print(qlayer.color) self.assertEqual(qlayer.color, str_colors_ans[idx]) self.assertEqual(qlayer.scheme, str_scheme_ans[idx]) # 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)})
def __init__(self, query, time=None, color=None, size=None, tooltip=None, legend=None): self.query = query self.style_cols = set() # color, scheme = self._get_colorscheme() # time = self._get_timescheme() # size = self._get_sizescheme() # If column was specified, force a scheme # It could be that there is a column named 'blue' for example if isinstance(color, dict): if 'column' not in color: raise ValueError("color must include a 'column' value") scheme = color.get('scheme', mint(5)) color = color['column'] self.style_cols.add(color) elif (color and color[0] != '#' and color not in webcolors.CSS3_NAMES_TO_HEX): # color specified that is not a web color or hex value so its # assumed to be a column name color = color self.style_cols.add(color) scheme = mint(5) else: # assume it's a color color = color scheme = None if time: if isinstance(time, dict): if 'column' not in time: raise ValueError("time must include a 'column' value") time_column = time['column'] time_options = time elif isinstance(time, str): time_column = time time_options = {} else: raise ValueError('`time` should be a column name or ' 'dictionary of styling options.') self.style_cols.add(time_column) time = { 'column': time_column, 'method': 'count', 'cumulative': False, 'frames': 256, 'duration': 30, } time.update(time_options) size = size or 10 if isinstance(size, str): size = {'column': size} if isinstance(size, dict): if 'column' not in size: raise ValueError("Size must include a 'column' key/value") if time: raise ValueError("When time is specified, size can " "only be a fixed size") old_size = size size = { 'range': [5, 25], 'bins': 10, 'bin_method': BinMethod.quantiles, } size.update(old_size) # Since we're accessing min/max, convert range into a list size['range'] = list(size['range']) self.style_cols.add(size['column']) self.color = color self.scheme = scheme self.size = size self.time = time self.tooltip = tooltip self.legend = legend self._validate_columns()
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)})
def _setup(self, layers, layer_idx): basemap = layers[0] # if color not specified, choose a default # choose color time default if self.time: # default torque color self.color = self.color or '#2752ff' else: self.color = self.color or DEFAULT_COLORS[layer_idx] # choose appropriate scheme if not already specified if (not self.scheme) and (self.color in self.style_cols): if self.style_cols[self.color] in ('string', 'boolean', ): self.scheme = antique(10) elif self.style_cols[self.color] in ('number', ): self.scheme = mint(5) elif self.style_cols[self.color] in ('date', 'geometry', ): raise ValueError( 'Cannot style column `{col}` of type `{type}`. It must be ' 'numeric, text, or boolean.'.format( col=self.color, type=self.style_cols[self.color])) if self.time: # validate time column information if self.geom_type != 'point': raise ValueError('Cannot do time-based maps with data in ' '`{query}` since this table does not contain ' 'point geometries'.format( query=self.orig_query)) elif self.style_cols[self.time['column']] not in ( 'number', 'date', ): raise ValueError('Cannot create an animated map from column ' '`{col}` because it is of type {t1}. It must ' 'be of type number or date.'.format( col=self.time['column'], t1=self.style_cols[self.time['column']])) # don't use turbo-carto for animated maps column = self.time['column'] frames = self.time['frames'] method = self.time['method'] duration = self.time['duration'] if (self.color in self.style_cols and self.style_cols[self.color] in ('string', 'boolean', )): self.query = minify_sql([ 'SELECT', ' orig.*, __wrap.cf_value_{col}', 'FROM ({query}) AS orig, (', ' SELECT', ' row_number() OVER (', ' ORDER BY val_{col}_cnt DESC) AS cf_value_{col},', ' {col}', ' FROM (', ' SELECT {col}, count({col}) AS val_{col}_cnt', ' FROM ({query}) as orig', ' GROUP BY {col}', ' ORDER BY 2 DESC', ' ) AS _wrap', ') AS __wrap', 'WHERE __wrap.{col} = orig.{col}', ]).format(col=self.color, query=self.orig_query) agg_func = '\'CDB_Math_Mode(cf_value_{})\''.format(self.color) self.scheme = { 'bins': [str(i) for i in range(1, 11)], 'name': (self.scheme.get('name') if self.scheme else 'Bold'), 'bin_method': '', } elif (self.color in self.style_cols and self.style_cols[self.color] in ('number', )): self.query = ' '.join([ 'SELECT *, {col} as value', 'FROM ({query}) as _wrap' ]).format(col=self.color, query=self.orig_query) agg_func = '\'avg({})\''.format(self.color) else: agg_func = "'{method}(cartodb_id)'".format( method=method) self.torque_cartocss = cssify({ 'Map': { '-torque-frame-count': frames, '-torque-animation-duration': duration, '-torque-time-attribute': "'{}'".format(column), '-torque-aggregation-function': agg_func, '-torque-resolution': 1, '-torque-data-aggregation': ('cumulative' if self.time['cumulative'] else 'linear'), }, }) self.cartocss = (self.torque_cartocss + self._get_cartocss(basemap, has_time=True)) else: # use turbo-carto for non-animated maps self.cartocss = self._get_cartocss(basemap)