In [1]:
from matplotlib import pyplot as plt
from IPython.display import display, Javascript, HTML
import jinja2
import json
import numpy as np
import pandas as pd
In [2]:
def inline_dc(stuff):
"""
Embeds the HTML source of the dc charts directly into the IPython notebook.
This method will not work if the dc charts depends on any files(json data).
Also this uses the HTML5 srcdoc attribute, which may not be supported in
all browsers.
"""
return HTML('''
<iframe srcdoc="{srcdoc}"
style="width: 100%; height: 300px; border: none"></iframe>
'''.format(srcdoc=stuff.replace('"', '"')))
def import_js(path_js):
with open(path_js) as f:
return Javascript(f.read())
In [3]:
with open('./maps/data/br-states.json') as f:
br_states = f.read()
br_states_json = json.loads(br_states)
states = [
br_states_json['features'][i]['properties']['nome']
for i in range(27)
]
In [5]:
np.random.seed(seed=2016)
df = pd.DataFrame({
'incidence': np.random.randint(low=2, high=100, size=54),
'age': np.random.randint(low=2, high=60, size=54),
'state': [st for st in states for t in range(2)],
'alert': np.random.randint(low=1, high=5, size=54),
'w': [i for t in range(27) for i in range(1, 3)]
})
df
Out[5]:
In [6]:
# import js libs
head = HTML('''
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF8">
<link
rel="stylesheet"
type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.0.0-alpha.5/dc.css"/>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.min.js"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.min.js"
></script>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.0.0-rc.3/dist/leaflet.css"
/>
<script
src="https://unpkg.com/leaflet@1.0.0-rc.3/dist/leaflet.js"
></script>
<script
src="https://code.jquery.com/jquery-2.2.4.min.js"
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
crossorigin="anonymous"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"
></script>
<script>
{import_js}
</script>
<style>
.chart {
margin-left:90px;
}
.table-hover tbody tr:hover td, .table-hover tbody tr:hover th {
background-color: #f5f5f5;
}
.table th, .table td {
padding: 8px;
line-height: 20px;
text-align: left;
vertical-align: top;
border-top: 1px solid #ddd;
}
user agent stylesheettd, th {
display: table-cell;
vertical-align: inherit;
}
</style>
</head>
'''.replace(
'{import_js}', import_js('static/libs/dc/dc.min.js').data
))
In [7]:
# body
body_html = HTML("""
<div>
Period (week):
<select id="week">
<option value="">All Weeks</option>
<option value="1" selected="selected">1</option>
<option value="2">2</option>
</select>
<input type="hidden" id="selected_state" value=""/>
</div>
<div id="map" style="height:250px;width:500px;"></div>
<div>
<table id="data-table" class='table'>
<!-- create a custom header -->
<thead class="header">
<tr class="header">
<th>State</th>
<th>Incidence</th>
<th>Age</th>
<th>Alert</th>
</tr>
</thead>
</table>
<div>
<!-- A div anchor that can be identified by id -->
<div id="linechart" class="dc-chart" style="display:none;">
<!-- Title or anything you want to add above the chart -->
<strong>Series Chart</strong>
<!--
This will create a reset button when ever the user selects a filter.
Clicking on the reset button will remove all filters.
-->
<a class="reset" href="javascript:void(0);" onclick="javascript:LineChart.filterAll();dc.redrawAll();" style="display: none;">reset</a>
<div class="clearfix"></div>
<!--
dc.js will also automatically inject applied current filter value into
any html element with css class set to "filter"
-->
<span class="reset" style="display: none;">Current filter: <span class="filter"></span></span>
<br/>
<br/>
</div>
<div id="rowchart" class="dc-chart" style="display:none;">
<!-- Title or anything you want to add above the chart -->
<strong>Age Chart</strong>
<!--
This will create a reset button when ever the user selects a filter.
Clicking on the reset button will remove all filters.
-->
<a class="reset" href="javascript:void(0);" onclick="javascript:RowChart.filterAll();dc.redrawAll();" style="display: none;">reset</a>
<div class="clearfix"></div>
<!--
dc.js will also automatically inject applied current filter value into
any html element with css class set to "filter"
-->
<span class="reset" style="display: none;">Current filter: <span class="filter"></span></span>
<br/>
<br/>
</div>
""")
In [8]:
# js template
dc_template = jinja2.Template(
"""
console.clear();
function filter_apply(filter) {
var f=eval(filter);
if (typeof(f.length) != "undefined") {} else {};
if (typeof(f.top) != "undefined") {f=f.top(Infinity);} else {};
if (typeof(f.dimension) != "undefined") {
f=f.dimension(function(d) { return "";}).top(Infinity);
} else {};
return f;
}
function print_filter(filter){
f = filter_apply(filter);
console.log(
filter + "(" + f.length + ") = " +
JSON.stringify(f)
);
}
// Create Global Variables
/*var RowChart = dc.rowChart("#rowchart");
var LineChart = dc.lineChart("#linechart");*/
var DataTable = dc.dataTable("#data-table");
// Load data
var dataset = {{ data }};
var br_states = {{ br_states }};
var flu_colors = {
1: '#dfdfdf',
2: '#ffcc00',
3: '#ff0000',
4: '#00ff00',
};
function style_map(feature) {
return {
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.3,
fillColor: '#ff0000'
};
}
// Create function
function Graph(data) {
var map = L.map('map');
map.setView([-16, -50.528742], 3);
// create the tile layer with correct attribution
var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
var osmAttrib='Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors';
var osm = new L.TileLayer(osmUrl, {minZoom: 1, maxZoom: 12, attribution: osmAttrib});
function onEachFeature(feature, layer) {
//bind click
layer.on({
click: function(){
// reset line weight
geojson_layer.eachLayer(function (_layer) {
_layer.setStyle({
weight: 1
});
});
// bold the selected state
layer.setStyle({
weight: 2
});
$('#selected_state').val(feature.properties.nome);
}
});
}
// Feed it through crossfilter
var ndx = crossfilter(data);
// for testing
//console.log(data);
//define a dimension
//Here we will group by state
var stateDim = ndx.dimension(dc.pluck('state'));
var weekDim = ndx.dimension(dc.pluck('w'));
// start the map in South-East England
geojson_layer = L.geoJson(br_states, {
onEachFeature: onEachFeature,
style: function(feature) {
l_name = feature.properties.nome;
df = filter_apply(weekDim.filter($('#week').val()));
result = {};
$(df).each(function(i){
if (l_name == df[i]['state']){
result = {
fillColor: flu_colors[df[i]['alert']],
color: '#333333',
opacity: 1,
weight: 1
};
return;
}
});
return result;
}
});
geojson_layer.addTo(map);
osm.addTo(map)
d3.select('#week')
.on('change', function(){
df = filter_apply(
weekDim.filter($('#week').val()));
//print_filter(df);
geojson_layer.eachLayer(function (layer) {
l_name = layer.feature.properties.nome;
$(df).each(function(i){
if (l_name == df[i]['state']){
layer.setStyle({
fillColor: flu_colors[df[i]['alert']],
color: '#333333',
opacity: 1
});
}
});
});
dc.renderAll(); // render all charts on the page
});
//Here we group by state and sum on column population
var stateGroup = stateDim.group().reduceSum(dc.pluck('incidence'));
/*
//Lets create a row chart
RowChart.dimension(stateDim)
.group(stateGroup)
.width(500);
RowChart.getColor = function(d, i){
var flu_colors = {
1: '#dfdfdf',
2: '#ffcc00',
3: '#ff0000',
4: '#00ff00',
};
return flu_colors[d.alert];
};
*/
DataTable.dimension(weekDim)
// data table does not use crossfilter group but rather a closure
// as a grouping function
.group(function(d) {
return '';
})
.columns([
dc.pluck('state'),
dc.pluck('incidence'),
dc.pluck('age'),
dc.pluck('alert'),
])
.size(100)
.sortBy(dc.pluck('state'))
.order(d3.ascending);
dc.renderAll(); // render all charts on the page
}; // end graph function
// Call function
Graph(dataset);
""")
In [9]:
# bind date to js template
body_js = Javascript(dc_template.render(
data=df.to_json(orient='records'),
br_states=br_states
))
# push data to notebook
inline_dc(
head.data + body_html.data +
"<script>" + body_js.data + "</script>"
)
Out[9]:
http://www.codeproject.com/Articles/693841/Making-Dashboards-with-Dc-js-Part-1-Using-Crossfil
http://gis.stackexchange.com/questions/121482/click-events-with-leaflet-and-geojson
https://geobrainstorms.wordpress.com/2014/03/31/mapas-tematicos-usando-leaflet/
http://stackoverflow.com/questions/23676236/filtering-crossfilter-dimension-using-dropdown
http://leafletjs.com/examples/choropleth.html
https://github.com/dc-js/dc.js/blob/master/web/docs/api-latest.md
http://gis.stackexchange.com/questions/121482/click-events-with-leaflet-and-geojson
http://gis.stackexchange.com/questions/75590/setstyle-function-for-geojson-features-leaflet