mirror of https://github.com/apache/superset.git
Merge branch 'master' of github.com:mistercrunch/panoramix
This commit is contained in:
commit
2a30908328
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -108,6 +108,18 @@ class FormFactory(object):
|
|||
'Color Metric', choices=datasource.metrics_combo,
|
||||
default=default_metric,
|
||||
description="A metric to use for color"),
|
||||
'country_fieldtype': SelectField(
|
||||
'Country Field Type',
|
||||
default='cca2',
|
||||
choices=(
|
||||
('name', 'Full name'),
|
||||
('cioc', 'code International Olympic Committee (cioc)'),
|
||||
('cca2', 'code ISO 3166-1 alpha-2 (cca2)'),
|
||||
('cca3', 'code ISO 3166-1 alpha-3 (cca3)'),
|
||||
),
|
||||
description=(
|
||||
"The country code standard that Panoramix should expect "
|
||||
"to find in the [country] column")),
|
||||
'groupby': SelectMultipleSortableField(
|
||||
'Group by',
|
||||
choices=self.choicify(datasource.groupby_column_names),
|
||||
|
@ -191,6 +203,18 @@ class FormFactory(object):
|
|||
'90 days ago',
|
||||
'1 year ago'])
|
||||
),
|
||||
'max_bubble_size': FreeFormSelectField(
|
||||
'Max Bubble Size', default="25",
|
||||
choices=self.choicify([
|
||||
'5',
|
||||
'10',
|
||||
'15',
|
||||
'25',
|
||||
'50',
|
||||
'75',
|
||||
'100',
|
||||
])
|
||||
),
|
||||
'row_limit':
|
||||
FreeFormSelectField(
|
||||
'Row limit',
|
||||
|
@ -298,6 +322,10 @@ class FormFactory(object):
|
|||
"Range Filter", default=True,
|
||||
description=(
|
||||
"Whether to display the time range interactive selector")),
|
||||
'show_bubbles': BetterBooleanField(
|
||||
"Show Bubbles", default=False,
|
||||
description=(
|
||||
"Whether to display bubbles on top of countries")),
|
||||
'show_legend': BetterBooleanField(
|
||||
"Legend", default=True,
|
||||
description="Whether to display the legend (toggles)"),
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -20,6 +20,10 @@ form div {
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
.select2-results .select2-highlighted {
|
||||
background-color: #005c66;
|
||||
}
|
||||
|
||||
.notbtn {
|
||||
cursor: default;
|
||||
}
|
||||
|
@ -55,9 +59,9 @@ legend.legend-style {
|
|||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
.nvtooltip{
|
||||
.nvtooltip {
|
||||
position: relative; !important
|
||||
z-index: 10;
|
||||
z-index: 888;
|
||||
}
|
||||
|
||||
legend {
|
||||
|
@ -185,7 +189,7 @@ legend {
|
|||
overflow: visible; /* This allows elements within these slice typesin a dashboard to overflow */
|
||||
}
|
||||
.dashboard div.nvtooltip {
|
||||
z-index: 1; /* this lets tool tips go on top of other slices */
|
||||
z-index: 888; /* this lets tool tips go on top of other slices */
|
||||
}
|
||||
.dashboard td.icons {
|
||||
width: 50px;
|
||||
|
|
|
@ -217,6 +217,13 @@ function initializeDashboardView(dashboard_id) {
|
|||
css = $(this).val();
|
||||
$("#user_style").html(css);
|
||||
});
|
||||
$('li.widget').each(function() { /* this sets the z-index for left side boxes higher. */
|
||||
current_row = $(this).attr('data-col');
|
||||
$( this ).css('z-index', 100 - current_row);
|
||||
});
|
||||
$("div.chart").each(function() { /* this makes the whole chart fit within the dashboard div */
|
||||
$(this).css('height', '95%');
|
||||
});
|
||||
}
|
||||
|
||||
// Export public functions
|
||||
|
|
|
@ -9,14 +9,13 @@ function viz_nvd3(data_attribute) {
|
|||
return v = new Date(dttm.getUTCFullYear(), dttm.getUTCMonth(), dttm.getUTCDate(), dttm.getUTCHours(), dttm.getUTCMinutes(), dttm.getUTCSeconds());
|
||||
}
|
||||
var tickMultiFormat = d3.time.format.multi([
|
||||
[".%L", function(d) { return d.getMilliseconds(); }],
|
||||
[":%S", function(d) { return d.getSeconds(); }],
|
||||
["%I:%M", function(d) { return d.getMinutes(); }],
|
||||
["%I %p", function(d) { return d.getHours(); }],
|
||||
["%a %d", function(d) { return d.getDay() && d.getDate() != 1; }],
|
||||
["%b %d", function(d) { return d.getDate() != 1; }],
|
||||
["%B", function(d) { return d.getMonth(); }],
|
||||
["%Y", function() { return true; }]
|
||||
[".%L", function(d) { return d.getMilliseconds(); }], // If there are millisections, show only them
|
||||
[":%S", function(d) { return d.getSeconds(); }], // If there are seconds, show only them
|
||||
["%a %b %d, %I:%M %p", function(d) { return d.getMinutes()!=0; }], // If there are non-zero minutes, show Date, Hour:Minute [AM/PM]
|
||||
["%a %b %d, %I %p", function(d) { return d.getHours() != 0; }], // If there are hours that are multiples of 3, show date and AM/PM
|
||||
["%a %b %d, %Y", function(d) { return d.getDate() != 1; }], // If not the first of the month, do "month day, year."
|
||||
["%B %Y", function(d) { return d.getMonth() != 0 && d.getDate() == 1; }], // If the first of the month, do "month day, year."
|
||||
["%Y", function(d) { return true; }] // fall back on month, year
|
||||
]);
|
||||
function formatDate(dttm) {
|
||||
var d = UTC(new Date(dttm));
|
||||
|
@ -44,7 +43,8 @@ function viz_nvd3(data_attribute) {
|
|||
chart.lines2.xScale(d3.time.scale.utc());
|
||||
chart.x2Axis
|
||||
.showMaxMin(viz.form_data.x_axis_showminmax)
|
||||
.tickFormat(formatDate);
|
||||
.tickFormat(formatDate)
|
||||
.staggerLabels(true);
|
||||
} else {
|
||||
chart = nv.models.lineChart()
|
||||
}
|
||||
|
@ -54,9 +54,13 @@ function viz_nvd3(data_attribute) {
|
|||
chart.interpolate(viz.form_data.line_interpolation);
|
||||
chart.xAxis
|
||||
.showMaxMin(viz.form_data.x_axis_showminmax)
|
||||
.tickFormat(formatDate);
|
||||
.tickFormat(formatDate)
|
||||
.staggerLabels(true);
|
||||
chart.showLegend(viz.form_data.show_legend);
|
||||
chart.yAxis.tickFormat(d3.format('.3s'));
|
||||
if (chart.y2Axis != undefined) {
|
||||
chart.y2Axis.tickFormat(d3.format('.3s'));
|
||||
}
|
||||
if (viz.form_data.contribution || viz.form_data.num_period_compare) {
|
||||
chart.yAxis.tickFormat(d3.format('.3p'));
|
||||
if (chart.y2Axis != undefined) {
|
||||
|
@ -125,6 +129,8 @@ function viz_nvd3(data_attribute) {
|
|||
chart.yAxis.tickFormat(d3.format('.3s'));
|
||||
}
|
||||
|
||||
// make space for labels on right
|
||||
chart.height($(".chart").height() - 50).margin({"right": 50});
|
||||
if ((viz_type === "line" || viz_type === "area") && viz.form_data.rich_tooltip) {
|
||||
chart.useInteractiveGuideline(true);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
Using the awesome lib at http://datamaps.github.io/
|
||||
*/
|
||||
|
||||
function viz_world_map(data_attribute) {
|
||||
var token = d3.select('#' + data_attribute.token);
|
||||
var render = function(done) {
|
||||
// Breadcrumb dimensions: width, height, spacing, width of tip/tail.
|
||||
var div = token;
|
||||
var xy = div.node().getBoundingClientRect();
|
||||
var width = xy.width;
|
||||
var height = xy.height - 25;
|
||||
|
||||
d3.json(data_attribute.json_endpoint, function(error, json){
|
||||
|
||||
if (error != null){
|
||||
var err = '<div class="alert alert-danger">' + error.responseText + '</div>';
|
||||
token.html(err);
|
||||
return '';
|
||||
done();
|
||||
}
|
||||
var ext = d3.extent(json.data, function(d){return d.m1});
|
||||
var extRadius = d3.extent(json.data, function(d){return d.m2});
|
||||
var radiusScale = d3.scale.linear()
|
||||
.domain([extRadius[0], extRadius[1]])
|
||||
.range([1, data_attribute.form_data.max_bubble_size]);
|
||||
json.data.forEach(function(d){
|
||||
d.radius = radiusScale(d.m2);
|
||||
})
|
||||
var colorScale = d3.scale.linear()
|
||||
.domain([ext[0], ext[1]])
|
||||
.range(["#FFF", "black"]);
|
||||
var d = {};
|
||||
for (var i=0; i<json.data.length; i++){
|
||||
var country = json.data[i];
|
||||
country['fillColor'] = colorScale(country.m1);
|
||||
d[country.country] = country;
|
||||
}
|
||||
f = d3.format('.3s');
|
||||
var map = new Datamap({
|
||||
element: document.getElementById(data_attribute.token),
|
||||
data: json.data,
|
||||
fills: {
|
||||
defaultFill: 'grey'
|
||||
},
|
||||
geographyConfig: {
|
||||
popupOnHover: true,
|
||||
highlightOnHover: true,
|
||||
borderWidth: 1,
|
||||
borderColor: 'grey',
|
||||
highlightBorderColor: 'black',
|
||||
highlightFillColor: '#005a63',
|
||||
highlightBorderWidth: 1,
|
||||
popupTemplate: function(geo, data) {
|
||||
return '<div class="hoverinfo"><strong>' + data.name + '</strong><br>'+ f(data.m1) + '</div>';
|
||||
},
|
||||
},
|
||||
bubblesConfig: {
|
||||
borderWidth: 1,
|
||||
borderOpacity: 1,
|
||||
borderColor: '#005a63',
|
||||
popupOnHover: true,
|
||||
radius: null,
|
||||
popupTemplate: function(geo, data) {
|
||||
return '<div class="hoverinfo"><strong>' + data.name + '</strong><br>'+ f(data.m2) + '</div>';
|
||||
},
|
||||
fillOpacity: 0.5,
|
||||
animate: true,
|
||||
highlightOnHover: true,
|
||||
highlightFillColor: '#005a63',
|
||||
highlightBorderColor: 'black',
|
||||
highlightBorderWidth: 2,
|
||||
highlightBorderOpacity: 1,
|
||||
highlightFillOpacity: 0.85,
|
||||
exitDelay: 100,
|
||||
key: JSON.stringify
|
||||
},
|
||||
});
|
||||
map.updateChoropleth(d);
|
||||
if(data_attribute.form_data.show_bubbles){
|
||||
map.bubbles(json.data);
|
||||
token.selectAll("circle.datamaps-bubble").style('fill', '#005a63');
|
||||
}
|
||||
done(json);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
render: render,
|
||||
resize: render,
|
||||
};
|
||||
}
|
||||
px.registerWidget('world_map', viz_world_map);
|
|
@ -0,0 +1,8 @@
|
|||
{% macro viz_html(viz) %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro viz_js(viz) %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro viz_css(viz) %}
|
||||
{% endmacro %}
|
|
@ -961,6 +961,87 @@ class DirectedForceViz(BaseViz):
|
|||
d = df.to_dict(orient='records')
|
||||
return dumps(d)
|
||||
|
||||
|
||||
class WorldMapViz(BaseViz):
|
||||
viz_type = "world_map"
|
||||
verbose_name = "World Map"
|
||||
is_timeseries = False
|
||||
template = 'panoramix/viz_world_map.html'
|
||||
js_files = [
|
||||
'lib/d3.min.js',
|
||||
'lib/topojson.min.js',
|
||||
'lib/datamaps.all.js',
|
||||
'widgets/viz_world_map.js']
|
||||
css_files = ['widgets/viz_world_map.css']
|
||||
fieldsets = (
|
||||
{
|
||||
'label': None,
|
||||
'fields': (
|
||||
'granularity',
|
||||
('since', 'until'),
|
||||
'entity',
|
||||
'country_fieldtype',
|
||||
'metric',
|
||||
)
|
||||
},
|
||||
{
|
||||
'label': 'Bubbles',
|
||||
'fields': (
|
||||
('show_bubbles', None),
|
||||
'secondary_metric',
|
||||
'max_bubble_size',
|
||||
)
|
||||
})
|
||||
form_overrides = {
|
||||
'entity': {
|
||||
'label': 'Country Field',
|
||||
'description': "3 letter code of the country",
|
||||
},
|
||||
'metric': {
|
||||
'label': 'Metric for color',
|
||||
'description': ("Metric that defines the color of the country"),
|
||||
},
|
||||
'secondary_metric': {
|
||||
'label': 'Bubble size',
|
||||
'description': ("Metric that defines the size of the bubble"),
|
||||
},
|
||||
}
|
||||
def query_obj(self):
|
||||
qry = super(WorldMapViz, self).query_obj()
|
||||
qry['metrics'] = [
|
||||
self.form_data['metric'], self.form_data['secondary_metric']]
|
||||
qry['groupby'] = [self.form_data['entity']]
|
||||
return qry
|
||||
|
||||
def get_json_data(self):
|
||||
from panoramix.data import countries
|
||||
df = self.get_df()
|
||||
cols = [self.form_data.get('entity')]
|
||||
metric = self.form_data.get('metric')
|
||||
secondary_metric = self.form_data.get('secondary_metric')
|
||||
if metric == secondary_metric:
|
||||
ndf = df[cols]
|
||||
ndf['m1'] = df[metric]
|
||||
ndf['m2'] = df[metric]
|
||||
else:
|
||||
cols += [metric, secondary_metric]
|
||||
ndf = df[cols]
|
||||
df = ndf
|
||||
df.columns = ['country', 'm1', 'm2']
|
||||
d = df.to_dict(orient='records')
|
||||
for row in d:
|
||||
country = countries.get(
|
||||
self.form_data.get('country_fieldtype'), row['country'])
|
||||
if country:
|
||||
row['country'] = country['cca3']
|
||||
row['latitude'] = country['lat']
|
||||
row['longitude'] = country['lng']
|
||||
row['name'] = country['name']
|
||||
else:
|
||||
row['country'] = "XXX"
|
||||
return dumps(d)
|
||||
|
||||
|
||||
viz_types_list = [
|
||||
TableViz,
|
||||
PivotTableViz,
|
||||
|
@ -977,6 +1058,7 @@ viz_types_list = [
|
|||
SunburstViz,
|
||||
DirectedForceViz,
|
||||
SankeyViz,
|
||||
WorldMapViz,
|
||||
]
|
||||
# This dict is used to
|
||||
viz_types = OrderedDict([(v.viz_type, v) for v in viz_types_list])
|
||||
|
|
Loading…
Reference in New Issue