def _setup(self, layers, layer_idx): basemap = layers[0] self.color = self.color or DEFAULT_COLORS[layer_idx] self.cartocss = self._get_cartocss(basemap) if self.time: column = self.time['column'] frames = self.time['frames'] method = self.time['method'] duration = self.time['duration'] agg_func = "'{method}({time_column})'".format(method=method, time_column=column) 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
def test_cssify(self): """utils.cssify""" # point style point_stylecss = cssify(self.point_style) self.assertEqual(point_stylecss, ("#layer['mapnik::geometry_type'=1] { " "marker-width: 6; marker-fill: yellow; " "marker-fill-opacity: 1; marker-allow-overlap: " "true; marker-line-width: 0.5; marker-line-color: " "black; marker-line-opacity: 1;} "), msg="point style") # polygon style polygon_stylecss = cssify(self.polygon_style) self.assertEqual(polygon_stylecss, ("#layer['mapnik::geometry_type'=3] { " "polygon-fill: ramp([column], (#ffc6c4, #ee919b, " "#cc607d, #9e3963, #672044), quantiles); " "polygon-opacity: 0.9; polygon-gamma: 0.5; " "line-color: #FFF; line-width: 0.5; line-opacity: " "0.25; line-comp-op: hard-light;} "), msg="polygon style") # complex style complex_stylecss = cssify(self.complex_style) self.assertEqual(complex_stylecss, ("#layer['mapnik::geometry_type'=1] { " "marker-width: 5; marker-fill: yellow; " "marker-fill-opacity: 1; marker-allow-overlap: " "true; marker-line-width: 0.5; marker-line-color: " "black; marker-line-opacity: 1;} " "#layer['mapnik::geometry_type'=2] { " "line-width: 1.5; line-color: black;} " "#layer['mapnik::geometry_type'=3] { " "polygon-fill: blue; polygon-opacity: 0.9; " "polygon-gamma: 0.5; line-color: #FFF; line-width: " "0.5; line-opacity: 0.25; " "line-comp-op: hard-light;} "), msg="multi-layer styling")
def _get_cartocss(self, basemap): """Generate cartocss for class properties""" if isinstance(self.size, int): size_style = self.size elif isinstance(self.size, dict): size_style = ('ramp([{column}],' ' range({min_range},{max_range}),' ' {bin_method}({bins}))').format( column=self.size['column'], min_range=self.size['range'][0], max_range=self.size['range'][1], bin_method=self.size['bin_method'], bins=self.size['bins']) if self.scheme: color_style = get_scheme_cartocss(self.color, self.scheme) else: color_style = self.color line_color = '#000' if basemap.source == 'dark' else '#FFF' return cssify({ # Point CSS "#layer['mapnik::geometry_type'=1]": { 'marker-width': size_style, 'marker-fill': color_style, 'marker-fill-opacity': '1', 'marker-allow-overlap': 'true', 'marker-line-width': '0.5', 'marker-line-color': line_color, 'marker-line-opacity': '1', }, # Line CSS "#layer['mapnik::geometry_type'=2]": { 'line-width': '1.5', 'line-color': color_style, }, # Polygon CSS "#layer['mapnik::geometry_type'=3]": { 'polygon-fill': color_style, 'polygon-opacity': '0.9', 'polygon-gamma': '0.5', 'line-color': '#FFF', 'line-width': '0.5', 'line-opacity': '0.25', 'line-comp-op': 'hard-light', } })
def _get_cartocss(self, basemap, has_time=False): """Generate cartocss for class properties""" if isinstance(self.size, int): size_style = self.size or 4 elif isinstance(self.size, dict): size_style = ('ramp([{column}],' ' range({min_range},{max_range}),' ' {bin_method}({bins}))').format( column=self.size['column'], min_range=self.size['range'][0], max_range=self.size['range'][1], bin_method=self.size['bin_method'], bins=self.size['bins']) if self.scheme: color_style = get_scheme_cartocss( 'value' if has_time else self.color, self.scheme) else: color_style = self.color line_color = '#000' if basemap.source == 'dark' else '#FFF' if self.time: css = cssify({ # Torque Point CSS "#layer": { 'marker-width': size_style, 'marker-fill': color_style, 'marker-fill-opacity': 0.9, 'marker-allow-overlap': 'true', 'marker-line-width': 0, 'marker-line-color': line_color, 'marker-line-opacity': 1, 'comp-op': 'source-over', } }) if self.color in self.style_cols: css += cssify({ '#layer[{} = null]'.format(self.color): { 'marker-fill': '#666'} }) for trail_num in range(1, self.time['trails'] + 1): # Trails decay as 1/2^n, and grow 30% at each step trail_temp = cssify({ '#layer[frame-offset={}]'.format(trail_num): { 'marker-width': size_style * (1.0 + trail_num * 0.3), 'marker-opacity': 0.9 / 2.0**trail_num, } }) css += trail_temp return css else: if self.geom_type == 'point': css = cssify({ # Point CSS "#layer": { 'marker-width': size_style, 'marker-fill': color_style, 'marker-fill-opacity': '1', 'marker-allow-overlap': 'true', 'marker-line-width': '0.5', 'marker-line-color': line_color, 'marker-line-opacity': '1', }}) if self.color in self.style_cols: css += cssify({ '#layer[{} = null]'.format(self.color): { 'marker-fill': '#ccc'} }) return css elif self.geom_type == 'line': css = cssify({ "#layer": { 'line-width': '1.5', 'line-color': color_style, }}) if self.color in self.style_cols: css += cssify({ '#layer[{} = null]'.format(self.color): { 'line-color': '#ccc'} }) return css elif self.geom_type == 'polygon': css = cssify({ "#layer": { 'polygon-fill': color_style, 'polygon-opacity': '0.9', 'polygon-gamma': '0.5', 'line-color': '#FFF', 'line-width': '0.5', 'line-opacity': '0.25', 'line-comp-op': 'hard-light', }}) if self.color in self.style_cols: css += cssify({ '#layer[{} = null]'.format(self.color): { 'polygon-fill': '#ccc'} }) return css else: raise ValueError('Unsupported geometry type: {}'.format( self.geom_type))
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)