Skip to content

Commit

Permalink
Add multiple colorramps to coordinate maps (#17403) (#18586)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasneirynck authored Apr 26, 2018
1 parent e3389ee commit 67fed21
Show file tree
Hide file tree
Showing 15 changed files with 118 additions and 60 deletions.
31 changes: 4 additions & 27 deletions src/core_plugins/region_map/public/choropleth_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { KibanaMapLayer } from 'ui/vis/map/kibana_map_layer';
import { truncatedColorMaps } from 'ui/vislib/components/color/truncated_colormaps';
import * as topojson from 'topojson-client';
import { toastNotifications } from 'ui/notify';
import * as colorUtil from 'ui/vis/map/color_util';

const EMPTY_STYLE = {
weight: 1,
Expand Down Expand Up @@ -179,7 +180,7 @@ CORS configuration of the server permits requests from the Kibana application on

if (this._metrics && this._metrics.length > 0) {
const { min, max } = getMinMax(this._metrics);
this._legendColors = getLegendColors(this._colorRamp);
this._legendColors = colorUtil.getLegendColors(this._colorRamp);
const quantizeDomain = (min !== max) ? [min, max] : d3.scale.quantize().domain();
this._legendQuantizer = d3.scale.quantize().domain(quantizeDomain).range(this._legendColors);
}
Expand Down Expand Up @@ -420,39 +421,15 @@ function getMinMax(data) {
return { min, max };
}

function getLegendColors(colorRamp) {
const colors = [];
colors[0] = getColor(colorRamp, 0);
colors[1] = getColor(colorRamp, Math.floor(colorRamp.length * 1 / 4));
colors[2] = getColor(colorRamp, Math.floor(colorRamp.length * 2 / 4));
colors[3] = getColor(colorRamp, Math.floor(colorRamp.length * 3 / 4));
colors[4] = getColor(colorRamp, colorRamp.length - 1);
return colors;
}

function getColor(colorRamp, i) {

if (!colorRamp[i]) {
return getColor();
}

const color = colorRamp[i][1];
const red = Math.floor(color[0] * 255);
const green = Math.floor(color[1] * 255);
const blue = Math.floor(color[2] * 255);
return `rgb(${red},${green},${blue})`;
}


function getChoroplethColor(value, min, max, colorRamp) {
if (min === max) {
return getColor(colorRamp, colorRamp.length - 1);
return colorUtil.getColor(colorRamp, colorRamp.length - 1);
}
const fraction = (value - min) / (max - min);
const index = Math.round(colorRamp.length * fraction) - 1;
const i = Math.max(Math.min(colorRamp.length - 1, index), 0);

return getColor(colorRamp, i);
return colorUtil.getColor(colorRamp, i);
}


Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { ImageComparator } from 'test_utils/image_comparator';
import sinon from 'sinon';
import dummyESResponse from './dummy_es_response.json';
import initial from './initial.png';
import blues from './blues.png';
import shadedGeohashGrid from './shadedGeohashGrid.png';
import heatmapRaw from './heatmap_raw.png';

function mockRawData() {
Expand Down Expand Up @@ -180,7 +182,6 @@ describe('CoordinateMapsVisualizationTest', function () {
coordinateMapVisualization.destroy();
expect(mismatchedPixels).to.be.lessThan(PIXEL_DIFF);


});

it('should toggle back&forth OK between mapTypes', async function () {
Expand Down Expand Up @@ -218,6 +219,61 @@ describe('CoordinateMapsVisualizationTest', function () {

});

it('should toggle to different color schema ok', async function () {

const coordinateMapVisualization = new CoordinateMapsVisualization(domNode, vis);
await coordinateMapVisualization.render(dummyESResponse, {
resize: false,
params: true,
aggs: true,
data: true,
uiState: false
});


vis.params.colorSchema = 'Blues';
await coordinateMapVisualization.render(dummyESResponse, {
resize: false,
params: true,
aggs: false,
data: false,
uiState: false
});

const mismatchedPixels = await compareImage(blues, 0);
coordinateMapVisualization.destroy();
expect(mismatchedPixels).to.be.lessThan(PIXEL_DIFF);

});

it('should toggle to different color schema and maptypes ok', async function () {

const coordinateMapVisualization = new CoordinateMapsVisualization(domNode, vis);
await coordinateMapVisualization.render(dummyESResponse, {
resize: false,
params: true,
aggs: true,
data: true,
uiState: false
});


vis.params.colorSchema = 'Greens';
vis.params.mapType = 'Shaded Geohash Grid';
await coordinateMapVisualization.render(dummyESResponse, {
resize: false,
params: true,
aggs: false,
data: false,
uiState: false
});

const mismatchedPixels = await compareImage(shadedGeohashGrid, 0);
coordinateMapVisualization.destroy();
expect(mismatchedPixels).to.be.lessThan(PIXEL_DIFF);

});


});

Expand Down
7 changes: 4 additions & 3 deletions src/core_plugins/tile_map/public/__tests__/geohash_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,19 @@ describe('geohash_layer', function () {
});

afterEach(function () {
// return;
kibanaMap.destroy();
teardownDOM();
imageComparator.destroy();
});

[
{
options: { mapType: 'Scaled Circle Markers' },
options: { mapType: 'Scaled Circle Markers', colorRamp: 'Yellow to Red' },
expected: scaledCircleMarkersPng
},
{
options: { mapType: 'Shaded Circle Markers' },
options: { mapType: 'Shaded Circle Markers', colorRamp: 'Yellow to Red' },
expected: shadedCircleMarkersPng
},
{
Expand Down Expand Up @@ -99,7 +100,7 @@ describe('geohash_layer', function () {
{
type: 'FeatureCollection',
features: []
}, {}, { 'mapType': 'Scaled Circle Markers' }, kibanaMap.getZoomLevel(), kibanaMap);
}, {}, { 'mapType': 'Scaled Circle Markers', colorRamp: 'Yellow to Red' }, kibanaMap.getZoomLevel(), kibanaMap);
kibanaMap.addLayer(geohashLayer);

expect(() => {
Expand Down
Binary file modified src/core_plugins/tile_map/public/__tests__/initial.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/core_plugins/tile_map/public/__tests__/scaledCircleMarkers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/core_plugins/tile_map/public/__tests__/shadedCircleMarkers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export function CoordinateMapsVisualizationProvider(Notifier, Private) {
mapType: newParams.mapType,
isFilteredByCollar: this._isFilteredByCollar(),
fetchBounds: this.getGeohashBounds.bind(this),
colorRamp: newParams.colorSchema,
heatmap: {
heatClusterSize: newParams.heatClusterSize
}
Expand Down
15 changes: 15 additions & 0 deletions src/core_plugins/tile_map/public/editors/tile_map_vis_params.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@
</select>
</div>


<div class="kuiSideBarFormRow" >
<label class="kuiSideBarFormRow__label" for="colorSchema">
Color Schema
</label>
<div class="kuiSideBarFormRow__control">
<select
id="colorSchema"
class="kuiSelect kuiSideBarSelect"
ng-model="vis.params.colorSchema"
ng-options="mode for mode in vis.type.editorConfig.collections.colorSchemas"
></select>
</div>
</div>

<div ng-if="vis.params.mapType === 'Heatmap'" class="form-group">
<div>
<label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ const module = uiModules.get('kibana');
module.directive('tileMapVisParams', function () {
return {
restrict: 'E',
template: tileMapTemplate
template: tileMapTemplate,
};
});
8 changes: 6 additions & 2 deletions src/core_plugins/tile_map/public/geohash_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export class GeohashLayer extends KibanaMapLayer {
isFilteredByCollar: this._geohashOptions.isFilteredByCollar,
valueFormatter: this._geohashOptions.valueFormatter,
tooltipFormatter: this._geohashOptions.tooltipFormatter,
label: this._geohashOptions.label
label: this._geohashOptions.label,
colorRamp: this._geohashOptions.colorRamp
};
switch (this._geohashOptions.mapType) {
case 'Scaled Circle Markers':
Expand Down Expand Up @@ -113,7 +114,10 @@ export class GeohashLayer extends KibanaMapLayer {
return true;
}

if (this._geohashOptions.mapType !== options.mapType) {
//check if any impacts leaflet styler function
if (this._geohashOptions.colorRamp !== options.colorRamp) {
return false;
} else if (this._geohashOptions.mapType !== options.mapType) {
return false;
} else if (this._geohashOptions.mapType === 'Heatmap' && !_.isEqual(this._geohashOptions.heatmap, options)) {
return false;
Expand Down
35 changes: 10 additions & 25 deletions src/core_plugins/tile_map/public/markers/scaled_circles.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import _ from 'lodash';
import d3 from 'd3';
import $ from 'jquery';
import { EventEmitter } from 'events';
import { truncatedColorMaps } from 'ui/vislib/components/color/truncated_colormaps';
import * as colorUtil from 'ui/vis/map/color_util';

export class ScaledCirclesMarkers extends EventEmitter {

Expand All @@ -17,6 +19,7 @@ export class ScaledCirclesMarkers extends EventEmitter {
this._valueFormatter = options.valueFormatter || ((x) => {x;});
this._tooltipFormatter = options.tooltipFormatter || ((x) => {x;});
this._label = options.label;
this._colorRamp = options.colorRamp;

this._legendColors = null;
this._legendQuantizer = null;
Expand All @@ -38,7 +41,6 @@ export class ScaledCirclesMarkers extends EventEmitter {
};
}
this._leafletLayer = L.geoJson(null, layerOptions);

this._leafletLayer.addData(this._featureCollection);
}

Expand All @@ -53,10 +55,11 @@ export class ScaledCirclesMarkers extends EventEmitter {
const max = _.get(this._featureCollectionMetaData, 'max', 1);

const quantizeDomain = (min !== max) ? [min, max] : d3.scale.quantize().domain();
this._legendColors = makeCircleMarkerLegendColors(min, max);

this._legendColors = makeLegendColors(this._colorRamp);
this._legendQuantizer = d3.scale.quantize().domain(quantizeDomain).range(this._legendColors);

return makeStyleFunction(min, max, this._legendColors, quantizeDomain);
return makeStyleFunction(this._legendColors, quantizeDomain);
}


Expand Down Expand Up @@ -191,35 +194,17 @@ export class ScaledCirclesMarkers extends EventEmitter {
}


/**
* d3 quantize scale returns a hex color, used for marker fill color
*
* @method quantizeLegendColors
* return {undefined}
*/
function makeCircleMarkerLegendColors(min, max) {
const reds1 = ['#ff6128'];
const reds3 = ['#fecc5c', '#fd8d3c', '#e31a1c'];
const reds5 = ['#fed976', '#feb24c', '#fd8d3c', '#f03b20', '#bd0026'];
const bottomCutoff = 2;
const middleCutoff = 24;
let legendColors;
if (max - min <= bottomCutoff) {
legendColors = reds1;
} else if (max - min <= middleCutoff) {
legendColors = reds3;
} else {
legendColors = reds5;
}
return legendColors;
function makeLegendColors(colorRampKey) {
const colorRamp = truncatedColorMaps[colorRampKey];
return colorUtil.getLegendColors(colorRamp);
}

function makeColorDarker(color) {
const amount = 1.3;//magic number, carry over from earlier
return d3.hcl(color).darker(amount).toString();
}

function makeStyleFunction(min, max, legendColors, quantizeDomain) {
function makeStyleFunction(legendColors, quantizeDomain) {
const legendQuantizer = d3.scale.quantize().domain(quantizeDomain).range(legendColors);
return (feature) => {
const value = _.get(feature, 'properties.value');
Expand Down
4 changes: 3 additions & 1 deletion src/core_plugins/tile_map/public/tile_map_vis.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import image from './images/icon-tilemap.svg';
import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
import { Status } from 'ui/vis/update_status';
import { makeGeoJsonResponseHandler } from './coordinatemap_response_handler';

import { truncatedColorMaps } from 'ui/vislib/components/color/truncated_colormaps';

VisTypesRegistryProvider.register(function TileMapVisType(Private, getAppState, courier, config) {

Expand All @@ -25,6 +25,7 @@ VisTypesRegistryProvider.register(function TileMapVisType(Private, getAppState,
visConfig: {
canDesaturate: !!supports.cssFilters,
defaults: {
colorSchema: 'Yellow to Red',
mapType: 'Scaled Circle Markers',
isDesaturated: true,
addTooltip: true,
Expand All @@ -40,6 +41,7 @@ VisTypesRegistryProvider.register(function TileMapVisType(Private, getAppState,
visualization: CoordinateMapsVisualization,
editorConfig: {
collections: {
colorSchemas: Object.keys(truncatedColorMaps),
legendPositions: [{
value: 'bottomleft',
text: 'bottom left',
Expand Down
17 changes: 17 additions & 0 deletions src/ui/public/vis/map/color_util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export function getLegendColors(colorRamp) {
const colors = [];
colors[0] = getColor(colorRamp, 0);
colors[1] = getColor(colorRamp, Math.floor(colorRamp.length * 1 / 4));
colors[2] = getColor(colorRamp, Math.floor(colorRamp.length * 2 / 4));
colors[3] = getColor(colorRamp, Math.floor(colorRamp.length * 3 / 4));
colors[4] = getColor(colorRamp, colorRamp.length - 1);
return colors;
}

export function getColor(colorRamp, i) {
const color = colorRamp[i][1];
const red = Math.floor(color[0] * 255);
const green = Math.floor(color[1] * 255);
const blue = Math.floor(color[2] * 255);
return `rgb(${red},${green},${blue})`;
}

0 comments on commit 67fed21

Please sign in to comment.