Skip to content

Commit

Permalink
- Add coverage percentiles getColorValue prop to grid layer (visgl#614)
Browse files Browse the repository at this point in the history
* add getColorValue percentile coverage to grid layer

* add layer tests

* added grid/hex layer test

* fix typo

* reset yarn.lock

* address comments

* replace concat
  • Loading branch information
heshan0131 authored May 1, 2017
1 parent 1741a52 commit d76e820
Show file tree
Hide file tree
Showing 14 changed files with 314 additions and 188 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Ref: http://keepachangelog.com/en/0.3.0/

- BREAKING: Only composite layers have `renderLayer` methods
- BREAKING: Only primitive layers' `draw` methods are called during render
- `GridLayer` add `coverage`, `lowerPercentile`, `upperPercentile` and `getColorValue` to layer prop
- HOTFIX: fix `HexagonLayer` hex color calculation, use `bin.value` instead of `bin.points.count` to calculate color

### deck.gl v4.1-alpha.1

Expand Down
53 changes: 53 additions & 0 deletions docs/layers/grid-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,45 @@ Color scale domain, default is set to the range of point counts in each cell.
Color ranges as an array of colors formatted as `[255, 255, 255]`. Default is
[colorbrewer](http://colorbrewer2.org/#type=sequential&scheme=YlOrRd&n=6) `6-class YlOrRd`.

##### `getColorValue` (Function, optional)

- Default: `points => points.length`

`getColorValue` is the accessor function to get the value that cell color is based on.
It takes an array of points inside each cell as arguments, returns a number. For example,
You can pass in `getColorValue` to color the cells by avg/mean/max of a specific attributes of each point.
By default `getColorValue` returns the length of the points array.

Note: grid layer compares whether `getColorValue` has changed to
recalculate the value for each bin that its color based on. You should
pass in the function defined outside the render function so it doesn't create a
new function on every rendering pass.

```
class MyGridLayer {
getColorValue (points) {
return points.length;
}
renderLayers() {
return new GridLayer({
id: 'grid-layer',
getColorValue: this.getColorValue // instead of getColorValue: (points) => { return points.length; }
data,
cellSize: 500
});
}
}
```

##### `coverage` (Number, optional)

- Default: `1`

Cell size multiplier, clamped between 0 - 1. The final size of cell
is calculated by `coverage * cellSize`. Note: coverage does not affect how points
are binned. Coverage are linear based.

##### `elevationDomain` (Array, optional)

- Default: `[0, max(count)]`
Expand All @@ -88,6 +127,20 @@ Cell elevation multiplier. The elevation of cell is calculated by

Whether to enable cell elevation. Cell elevation scale by count of points in each cell. If set to false, all cell will be flat.

##### `upperPercentile` (Number, optional)

- Default: `100`

Filter cells and re-calculate color by `upperPercentile`. Cells with value
larger than the upperPercentile will be hidden.

##### `lowerPercentile` (Number, optional)

- Default: `0`

Filter bins and re-calculate color by `lowerPercentile`. Cells with value
smaller than the lowerPercentile will be hidden.

##### `fp64` (Boolean, optional)

- Default: `false`
Expand Down
35 changes: 28 additions & 7 deletions docs/layers/hexagon-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,39 @@ Hexagon color ranges as an array of colors formatted as `[[255, 255, 255, 255]]`
- Default: `points => points.length`

`getColorValue` is the accessor function to get the value that bin color is based on.
It takes an array of points inside each bin as arguments, returns a value. For example,
It takes an array of points inside each bin as arguments, returns a number. For example,
You can pass in `getColorValue` to color the bins by avg/mean/max of a specific attributes of each point.
By default `getColorValue` returns the length of the points array.

Note: hexagon layer compares whether `getColorValue` has changed to
recalculate the value for each bin that its color based on. You should
pass in the function defined outside the render function so it doesn't create a
new function on every rendering pass.

```
class MyHexagonLayer {
getColorValue (points) {
return points.length;
}
renderLayers() {
return new HexagonLayer({
id: 'hexagon-layer',
getColorValue: this.getColorValue // instead of getColorValue: (points) => { return points.length; }
data,
radius: 500
});
}
}
```

##### `coverage` (Number, optional)

- Default: `1`

Hexagon radius multiplier, clamped between 0 - 1. The final radius of hexagon
is calculated by `coverage * radius`. Note: coverage does not affect how points
are binned.
The radius of the bin is determined only by the `radius` property.
are binned. The radius of the bin is determined only by the `radius` property.

##### `elevationDomain` (Array, optional)

Expand Down Expand Up @@ -131,15 +152,15 @@ Whether to enable cell elevation. Cell elevation scale by count of points in eac

- Default: `100`

Filter bins and re-calculate color by `upperPercentile`. Hexagons with counts
bigger than the upperPercentile counts will be hidden.
Filter bins and re-calculate color by `upperPercentile`. Hexagons with value
larger than the upperPercentile will be hidden.

##### `lowerPercentile` (Number, optional)

- Default: `0`

Filter bins and re-calculate color by `lowerPercentile`. Hexagons with counts
smaller than the lowerPercentile counts will be hidden.
Filter bins and re-calculate color by `lowerPercentile`. Hexagons with value
smaller than the lowerPercentile will be hidden.

##### `fp64` (Boolean, optional)

Expand Down
28 changes: 25 additions & 3 deletions examples/layer-browser/src/examples/core-layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ const MARKER_SIZE_MAP = {

const LIGHT_SETTINGS = {
lightsPosition: [-122.45, 37.66, 8000, -122.0, 38.00, 8000],
ambientRatio: 0.4,
ambientRatio: 0.3,
diffuseRatio: 0.6,
specularRatio: 0.6,
specularRatio: 0.4,
lightsStrength: [1, 0.0, 0.8, 0.0],
numberOfLights: 2
};
Expand Down Expand Up @@ -209,14 +209,36 @@ const GridCellLayerExample = {
}
};

function getMean(pts, key) {
const filtered = pts.filter(pt => Number.isFinite(pt[key]));

return filtered.length ?
filtered.reduce((accu, curr) => accu + curr[key], 0) / filtered.length : null;
}

// grid layer compares whether getColorValue has changed to
// call out bin sorting. Here we pass in the function defined
// outside props, so it doesn't create a new function on
// every rendering pass
function getColorValue(points) {
return getMean(points, 'SPACES');
}

const GridLayerExample = {
layer: GridLayer,
propTypes: {
cellSize: {type: 'number', min: 0, max: 1000}
cellSize: {type: 'number', min: 0, max: 1000},
coverage: {type: 'number', min: 0, max: 1},
lowerPercentile: {type: 'number', min: 0, max: 100},
upperPercentile: {type: 'number', min: 0, max: 100}
},
props: {
id: 'gridLayer',
data: dataSamples.points,
// instead of doing getColorValue = () => {}
// defined the function outside and pass in here
// so it doesn't generate a new function on every render
getColorValue,
cellSize: 200,
opacity: 1,
extruded: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ uniform vec3 selectedPickingColor;
// Custom uniforms
uniform float extruded;
uniform float cellSize;
uniform float coverage;
uniform float opacity;
uniform float elevationScale;
Expand All @@ -57,10 +58,14 @@ float isPicked(vec3 pickingColors, vec3 selectedColor) {
void main(void) {
vec2 topLeftPos = project_position(instancePositions.xy);
// if ahpha == 0.0, do not render element
float finalCellSize = cellSize * mix(1.0, 0.0, float(instanceColors.a == 0.0));
// cube gemoetry vertics are between -1 to 1, scale and transform it to between 0, 1
vec2 pos = topLeftPos + vec2((positions.x + 1.0) * cellSize
/ 2.0, (positions.y + 1.0) * cellSize / 2.0);
vec2 pos = topLeftPos + vec2(
(positions.x * coverage + 1.0) / 2.0 * finalCellSize,
(positions.y * coverage + 1.0) / 2.0 * finalCellSize);
float elevation = 0.0;
Expand Down
6 changes: 4 additions & 2 deletions src/layers/core/grid-cell-layer/grid-cell-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const DEFAULT_COLOR = [255, 0, 255, 255];

const defaultProps = {
cellSize: 1000,
coverage: 1,
elevationScale: 1,
extruded: true,
fp64: false,
Expand Down Expand Up @@ -133,15 +134,16 @@ export default class GridCellLayer extends Layer {
}

updateUniforms() {
const {opacity, extruded, elevationScale, cellSize, lightSettings} = this.props;
const {opacity, extruded, elevationScale, cellSize, coverage, lightSettings} = this.props;
const {viewport} = this.context;
const {pixelsPerMeter} = viewport.getDistanceScales();

this.setUniforms(Object.assign({}, {
extruded,
elevationScale,
opacity,
cellSize: cellSize * pixelsPerMeter[0]
cellSize: cellSize * pixelsPerMeter[0],
coverage
},
lightSettings));
}
Expand Down
11 changes: 1 addition & 10 deletions src/layers/core/grid-layer/grid-aggregator.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,10 @@ export function pointToDensityGridData(points, cellSize, getPosition) {

const {gridHash, gridOffset} = _pointsToGridHashing(points, cellSize, getPosition);
const layerData = _getGridLayerDataFromGridHash(gridHash, gridOffset);
const countRange = _getCellCountExtent(layerData);

return {
gridOffset,
layerData,
countRange
layerData
};
}

Expand Down Expand Up @@ -94,13 +92,6 @@ function _getGridLayerDataFromGridHash(gridHash, gridOffset) {
}, []);
}

function _getCellCountExtent(data) {
return data.length ? [
Math.min.apply(null, data.map(d => d.count)),
Math.max.apply(null, data.map(d => d.count))
] : [0, 1];
}

/**
* calculate grid layer cell size in lat lon based on world unit size
* and current latitude
Expand Down
Loading

0 comments on commit d76e820

Please sign in to comment.