Compare commits
6 Commits
master
...
histograms
Author | SHA1 | Date | |
---|---|---|---|
|
2b61a508b2 | ||
|
a27b3aa503 | ||
|
a8c1bf7682 | ||
|
eaba6ee4dc | ||
|
26d0d40e35 | ||
|
c27b2471da |
@ -1,16 +1,32 @@
|
||||
|
||||
<html>
|
||||
<link rel="stylesheet" href="vendor/leaflet.css" />
|
||||
<link rel="stylesheet" href="vendor/cartodb.css" />
|
||||
<style>
|
||||
#map, html, body {
|
||||
width: 100%; height: 100%; padding: 0; margin: 0;
|
||||
}
|
||||
#charts{
|
||||
width: 25%;
|
||||
min-height: 200px;
|
||||
position:absolute;
|
||||
right:20px;
|
||||
top:10px;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<div id='charts'></div>
|
||||
|
||||
<script src="vendor/leaflet.js"></script>
|
||||
<script src="vendor/leaflet-hash.js"></script>
|
||||
<script src="../dist/torque.full.uncompressed.js"></script>
|
||||
|
||||
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js'></script>
|
||||
<script src='https://code.jquery.com/jquery-1.11.3.js'></script>
|
||||
<script src='vendor/underscore.js'></script>
|
||||
<script src='vendor/backbone.js'></script>
|
||||
<script src='vendor/histogram-widget.js'></script>
|
||||
|
||||
<script>
|
||||
// define the torque layer style using cartocss
|
||||
@ -18,49 +34,142 @@
|
||||
'Map {',
|
||||
'-torque-time-attribute: "date";',
|
||||
'-torque-aggregation-function: "count(cartodb_id)";',
|
||||
'-torque-frame-count: 760;',
|
||||
'-torque-animation-duration: 15;',
|
||||
'-torque-resolution: 2',
|
||||
'-torque-frame-count: 1;',
|
||||
'-torque-animation-duration: 1;',
|
||||
'-torque-resolution: 0.25',
|
||||
// '@8: #15f4ee;',
|
||||
// '@10: white',
|
||||
'}',
|
||||
'#layer {',
|
||||
' marker-width: 3;',
|
||||
' marker-fill-opacity: 0.8;',
|
||||
' marker-fill: #FEE391; ',
|
||||
' comp-op: "lighten";',
|
||||
' [value > 2] { marker-fill: #FEC44F; }',
|
||||
' [value > 3] { marker-fill: #FE9929; }',
|
||||
' [value > 4] { marker-fill: #EC7014; }',
|
||||
' [value > 5] { marker-fill: #CC4C02; }',
|
||||
' [value > 6] { marker-fill: #993404; }',
|
||||
' [value > 7] { marker-fill: #662506; }',
|
||||
' [frame-offset = 1] { marker-width: 10; marker-fill-opacity: 0.05;}',
|
||||
' [frame-offset = 2] { marker-width: 15; marker-fill-opacity: 0.02;}',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
|
||||
'marker-opacity: 0.2;',
|
||||
'image-filters: colorize-alpha(#078c88,#15f4ee,#b7fcfa,#f8fffe);',
|
||||
'marker-line-width: 0;',
|
||||
'marker-fill: #2E5387;',
|
||||
'marker-allow-overlap: true;',
|
||||
'marker-width: 1;',
|
||||
//'[zoom=4]{marker-width: 0.25;}',
|
||||
//'[zoom=5]{marker-width: 0.75;}',
|
||||
//'[zoom=6]{marker-width: 1.25;}',
|
||||
//'[zoom=7]{marker-width: 1.5;}',
|
||||
//'[zoom=8]{marker-width: 1.75;}',
|
||||
//'[zoom=9]{marker-width: 2.0;}',
|
||||
//'[zoom=10]{marker-width: 2.25;}',
|
||||
// '[zoom=11]{marker-width: 2.5;marker-opacity:0.3;}',
|
||||
// '[zoom=12]{marker-width: 2.75;marker-opacity:0.4;}',
|
||||
// '[zoom=13]{marker-width: 3.0;marker-opacity:0.45;}',
|
||||
// '[zoom=14]{marker-width: 3.25;marker-opacity:0.5;}',
|
||||
// '[zoom=15]{marker-width: 0.75;marker-opacity:0.55;}',
|
||||
// '[zoom=16]{marker-width: 4.0;marker-opacity:0.6;}',
|
||||
// '[zoom>=17]{marker-width: 4.25;marker-opacity:0.65;}',
|
||||
|
||||
/*
|
||||
|
||||
|
||||
' [value > 2] { marker-fill: #FEC44F; }',
|
||||
' [value > 10] { marker-fill: #FE9929; }',
|
||||
' [value > 20] { marker-fill: #EC7014; }',
|
||||
' [value > 30] { marker-fill: #CC4C02; }',
|
||||
' [value > 40] { marker-fill: #993404; }',
|
||||
' [value > 50] { marker-fill: #662506; }',
|
||||
*/
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
// for(var i =0; i <255; i += 10){
|
||||
// var color = d3.scale.linear()
|
||||
// .range()
|
||||
// .range(["steelblue", "brown"])
|
||||
// .interpolate(d3.interpolateHcl);
|
||||
// CARTOCSS.push("[value >= "+i+"]{marker-fill:"+color(i)+"};")
|
||||
// }
|
||||
|
||||
CARTOCSS.push("}")
|
||||
CARTOCSS= CARTOCSS.join(" ")
|
||||
|
||||
console.log(CARTOCSS)
|
||||
|
||||
/*
|
||||
['Map {,',
|
||||
'-torque-time-attribute: "date";,',
|
||||
'-torque-aggregation-function: "count(cartodb_id)";,',
|
||||
'-torque-frame-count: 6;,',
|
||||
'-torque-animation-duration: 15;,',
|
||||
'-torque-resolution: 1,',
|
||||
'},',
|
||||
'#layer {,',
|
||||
'marker-width: 1;,',
|
||||
'marker-fill-opacity: 1.0;,',
|
||||
'marker-fill: #FF2900; ,',
|
||||
'[ value <= 20] { marker-fill: #0C2C84; } ',
|
||||
'[ value <= 8] { marker-fill: #225EA8; }',
|
||||
'[ value <= 4] { marker-fill: #1D91C0; }',
|
||||
'[ value <= 3] { marker-fill: #41B6C4; }',
|
||||
'[ value <= 2] { marker-fill: #7FCDBB; }',
|
||||
'[ value <= 1] { marker-fill: #C7E9B4; }',
|
||||
'[ value <= 0.1] { marker-fill: #FFFFCC; }',
|
||||
'}'].join('\n')
|
||||
*/
|
||||
|
||||
|
||||
var map = new L.Map('map', {
|
||||
zoomControl: true,
|
||||
center: [40, 0],
|
||||
zoom: 3
|
||||
zoom: 0
|
||||
});
|
||||
var hash = new L.Hash(map);
|
||||
|
||||
L.tileLayer('http://{s}.api.cartocdn.com/base-dark/{z}/{x}/{y}.png', {
|
||||
L.tileLayer('http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', {
|
||||
attribution: 'CartoDB'
|
||||
}).addTo(map);
|
||||
|
||||
var torqueLayer = new L.TorqueLayer({
|
||||
tileJSON: "http://localhost:4000/examples/tilejson.json",
|
||||
cartocss: CARTOCSS
|
||||
tileJSON: "http://bigdata.int.cartodb.net:8888/tile.json",
|
||||
//tileJSON: "http://10.0.180.5:1234/",
|
||||
cartocss: CARTOCSS,
|
||||
valueDataType: Float32Array
|
||||
});
|
||||
torqueLayer.error(function(err){
|
||||
for(error in err){
|
||||
console.warn(err[error]);
|
||||
console.warn([error]);
|
||||
}
|
||||
});
|
||||
torqueLayer.addTo(map);
|
||||
torqueLayer.play()
|
||||
torqueLayer.setStep(0);
|
||||
|
||||
|
||||
//torqueLayer.play()
|
||||
window.torqueLayer = torqueLayer;
|
||||
|
||||
function createHistogram(variable){
|
||||
var div_id = "histogram_"+variable;
|
||||
$("#charts").append("<svg id='"+div_id+"'><g class='Canvas'></g></svg>")
|
||||
|
||||
var chart = new cdb.geo.ui.Widget.Histogram.Chart(
|
||||
{el:$("#"+div_id),
|
||||
data:{values: [1,3,4,5,22,1,3], labels:[22, 34, 45, 67,100, 200, 300]},
|
||||
width: 300,
|
||||
height:100,
|
||||
y:0,
|
||||
x:0})
|
||||
chart.render()
|
||||
chart.show()
|
||||
chart.bind("fliterChanged",function(start,end){
|
||||
console.log('setting filter', start,end )
|
||||
torqueLayer.setQueryFilter(variable, [start,end] )
|
||||
})
|
||||
|
||||
torqueLayer.addEventListener("histogramAdded",function(data){
|
||||
var data = torqueLayer.histogramForVariable(variable)
|
||||
chart.reset(data)
|
||||
})
|
||||
}
|
||||
|
||||
createHistogram(0)
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
1894
examples/vendor/backbone.js
vendored
Normal file
1894
examples/vendor/backbone.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
examples/vendor/cartodb.css
vendored
Normal file
2
examples/vendor/cartodb.css
vendored
Normal file
File diff suppressed because one or more lines are too long
55281
examples/vendor/cartodb.uncompressed.js
vendored
Normal file
55281
examples/vendor/cartodb.uncompressed.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1277
examples/vendor/histogram-widget.js
vendored
Normal file
1277
examples/vendor/histogram-widget.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
146
examples/vendor/leaflet-hash.js
vendored
Normal file
146
examples/vendor/leaflet-hash.js
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
(function(window) {
|
||||
var HAS_HASHCHANGE = (function() {
|
||||
var doc_mode = window.documentMode;
|
||||
return ('onhashchange' in window) &&
|
||||
(doc_mode === undefined || doc_mode > 7);
|
||||
})();
|
||||
|
||||
L.Hash = function(map) {
|
||||
this.onHashChange = L.Util.bind(this.onHashChange, this);
|
||||
|
||||
if (map) {
|
||||
this.init(map);
|
||||
}
|
||||
};
|
||||
|
||||
L.Hash.prototype = {
|
||||
map: null,
|
||||
lastHash: null,
|
||||
|
||||
parseHash: function(hash) {
|
||||
if(hash.indexOf('#') == 0) {
|
||||
hash = hash.substr(1);
|
||||
}
|
||||
var args = hash.split("/");
|
||||
if (args.length == 3) {
|
||||
var zoom = parseInt(args[0], 10),
|
||||
lat = parseFloat(args[1]),
|
||||
lon = parseFloat(args[2]);
|
||||
if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) {
|
||||
return false;
|
||||
} else {
|
||||
return {
|
||||
center: new L.LatLng(lat, lon),
|
||||
zoom: zoom
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
formatHash: function(map) {
|
||||
var center = map.getCenter(),
|
||||
zoom = map.getZoom(),
|
||||
precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
|
||||
|
||||
return "#" + [zoom,
|
||||
center.lat.toFixed(precision),
|
||||
center.lng.toFixed(precision)
|
||||
].join("/");
|
||||
},
|
||||
|
||||
init: function(map) {
|
||||
this.map = map;
|
||||
|
||||
this.map.on("moveend", this.onMapMove, this);
|
||||
|
||||
// reset the hash
|
||||
this.lastHash = null;
|
||||
this.onHashChange();
|
||||
|
||||
if (!this.isListening) {
|
||||
this.startListening();
|
||||
}
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
this.map = null;
|
||||
if (this.isListening) {
|
||||
this.stopListening();
|
||||
}
|
||||
},
|
||||
|
||||
onMapMove: function(map) {
|
||||
// bail if we're moving the map (updating from a hash),
|
||||
// or if the map has no zoom set
|
||||
|
||||
if (this.movingMap || this.map.getZoom() === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var hash = this.formatHash(this.map);
|
||||
if (this.lastHash != hash) {
|
||||
location.replace(hash);
|
||||
this.lastHash = hash;
|
||||
}
|
||||
},
|
||||
|
||||
movingMap: false,
|
||||
update: function() {
|
||||
var hash = location.hash;
|
||||
if (hash === this.lastHash) {
|
||||
// console.info("(no change)");
|
||||
return;
|
||||
}
|
||||
var parsed = this.parseHash(hash);
|
||||
if (parsed) {
|
||||
// console.log("parsed:", parsed.zoom, parsed.center.toString());
|
||||
this.movingMap = true;
|
||||
|
||||
this.map.setView(parsed.center, parsed.zoom);
|
||||
|
||||
this.movingMap = false;
|
||||
} else {
|
||||
// console.warn("parse error; resetting:", this.map.getCenter(), this.map.getZoom());
|
||||
this.onMapMove(this.map);
|
||||
}
|
||||
},
|
||||
|
||||
// defer hash change updates every 100ms
|
||||
changeDefer: 100,
|
||||
changeTimeout: null,
|
||||
onHashChange: function() {
|
||||
// throttle calls to update() so that they only happen every
|
||||
// `changeDefer` ms
|
||||
if (!this.changeTimeout) {
|
||||
var that = this;
|
||||
this.changeTimeout = setTimeout(function() {
|
||||
that.update();
|
||||
that.changeTimeout = null;
|
||||
}, this.changeDefer);
|
||||
}
|
||||
},
|
||||
|
||||
isListening: false,
|
||||
hashChangeInterval: null,
|
||||
startListening: function() {
|
||||
if (HAS_HASHCHANGE) {
|
||||
L.DomEvent.addListener(window, "hashchange", this.onHashChange);
|
||||
} else {
|
||||
clearInterval(this.hashChangeInterval);
|
||||
this.hashChangeInterval = setInterval(this.onHashChange, 50);
|
||||
}
|
||||
this.isListening = true;
|
||||
},
|
||||
|
||||
stopListening: function() {
|
||||
if (HAS_HASHCHANGE) {
|
||||
L.DomEvent.removeListener(window, "hashchange", this.onHashChange);
|
||||
} else {
|
||||
clearInterval(this.hashChangeInterval);
|
||||
}
|
||||
this.isListening = false;
|
||||
}
|
||||
};
|
||||
})(window);
|
14
examples/vendor/torque.category.css
vendored
Normal file
14
examples/vendor/torque.category.css
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
.torque-category .axis text {
|
||||
font: 10px sans-serif;
|
||||
fill: white;
|
||||
}
|
||||
.torque-category .bars rect {
|
||||
fill: steelblue;
|
||||
}
|
||||
.torque-category .axis path,
|
||||
.torque-category .axis line {
|
||||
fill: none;
|
||||
stroke: #FFF;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
140
examples/vendor/torque.category.js
vendored
Normal file
140
examples/vendor/torque.category.js
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
|
||||
// histogram widget for torque
|
||||
// d3 based
|
||||
|
||||
function categoryChart() {
|
||||
var margin = {top: 0, right: 0, bottom: 20, left: 0},
|
||||
width = 960,
|
||||
height = 500;
|
||||
|
||||
var histogram = d3.layout.histogram(),
|
||||
x = d3.scale.ordinal(),
|
||||
y = d3.scale.linear(),
|
||||
xAxis = d3.svg.axis().scale(x).orient("bottom").tickSize(6, 0);
|
||||
|
||||
function chart(selection) {
|
||||
|
||||
var colors = ['#0000b4','#0082ca','#0094ff','#0d4bcf','#0066AE','#074285','#00187B','#285964','#405F83','#416545','#4D7069','#6E9985','#7EBC89','#0283AF','#79BCBF','#99C19E'];
|
||||
|
||||
selection.each(function(data) {
|
||||
// Compute the histogram.
|
||||
// data = histogram(data);
|
||||
|
||||
// Update the x-scale.
|
||||
x .domain(data.map(function(d) { return d[0]; }))
|
||||
.rangeRoundBands([0, width - margin.left - margin.right], .1);
|
||||
|
||||
// Update the y-scale.
|
||||
y .domain([])
|
||||
.range([height - margin.top - margin.bottom, 0]);
|
||||
|
||||
colorScale = d3.scale.quantize()
|
||||
.domain([0,data.map(function(d){return d[0]}).length])
|
||||
.range(colors);
|
||||
|
||||
// Select the svg element, if it exists.
|
||||
|
||||
var svg = d3.select(this).selectAll("svg")
|
||||
.attr('class', 'torque-histogram')
|
||||
.data([data]);
|
||||
|
||||
// Otherwise, create the skeletal chart.
|
||||
var gEnter = svg.enter().append("svg").append("g");
|
||||
gEnter.append("g").attr("class", "bars");
|
||||
gEnter.append("g").attr("class", "x axis");
|
||||
|
||||
// Update the outer dimensions.
|
||||
svg .attr("width", width)
|
||||
.attr("height", height);
|
||||
|
||||
// Update the inner dimensions.
|
||||
var g = svg.select("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
// Update the bars.
|
||||
var bar = svg.select(".bars").selectAll(".bar").data(data);
|
||||
bar.enter().append("rect").attr('class', 'bar');
|
||||
bar.exit().remove();
|
||||
bar .attr("width", x.rangeBand())
|
||||
.attr("x", function(d) { return x(d[0]); })
|
||||
.attr("y", function(d) { return y(d[1]); })
|
||||
.attr("height", function(d) { return y.range()[0] - y(d[1]); })
|
||||
.order();
|
||||
|
||||
// Update the x-axis.
|
||||
g.select(".x.axis")
|
||||
.attr("transform", "translate(0," + y.range()[0] + ")")
|
||||
.call(xAxis);
|
||||
});
|
||||
}
|
||||
|
||||
chart.margin = function(_) {
|
||||
if (!arguments.length) return margin;
|
||||
margin = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.width = function(_) {
|
||||
if (!arguments.length) return width;
|
||||
width = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.height = function(_) {
|
||||
if (!arguments.length) return height;
|
||||
height = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
// Expose the histogram's value, range and bins method.
|
||||
d3.rebind(chart, histogram, "value", "range", "bins");
|
||||
|
||||
// Expose the x-axis' tickFormat method.
|
||||
d3.rebind(chart, xAxis, "tickFormat");
|
||||
|
||||
return chart;
|
||||
}
|
||||
|
||||
torque.widgets = torque.widgets || {}
|
||||
|
||||
/**
|
||||
* creates an histogram inside the element based on torque data
|
||||
* @id any d3 valid selector
|
||||
* @torqueLayer torque layer objecy
|
||||
* @returns histogram widget object
|
||||
*/
|
||||
torque.widgets.category = function(id, torqueLayer, variable) {
|
||||
|
||||
function updateVategories() {
|
||||
var values = torqueLayer.totalHistogramFor(variable, 0);
|
||||
var el = d3.select(id)
|
||||
.datum(values)
|
||||
|
||||
var size = el.node().getBoundingClientRect()
|
||||
|
||||
el.call(histogramChart()
|
||||
.width(size.width)
|
||||
.height(size.height)
|
||||
.bins(10)
|
||||
.tickFormat(d3.format(".0f"))
|
||||
);
|
||||
};
|
||||
|
||||
// public API
|
||||
var h = {
|
||||
disable: function() {
|
||||
// torqueLayer.off('change:time', updateHistogram);
|
||||
// torqueLayer.off('tilesLoaded', updateHistogram);
|
||||
// torqueLayer.off('tileLoaded', updateHistogram);
|
||||
torqueLayer.off('histLoaded', updateHistogram);
|
||||
},
|
||||
enable: function() {
|
||||
// torqueLayer.on('change:time', updateHistogram);
|
||||
// torqueLayer.on('tilesLoaded', updateHistogram);
|
||||
// torqueLayer.on('tileLoaded', updateHistogram);
|
||||
torqueLayer.on('histLoaded', updateHistogram);
|
||||
}
|
||||
}
|
||||
h.enable();
|
||||
return h;
|
||||
}
|
1548
examples/vendor/underscore.js
vendored
Normal file
1548
examples/vendor/underscore.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -25,6 +25,8 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
||||
if (!torque.isBrowserSupported()) {
|
||||
throw new Error("browser is not supported by torque");
|
||||
}
|
||||
|
||||
this._histograms = []
|
||||
options.tileLoader = true;
|
||||
this.key = 0;
|
||||
this.prevRenderedKey = 0;
|
||||
@ -93,6 +95,10 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
||||
// don't load tiles that are not being shown
|
||||
if (t.zoom !== self._map.getZoom()) return;
|
||||
self._tileLoaded(t, tileData);
|
||||
if(Object.keys(tileData.histogram).length > 0){
|
||||
self._addTileHistogram(t.x,t.y,t.zoom, tileData.histogram)
|
||||
}
|
||||
|
||||
self._clearTileCaches();
|
||||
if (tileData) {
|
||||
self.redraw();
|
||||
@ -102,7 +108,66 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
||||
}, this);
|
||||
|
||||
},
|
||||
_invalidateHistograms:function(){
|
||||
this._histograms= undefined
|
||||
},
|
||||
_addTileHistogram:function(x, y, zoom, histogram){
|
||||
|
||||
this._histograms[zoom] = this._histograms[zoom] ? this._histograms[zoom] : []
|
||||
this._histograms[zoom][x] = this._histograms[zoom][x] ? this._histograms[zoom][x] : []
|
||||
this._histograms[zoom][x][y] = histogram
|
||||
this.fire('histogramAdded')
|
||||
},
|
||||
histogramForVariable:function(variable){
|
||||
histogramParts=[]
|
||||
Object.keys(this._tiles).forEach(function(t){
|
||||
var coord = t.split(":")
|
||||
if(this._histograms[coord[2]][coord[0]][coord[1]]){
|
||||
histogramParts.push(this._histograms[coord[2]][coord[0]][coord[1]][variable])
|
||||
}
|
||||
}.bind(this))
|
||||
return this._combineHistograms(histogramParts)
|
||||
},
|
||||
_transfromHistogram:function(hist,zoom){
|
||||
var new_hist= {
|
||||
bins: {},
|
||||
bounds: hist.bounds,
|
||||
zoom : zoom,
|
||||
x: 0
|
||||
}
|
||||
var zoom_diff = zoom - hist.zoom;
|
||||
new_hist.x = hist.x >> zoom_diff;
|
||||
for( var k in hist.bins){
|
||||
var idx = (hist.x + k ) >> (zoom_diff - new_hist.x)
|
||||
new_hist.bins[idx] = new_hist.bins[idx] || 0
|
||||
new_hist.bins[idx] += hist.bins[k]
|
||||
}
|
||||
return new_hist
|
||||
},
|
||||
_combineHistograms:function(histograms){
|
||||
var minZoom = Math.min.apply(null,histograms.map(function(h){return h.zoom}))
|
||||
var mappedHistograms = histograms.map(function(h){return this._transfromHistogram(h,minZoom)}.bind(this))
|
||||
|
||||
var combinedHistogram = {
|
||||
bins:{},
|
||||
bounds: [
|
||||
Math.min.apply(null,mappedHistograms.map(function(h){return h.bounds[0]})),
|
||||
Math.max.apply(null,mappedHistograms.map(function(h){return h.bounds[1]}))
|
||||
],
|
||||
zoom: minZoom,
|
||||
x: 0
|
||||
}
|
||||
|
||||
mappedHistograms.forEach(function(histogram){
|
||||
Object.keys(histogram.bins).forEach(function(bin){
|
||||
combinedHistogram.bins[bin] = combinedHistogram.bins[bin] || 0
|
||||
combinedHistogram.bins[bin]+= histogram.bins[bin]
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
return combinedHistogram
|
||||
},
|
||||
_clearTileCaches: function() {
|
||||
var t, tile;
|
||||
for(t in this._tiles) {
|
||||
@ -332,6 +397,11 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
||||
return this.provider.getKeySpan();
|
||||
},
|
||||
|
||||
setQueryFilter:function(variable, start,end){
|
||||
this.provider.setFilter(variable,start,end)
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* set the cartocss for the current renderer
|
||||
*/
|
||||
|
@ -19,6 +19,7 @@
|
||||
var tileJSON = function (options) {
|
||||
this._ready = false;
|
||||
this._tileQueue = [];
|
||||
this._filters = {}
|
||||
this.options = options;
|
||||
|
||||
this.options.coordinates_data_type = this.options.coordinates_data_type || Uint8Array;
|
||||
@ -39,6 +40,23 @@
|
||||
|
||||
NAME: "tileJSON",
|
||||
|
||||
filterRange: function(attrIndex, min, max) {
|
||||
this.filter = attrIndex + "=" + min + "," + max;
|
||||
this.reload()
|
||||
},
|
||||
|
||||
filterExact: function(attrIndex, exact) {
|
||||
this.filter = attrIndex + "=" + exact
|
||||
this.reload()
|
||||
},
|
||||
|
||||
_setFilter:function(variable, start,end){
|
||||
this._filters[variable] = {type:'range', start:start,end:end)
|
||||
this.reload();
|
||||
},
|
||||
_removefilter:function(variable){
|
||||
this._filters[variable] = undefined
|
||||
},
|
||||
/**
|
||||
* return the torque tile encoded in an efficient javascript
|
||||
* structure:
|
||||
@ -48,10 +66,43 @@
|
||||
* Index: Array index to the properties
|
||||
* }
|
||||
*/
|
||||
proccessTile: function(rows, coord, zoom) {
|
||||
createProccessTileWorker:function(){
|
||||
var workerFunction = "var proccessTile ="+ this.proccessTileSerial.toString()
|
||||
var wrapper = "; self.onmessage = function(e){var data = JSON.parse(e.data); JSON.stringify(self.postMessage(proccessTile(data.response,data.coord, data.zoom, data.options)))}"
|
||||
var script = workerFunction + wrapper;
|
||||
var blob = new Blob([script], {type: "text/javascript"})
|
||||
var worker = new Worker(window.URL.createObjectURL(blob))
|
||||
return worker
|
||||
},
|
||||
|
||||
proccessTile:function(response,coord,zoom,callback){
|
||||
if(typeof(Worker) === "undefined"){
|
||||
callback(this.proccessTileSerial(response,coord,zoom, this.options))
|
||||
}
|
||||
else{
|
||||
var worker = this.createProccessTileWorker()
|
||||
worker.onmessage = function(e){
|
||||
callback(e.data)
|
||||
worker.terminate()
|
||||
}
|
||||
|
||||
var workerSafeOptions= {
|
||||
cumulative: this.options.cumulative,
|
||||
valueDataType: this.options.valueDataType,
|
||||
resolution: this.options.resolution,
|
||||
}
|
||||
worker.postMessage(JSON.stringify({response: response, coord: {x:coord.x,y:coord.y}, zoom:zoom, options: workerSafeOptions}))
|
||||
}
|
||||
},
|
||||
|
||||
proccessTileSerial: function(response, coord, zoom,options) {
|
||||
var r;
|
||||
var x = new this.options.coordinates_data_type(rows.length);
|
||||
var y = new this.options.coordinates_data_type(rows.length);
|
||||
var data = JSON.parse(response)
|
||||
var rows = data.pixels
|
||||
var histograms = data.histograms
|
||||
|
||||
var x = options.x || new Uint8Array(rows.length);
|
||||
var y = options.y || new Uint8Array(rows.length);
|
||||
|
||||
// count number of dates
|
||||
var dates = 0;
|
||||
@ -64,16 +115,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
if(this.options.cumulative) {
|
||||
if(options.cumulative) {
|
||||
dates = (1 + maxDateSlots) * rows.length;
|
||||
}
|
||||
|
||||
var type = this.options.cumulative ? Uint32Array: Uint8ClampedArray;
|
||||
var type = options.cumulative ? Uint32Array: Uint8ClampedArray;
|
||||
|
||||
// reserve memory for all the dates
|
||||
var timeIndex = new Int32Array(maxDateSlots + 1); //index-size
|
||||
var timeCount = new Int32Array(maxDateSlots + 1);
|
||||
var renderData = new (this.options.valueDataType || type)(dates);
|
||||
var renderData = new (options.valueDataType || type)(dates);
|
||||
var renderDataPos = new Uint32Array(dates);
|
||||
|
||||
var rowsPerSlot = {};
|
||||
@ -81,15 +132,15 @@
|
||||
// precache pixel positions
|
||||
for (var r = 0; r < rows.length; ++r) {
|
||||
var row = rows[r];
|
||||
x[r] = row.x__uint8 * this.options.resolution;
|
||||
y[r] = row.y__uint8 * this.options.resolution;
|
||||
x[r] = row.x__uint8 * options.resolution;
|
||||
y[r] = row.y__uint8 * options.resolution;
|
||||
|
||||
var dates = row.dates__uint16;
|
||||
var vals = row.vals__uint8;
|
||||
if (!this.options.cumulative) {
|
||||
if (!options.cumulative) {
|
||||
for (var j = 0, len = dates.length; j < len; ++j) {
|
||||
var rr = rowsPerSlot[dates[j]] || (rowsPerSlot[dates[j]] = []);
|
||||
if(this.options.cumulative) {
|
||||
if(options.cumulative) {
|
||||
vals[j] += prev_val;
|
||||
}
|
||||
prev_val = vals[j];
|
||||
@ -148,11 +199,12 @@
|
||||
timeIndex: timeIndex,
|
||||
renderDataPos: renderDataPos,
|
||||
renderData: renderData,
|
||||
maxDate: maxDateSlots
|
||||
maxDate: maxDateSlots,
|
||||
histogram: histograms
|
||||
};
|
||||
},
|
||||
|
||||
setSteps: function(steps, opt) {
|
||||
setSteps: function(steps, opt) {
|
||||
opt = opt || {};
|
||||
if (this.options.steps !== steps) {
|
||||
this.options.steps = steps;
|
||||
@ -249,15 +301,18 @@
|
||||
var limit_x = Math.pow(2, zoom);
|
||||
var corrected_x = ((coord.x % limit_x) + limit_x) % limit_x;
|
||||
var index = Math.abs(corrected_x + coord.y) % subdomains.length;
|
||||
var url = this.templateUrl
|
||||
var url = this.tileUrls[(corrected_x + coord.y) % this.tileUrls.length]
|
||||
.replace('{x}', corrected_x)
|
||||
.replace('{y}', coord.y)
|
||||
.replace('{z}', zoom)
|
||||
.replace('{s}', subdomains[index])
|
||||
if (this.filter) {
|
||||
url += "?" + this.filter
|
||||
}
|
||||
torque.net.get( url , function (data) {
|
||||
if (data && data.responseText) {
|
||||
var rows = JSON.parse(data.responseText);
|
||||
callback(self.proccessTile(rows, coord, zoom));
|
||||
|
||||
self.proccessTile(data.responseText, coord, zoom,callback.bind(self));
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
@ -314,6 +369,7 @@
|
||||
|
||||
_fetchMap: function(callback) {
|
||||
var self = this;
|
||||
|
||||
torque.net.get(this.options.tileJSON, function (data) {
|
||||
data = JSON.parse(data.response);
|
||||
if (data) {
|
||||
@ -325,6 +381,7 @@
|
||||
self.options[k] = data[k];
|
||||
}
|
||||
self.templateUrl = data.tiles[0];
|
||||
self.tileUrls = data.tiles;
|
||||
if (self.templateUrl.indexOf("http") !== 0){
|
||||
self.templateUrl = self.options.tileJSON.substring(0, self.options.tileJSON.lastIndexOf("/") + 1) + self.templateUrl;
|
||||
}
|
||||
@ -334,4 +391,4 @@
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = tileJSON;
|
||||
module.exports = tileJSON;
|
||||
|
@ -44,14 +44,16 @@ function getTile(jsonRelPath, cartocss, z, x, y, step, callback) {
|
||||
|
||||
var canvas = new Canvas(256, 256);
|
||||
var pointRenderer = new torque.renderer.Point(canvas, rendererOptions);
|
||||
provider.proccessTile(rows, {x: x, y: y}, z, function(tile){
|
||||
pointRenderer.renderTile(tile, step, function(err) {
|
||||
if (err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
pointRenderer.applyFilters();
|
||||
return callback(null, canvas);
|
||||
});
|
||||
}.bind(this));
|
||||
|
||||
pointRenderer.renderTile(provider.proccessTile(rows, {x: x, y: y}, z), step, function(err) {
|
||||
if (err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
pointRenderer.applyFilters();
|
||||
return callback(null, canvas);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
Loading…
Reference in New Issue
Block a user