Merge pull request #88 from mistercrunch/map

World Map viz with bubbles
This commit is contained in:
Maxime Beauchemin 2015-12-17 11:12:09 -08:00
commit 047e142b43
11 changed files with 26789 additions and 0 deletions

11549
panoramix/data/countries.json Normal file

File diff suppressed because it is too large Load Diff

2490
panoramix/data/countries.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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

1
panoramix/static/lib/topojson.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -20,6 +20,10 @@ form div {
text-align: right;
}
.select2-results .select2-highlighted {
background-color: #005c66;
}
.notbtn {
cursor: default;
}

View File

@ -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);

View File

@ -0,0 +1,8 @@
{% macro viz_html(viz) %}
{% endmacro %}
{% macro viz_js(viz) %}
{% endmacro %}
{% macro viz_css(viz) %}
{% endmacro %}

View File

@ -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])