commit
7ea3709bee
1
NEWS
1
NEWS
@ -5,6 +5,7 @@
|
|||||||
- Add no_cdn option to windshaft provider
|
- Add no_cdn option to windshaft provider
|
||||||
- Add getActivePointsBBox method
|
- Add getActivePointsBBox method
|
||||||
- Added renderer cache when the map is not animated
|
- Added renderer cache when the map is not animated
|
||||||
|
- Added interaction method
|
||||||
|
|
||||||
2.4.01
|
2.4.01
|
||||||
- fixed rectangle anchor and size #42
|
- fixed rectangle anchor and size #42
|
||||||
|
121
doc/API.md
121
doc/API.md
@ -1,13 +1,6 @@
|
|||||||
|
|
||||||
# Torque API
|
# Torque API
|
||||||
|
|
||||||
Torque provides two kinds of visualizations.
|
|
||||||
|
|
||||||
- static: provides a way to create heatmap like visualizations (note for Andrew: fon). see ``TorqueLayer``
|
|
||||||
- dynamic: animate points over a map (note for Andrew: Navy) see ``TiledTorqueLayer``
|
|
||||||
|
|
||||||
|
|
||||||
depending on the map provider you are using you need to use different layer type. Currently we provide layers for Google Maps and Leaflet.
|
|
||||||
|
|
||||||
## L.TorqueLayer(options)
|
## L.TorqueLayer(options)
|
||||||
|
|
||||||
@ -20,12 +13,7 @@ One of two core classes for the Torque library - it is used to create an animate
|
|||||||
var torqueLayer = new L.TorqueLayer({
|
var torqueLayer = new L.TorqueLayer({
|
||||||
user : 'viz2',
|
user : 'viz2',
|
||||||
table : 'ow',
|
table : 'ow',
|
||||||
column : 'date',
|
cartocss: CARTOCSS
|
||||||
countby : 'count(cartodb_id)',
|
|
||||||
resolution : 1,
|
|
||||||
steps : 750,
|
|
||||||
pixel_size : 3,
|
|
||||||
blendmode : 'lighter'
|
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -34,37 +22,29 @@ One of two core classes for the Torque library - it is used to create an animate
|
|||||||
##### Provider options
|
##### Provider options
|
||||||
| Option | type | Default | Description |
|
| Option | type | Default | Description |
|
||||||
|-----------|:-----------|:----------|:---------------------------------------|
|
|-----------|:-----------|:----------|:---------------------------------------|
|
||||||
| provider | string | ```sql_api``` | Where is the data coming from? Alternative is 'url_template'|
|
| provider | string | ```sql_api``` | Where is the data coming from |
|
||||||
|
|
||||||
##### CartoDB data options
|
##### CartoDB data options (SQL API provider)
|
||||||
| Option | type | Default | Description |
|
| Option | type | Default | Description |
|
||||||
|-----------|:-----------|:----------|:---------------------------------------|
|
|-----------|:-----------|:----------|:---------------------------------------|
|
||||||
| user | string | ```null``` | CartoDB account name. Found as, accountname.cartodb.com|
|
| user | string | ```null``` | CartoDB account name. Found as, accountname.cartodb.com|
|
||||||
| table | string | ```null``` | CartoDB table name where data is found |
|
| table | string | ```null``` | CartoDB table name where data is found |
|
||||||
| column | string | ```null``` | CartoDB table's column name where date information is found (for dynamic type torque layer only)|
|
| sql | string | ```null``` | SQL query to be performed to fetch the data. You must
|
||||||
| countby | string | ```null``` | The aggregation method to use for each pixel displayed where multiple data are found. Any valid PostgreSQL aggregate function |
|
use this param or table, not at the same time |
|
||||||
|
|
||||||
|
|
||||||
##### Display options
|
|
||||||
| Option | type | Default | Description |
|
|
||||||
|-----------|:-----------|:----------|:---------------------------------------|
|
|
||||||
| resolution| numeric | ```2``` | The x and y dimensions of each pixel as returned by the data|
|
|
||||||
| blendmode | boolean | ```source-over``` | The HTML5 Canvas composite operation for when multiple pixels overlap on the canvas |
|
|
||||||
|
|
||||||
##### Time options
|
|
||||||
| Option | type | Default | Description |
|
|
||||||
|-----------|:-----------|:----------|:---------------------------------------|
|
|
||||||
| steps | integer | ```100``` | The maximun number of steps to divide the data into for animated renderings |
|
|
||||||
| animationDuration | integer | ```null``` | time in seconds the animation last |
|
|
||||||
|
|
||||||
### Time methods
|
### Time methods
|
||||||
|
|
||||||
| Method | options | returns | Description |
|
| Method | options | returns | Description |
|
||||||
|-----------|:-----------|:----------|:---------------------------------------|
|
|-----------|:-----------|:----------|:---------------------------------------|
|
||||||
| ```setStep(step)``` | ```time numeric``` | ```this``` | sets the animation to the step indicated by ```step```, must be between 0 and ```steps```|
|
| ```setStep(step)``` | ```time numeric``` | ```this``` | sets the animation to the step indicated by ```step```, must be between 0 and ```steps```|
|
||||||
| ```play```| | ```this```| starts the animation
|
| ```play()```| | ```this```| starts the animation
|
||||||
| ```stop```| | ```this```| stops the animation and set time to step 0
|
| ```stop()```| | ```this```| stops the animation and set time to step 0
|
||||||
| ```pause```| | ```this```| stops the animation but keep the current time (play enables the animation again)
|
| ```pause()```| | ```this```| stops the animation but keep the current time (play enables the animation again)
|
||||||
|
| ```toggle()```| | ```this```| toggles (pause/play) the animation
|
||||||
|
| ```getStep()``` | | current animation step (integer) | gets the current animation step
|
||||||
|
| ```getTime()``` | | current animation time (Date) | gets the real animation time
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Style methods
|
### Style methods
|
||||||
@ -97,63 +77,16 @@ This should be ```string``` encoded in Javascript
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## L.TiledTorqueLayer(options)
|
### Interaction methods (only available for Leaflet)
|
||||||
|
|
||||||
One of two core classes for the Torque library - it is used to create a static torque layer with client side filtering.
|
|
||||||
|
|
||||||
##### Provider options
|
|
||||||
| Option | type | Default | Description |
|
|
||||||
|-----------|:-----------|:----------|:---------------------------------------|
|
|
||||||
| provider | string | ```sql_api``` | Where is the data coming from? Alternative is 'url_template'|
|
|
||||||
| url | string | ```null``` | Tile template URL for fetching data e.g 'http://host.com/{z}/{x}/{y}.json'|
|
|
||||||
|
|
||||||
##### CartoDB data options (Note to Santana: are these really here?)
|
|
||||||
| Option | type | Default | Description |
|
|
||||||
|-----------|:-----------|:----------|:---------------------------------------|
|
|
||||||
| user | string | ```null``` | CartoDB account name. Found as, accountname.cartodb.com|
|
|
||||||
| table | string | ```null``` | CartoDB table name where data is found |
|
|
||||||
| column | string | ```null``` | CartoDB table's column name where date information is found (for dynamic type torque layer only)|
|
|
||||||
| countby | string | ```null``` | The aggregation method to use for each pixel displayed where multiple data are found. Any valid PostgreSQL aggregate function |
|
|
||||||
|
|
||||||
|
|
||||||
##### Display options (Note to Santana: is blendmode here? or above even?)
|
|
||||||
| Option | type | Default | Description |
|
|
||||||
|-----------|:-----------|:----------|:---------------------------------------|
|
|
||||||
| resolution| numeric | ```2``` | The x and y dimensions of each pixel as returned by the data|
|
|
||||||
| blendmode | boolean | ```source-over``` | The HTML5 Canvas composite operation for when multiple pixels overlap on the canvas |
|
|
||||||
|
|
||||||
### Filtering options
|
|
||||||
|
|
||||||
| Method | options | returns | Description |
|
| Method | options | returns | Description |
|
||||||
|-----------|:-----------|:----------|:---------------------------------------|
|
|-----------|:-----------|:----------|:---------------------------------------|
|
||||||
| ```setKey(keys)``` | ```keys numeric/array``` | ```this``` | which data categories to display on the map |
|
| ```getValueForPos(x, y[, step])```| | an object like { bbox:[], value: VALUE } if there is value
|
||||||
| ```setSQL(sql)``` | ```sql string ``` | ```this``` | by default sql torque layer uses is
|
for the pos, null otherwise | allows to get the value for the coordinate (in map reference system)
|
||||||
"select * from table", this method changes the sql query torque uses to fetch the data |
|
for a concrete step. If step is not specified the animation one is used. This method is expensive
|
||||||
|
in terms of CPU so be careful. It returns the value from the raster data not the rendered data |
|
||||||
|
| ```getActivePointsBBox(step)```| | list of bbox | returns the list of bounding boxes active for
|
||||||
|
``step``
|
||||||
|
|
||||||
### Style options
|
|
||||||
|
|
||||||
| Method | options | returns | Description |
|
|
||||||
|-----------|:-----------|:----------|:---------------------------------------|
|
|
||||||
| ```setCartoCSS(cartocss)``` | ```cartocss string``` | ```this``` | style the map rendering using client-side cartocss |
|
|
||||||
|
|
||||||
``value`` and ``zoom`` variables can be used. only ``polygon-fill`` and ``polygon-opacity`` properties are supported currently. To see the full list of supported parameters, read the [Torque CartoCSS documentation here](CartoCSS.md).
|
|
||||||
|
|
||||||
TorqueLayer currently expects ```polygon``` styling
|
|
||||||
|
|
||||||
##### CartoCSS Example
|
|
||||||
|
|
||||||
This should be ```string``` encoded in Javascript
|
|
||||||
|
|
||||||
```css
|
|
||||||
#layer {
|
|
||||||
polygon-fill: #FFFF00;
|
|
||||||
[value >= 10] { polygon-fill: #FFCC00; }
|
|
||||||
[value >= 100] { polygon-fill: #FF9900; }
|
|
||||||
[value >= 1000] { polygon-fill: #FF6600; }
|
|
||||||
[value >= 10000] { polygon-fill: #FF3300; }
|
|
||||||
[value > 100000] { polygon-fill: #C00; }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Google Maps Layers
|
# Google Maps Layers
|
||||||
|
|
||||||
@ -171,19 +104,3 @@ is not a layer is a overlay so in order to add it to the map use ``layer.setMap`
|
|||||||
see ``L.TorqueLayer`` for the rest of the options.
|
see ``L.TorqueLayer`` for the rest of the options.
|
||||||
|
|
||||||
|
|
||||||
## GMapsTiledTorqueLayer(options)
|
|
||||||
creates a static _overlay_ to use it with google maps.
|
|
||||||
|
|
||||||
```js
|
|
||||||
var torqueLayer = new torque.GMapsTiledTorqueLayer({
|
|
||||||
provider: 'url_template',
|
|
||||||
url: GBIF_URL,
|
|
||||||
resolution: 4,
|
|
||||||
});
|
|
||||||
|
|
||||||
torqueLayer.setMap(map);
|
|
||||||
|
|
||||||
torqueLayer.setKey([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]);
|
|
||||||
```
|
|
||||||
|
|
||||||
see ``L.TiledTorqueLayer`` for options reference
|
|
||||||
|
108
examples/leaflet_interaction.html
Normal file
108
examples/leaflet_interaction.html
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
|
||||||
|
<html>
|
||||||
|
<link rel="stylesheet" href="../vendor/leaflet.css" />
|
||||||
|
<style>
|
||||||
|
#map, html, body {
|
||||||
|
width: 100%; height: 100%; padding: 0; margin: 0;
|
||||||
|
}
|
||||||
|
#title {
|
||||||
|
position: absolute;
|
||||||
|
top: 100px;
|
||||||
|
left: 50px;
|
||||||
|
color: white;
|
||||||
|
font-size: 27px;
|
||||||
|
font-family: Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="map"></div>
|
||||||
|
<div id="title">Average temperature collected by Britain's Royal Navy (1913-1925)</div>
|
||||||
|
|
||||||
|
<script src="../vendor/leaflet.js"></script>
|
||||||
|
<script src="../vendor/underscore.js"></script>
|
||||||
|
<script src="../vendor/carto.js"></script>
|
||||||
|
<script src="../dist/torque.uncompressed.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// define the torque layer style using cartocss
|
||||||
|
// this creates a kind of density map
|
||||||
|
//color scale from http://colorbrewer2.org/
|
||||||
|
var CARTOCSS = [
|
||||||
|
'Map {',
|
||||||
|
'-torque-time-attribute: "date";',
|
||||||
|
'-torque-aggregation-function: "avg(temp::float)";',
|
||||||
|
'-torque-frame-count: 1;',
|
||||||
|
'-torque-animation-duration: 15;',
|
||||||
|
'-torque-resolution: 16',
|
||||||
|
'}',
|
||||||
|
'#layer {',
|
||||||
|
' marker-width: 8;',
|
||||||
|
' marker-fill-opacity: 1.0;',
|
||||||
|
' marker-fill: #fff5eb; ',
|
||||||
|
' marker-type: rectangle;',
|
||||||
|
' [value > 1] { marker-fill: #fee6ce; }',
|
||||||
|
' [value > 2] { marker-fill: #fdd0a2; }',
|
||||||
|
' [value > 4] { marker-fill: #fdae6b; }',
|
||||||
|
' [value > 10] { marker-fill: #fd8d3c; }',
|
||||||
|
' [value > 15] { marker-fill: #f16913; }',
|
||||||
|
' [value > 20] { marker-fill: #d94801; }',
|
||||||
|
' [value > 25] { marker-fill: #8c2d04; }',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
|
||||||
|
var map = new L.Map('map', {
|
||||||
|
zoomControl: true,
|
||||||
|
center: [40, 0],
|
||||||
|
zoom: 3
|
||||||
|
});
|
||||||
|
|
||||||
|
L.tileLayer('http://{s}.api.cartocdn.com/base-dark/{z}/{x}/{y}.png', {
|
||||||
|
attribution: 'CartoDB'
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
var torqueLayer = new L.TorqueLayer({
|
||||||
|
user : 'viz2',
|
||||||
|
table : 'ow',
|
||||||
|
cartocss: CARTOCSS
|
||||||
|
});
|
||||||
|
torqueLayer.addTo(map);
|
||||||
|
|
||||||
|
map.on('click', function(e) {
|
||||||
|
var p = e.containerPoint
|
||||||
|
var value = torqueLayer.getValueForPos(p.x, p.y);
|
||||||
|
if (value !== null) {
|
||||||
|
map.openPopup('average temperature: ' + value.value + "C", e.latlng);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// show small rectable and change cursor on hover
|
||||||
|
var hover = null;
|
||||||
|
map.on('mousemove', function(e) {
|
||||||
|
var p = e.containerPoint
|
||||||
|
var value = torqueLayer.getValueForPos(p.x, p.y);
|
||||||
|
|
||||||
|
// remove previous hover box
|
||||||
|
if (hover) {
|
||||||
|
map.removeLayer(hover);
|
||||||
|
hover = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value !== null) {
|
||||||
|
hover = L.rectangle(value.bbox, {
|
||||||
|
color: '#000',
|
||||||
|
weight: 1
|
||||||
|
}).addTo(map);
|
||||||
|
map._container.style.cursor = 'pointer';
|
||||||
|
} else {
|
||||||
|
map._container.style.cursor = 'auto';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -330,6 +330,27 @@ L.TorqueLayer = L.CanvasLayer.extend({
|
|||||||
positions = positions.concat(this.renderer.getActivePointsBBox(tile, step));
|
positions = positions.concat(this.renderer.getActivePointsBBox(tile, step));
|
||||||
}
|
}
|
||||||
return positions;
|
return positions;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the value for position relative to map coordinates. null for no value
|
||||||
|
*/
|
||||||
|
getValueForPos: function(x, y, step) {
|
||||||
|
step = step === undefined ? this.key: step;
|
||||||
|
var t, tile, pos, value = null, xx, yy;
|
||||||
|
for(t in this._tiles) {
|
||||||
|
tile = this._tiles[t];
|
||||||
|
pos = this.getTilePos(tile.coord);
|
||||||
|
xx = x - pos.x;
|
||||||
|
yy = y - pos.y;
|
||||||
|
if (xx >= 0 && yy >= 0 && xx < this.renderer.TILE_SIZE && yy <= this.renderer.TILE_SIZE) {
|
||||||
|
value = this.renderer.getValueFor(tile, step, xx, yy);
|
||||||
|
}
|
||||||
|
if (value !== null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -59,9 +59,10 @@ MercatorProjection.prototype._tilePixelPos = function(tileX, tileY) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
MercatorProjection.prototype.tilePixelBBox = function(x, y, zoom, px, py) {
|
MercatorProjection.prototype.tilePixelBBox = function(x, y, zoom, px, py, res) {
|
||||||
|
res = res || 1.0;
|
||||||
var numTiles = 1 <<zoom;
|
var numTiles = 1 <<zoom;
|
||||||
var inc = 1.0/numTiles;
|
var inc = res/numTiles;
|
||||||
px = (x*this._tileSize + px)/numTiles;
|
px = (x*this._tileSize + px)/numTiles;
|
||||||
py = (y*this._tileSize + py)/numTiles;
|
py = (y*this._tileSize + py)/numTiles;
|
||||||
return [
|
return [
|
||||||
|
@ -187,6 +187,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return positions;
|
return positions;
|
||||||
|
},
|
||||||
|
|
||||||
|
// return the value for x, y (tile coordinates)
|
||||||
|
// null for no value
|
||||||
|
getValueFor: function(tile, step, px, py) {
|
||||||
|
var mercator = new torque.Mercator();
|
||||||
|
var res = this.options.resolution;
|
||||||
|
var res2 = res >> 1;
|
||||||
|
|
||||||
|
var tileMax = this.options.resolution * (this.TILE_SIZE/this.options.resolution - 1);
|
||||||
|
//this.renderer.renderTile(tile, this.key, pos.x, pos.y);
|
||||||
|
var activePixels = tile.timeCount[step];
|
||||||
|
var pixelIndex = tile.timeIndex[step];
|
||||||
|
for(var p = 0; p < activePixels; ++p) {
|
||||||
|
var posIdx = tile.renderDataPos[pixelIndex + p];
|
||||||
|
var c = tile.renderData[pixelIndex + p];
|
||||||
|
if (c) {
|
||||||
|
var x = tile.x[posIdx];
|
||||||
|
var y = tileMax - tile.y[posIdx];
|
||||||
|
var dx = px + res2 - x;
|
||||||
|
var dy = py + res2 - y;
|
||||||
|
if (dx >= 0 && dx < res && dy >= 0 && dy < res) {
|
||||||
|
return {
|
||||||
|
value: c,
|
||||||
|
bbox: mercator.tilePixelBBox(
|
||||||
|
tile.coord.x,
|
||||||
|
tile.coord.y,
|
||||||
|
tile.coord.z,
|
||||||
|
x - res2, y - res2, res
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -47,3 +47,31 @@ test('render conditional point layers', function() {
|
|||||||
st = layer.getStyle('canvas-2d', {}, { zoom: 18, 'frame-offset': 0 });
|
st = layer.getStyle('canvas-2d', {}, { zoom: 18, 'frame-offset': 0 });
|
||||||
equal(st['point-radius'], 20);
|
equal(st['point-radius'], 20);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('get value for position', function() {
|
||||||
|
var mercator = new torque.Mercator();
|
||||||
|
tile = {
|
||||||
|
timeCount: [1],
|
||||||
|
timeIndex: [0],
|
||||||
|
renderDataPos: [0],
|
||||||
|
renderData: [5],
|
||||||
|
x: [100],
|
||||||
|
y: [3],
|
||||||
|
coord: { x: 0, y: 0, z: 0 }
|
||||||
|
};
|
||||||
|
renderer.options = {
|
||||||
|
resolution: 1
|
||||||
|
};
|
||||||
|
var v = renderer.getValueFor(tile, 0, 100, 255 - 3);
|
||||||
|
var bbox = mercator.tilePixelBBox(0, 0, 0, 100, 255 - 3, 1);
|
||||||
|
equal(v.bbox[0].lat, bbox[0].lat);
|
||||||
|
equal(v.bbox[1].lat, bbox[1].lat);
|
||||||
|
equal(v.bbox[0].lon, bbox[0].lon);
|
||||||
|
equal(v.bbox[1].lon, bbox[1].lon);
|
||||||
|
equal(v.value, 5);
|
||||||
|
|
||||||
|
v = renderer.getValueFor(tile, 0, 100, 255 - 4);
|
||||||
|
equal(v, null);
|
||||||
|
v = renderer.getValueFor(tile, 0, 99, 255 - 3);
|
||||||
|
equal(v, null);
|
||||||
|
});
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
<script src="../lib/torque/core.js"></script>
|
<script src="../lib/torque/core.js"></script>
|
||||||
<script src="../lib/torque/profiler.js"></script>
|
<script src="../lib/torque/profiler.js"></script>
|
||||||
<script src="../lib/torque/request.js"></script>
|
<script src="../lib/torque/request.js"></script>
|
||||||
|
<script src="../lib/torque/mercator.js"></script>
|
||||||
<script src="../lib/torque/leaflet/leaflet_tileloader_mixin.js"></script>
|
<script src="../lib/torque/leaflet/leaflet_tileloader_mixin.js"></script>
|
||||||
<script src="../lib/torque/leaflet/canvas_layer.js"></script>
|
<script src="../lib/torque/leaflet/canvas_layer.js"></script>
|
||||||
<script src="../lib/torque/renderer/point.js"></script>
|
<script src="../lib/torque/renderer/point.js"></script>
|
||||||
|
Loading…
Reference in New Issue
Block a user