Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding minimum value filtering and dual metric, using circle sizes and colours for different metrics. #327

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
node_modules
node_modules*
npm-debug.log
.DS_Store
.vscode
Expand All @@ -10,4 +10,4 @@ artifacts/

.venv*
bin/
build/
build/
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
10.24.1
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Changelog
## Entries

## v1.0.2
- Added filtering by minimum value and color as second metric

## v1.0.1

- Release for Grafana 7.0 with plugin signing
Expand Down
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ This is minimum size for a circle in pixels.

This is the maximum size for a circle in pixels. Depending on the zoom level you might want a larger or smaller max circle size to avoid overlapping.

**Min Value**

This is the minimum value to show a circle. If provided, data points with values lower than this minimum value won't be shown at the map.

**Unit**

The Unit is shown in the popover when you hover over a circle. There are two fields the singular form and the plural form. E.g. visit/visits or error/errors
Expand All @@ -269,14 +273,23 @@ The Unit is shown in the popover when you hover over a circle. There are two fie

Shows/hide the legend on the bottom left that shows the threshold ranges and their associated colors.

### Threshold Options
### Coloring Options

The coloring options allows the use of the circle colors with a second metric. In case of a single metric, leave the Color metric field empty and the same value will be used for circle size and color.

Using the field color metric will enable the color to read from this second metric and generate thresholds based on this number.

The Color label is used on the tooltip, showing a "Label: Value Unit" as a second line on the map's tooltip, together with the Color unit field.

The Color value decimals limits the decimal cases from the color metric on the map's tooltip.

Thresholds control the color of the circles.

If one value is specified then two colors are used. For example, if the threshold is set to 10 then values under 10 get the first color and values that are 10 or more get the second color.

The threshold field also accepts 2 or more comma-separated values. For example, if you have 2 values that represents 3 ranges that correspond to the three colors. For example: if the thresholds are 70, 90 then the first color represents < 70, the second color represents between 70 and 90 and the third color represents > 90.


### CHANGELOG

The latest changes can be found here: [CHANGELOG.md](https://github.com/grafana/worldmap-panel/blob/master/CHANGELOG.md)
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "worldmap-panel",
"version": "1.0.1",
"version": "1.0.2",
"description": "Worldmap Panel Plugin for Grafana",
"main": "src/module.js",
"scripts": {
Expand Down
12 changes: 7 additions & 5 deletions src/data_formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ export default class DataFormatter {
}
}

createDataValue(encodedGeohash, decodedGeohash, locationName, value) {
createDataValue(encodedGeohash, decodedGeohash, locationName, value, colorValue) {
const dataValue = {
colorValue: colorValue,
key: encodedGeohash,
locationName: locationName,
locationLatitude: decodedGeohash.latitude,
Expand All @@ -65,6 +66,7 @@ export default class DataFormatter {
};

dataValue.valueRounded = kbn.roundValue(dataValue.value, this.ctrl.panel.decimals || 0);
dataValue.colorValue = kbn.roundValue(dataValue.colorValue, this.ctrl.panel.colorDecimals || 0);
return dataValue;
}

Expand Down Expand Up @@ -92,8 +94,8 @@ export default class DataFormatter {
? row[columnNames[this.ctrl.panel.esLocationName]]
: encodedGeohash;
const value = row[columnNames[this.ctrl.panel.esMetric]];

const dataValue = this.createDataValue(encodedGeohash, decodedGeohash, locationName, value);
const colorValue = row[columnNames[this.ctrl.panel.colorMetric]];
const dataValue = this.createDataValue(encodedGeohash, decodedGeohash, locationName, value, colorValue);
if (dataValue.value > highestValue) {
highestValue = dataValue.value;
}
Expand All @@ -116,8 +118,8 @@ export default class DataFormatter {
? datapoint[this.ctrl.panel.esLocationName]
: encodedGeohash;
const value = datapoint[this.ctrl.panel.esMetric];

const dataValue = this.createDataValue(encodedGeohash, decodedGeohash, locationName, value);
const colorValue = datapoint[this.ctrl.panel.colorMetric];
const dataValue = this.createDataValue(encodedGeohash, decodedGeohash, locationName, value, colorValue);
if (dataValue.value > highestValue) {
highestValue = dataValue.value;
}
Expand Down
27 changes: 26 additions & 1 deletion src/partials/editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ <h5 class="section-heading">Map Visual Options</h5>
<input type="text" class="input-small gf-form-input width-10" ng-model="ctrl.panel.circleMaxSize" ng-change="ctrl.render()"
placeholder="30" ng-model-onblur />
</div>
<div class="gf-form">
<label class="gf-form-label width-10">Min value</label>
<input type="text" class="input-small gf-form-input width-10" ng-model="ctrl.panel.minValue" ng-change="ctrl.render()"
placeholder="10" ng-model-onblur />
</div>
<gf-form-switch class="gf-form" label="Sticky Labels" label-class="width-10" checked="ctrl.panel.stickyLabels" on-change="ctrl.toggleStickyLabels()">
</gf-form-switch>
<div class="gf-form">
Expand Down Expand Up @@ -189,7 +194,27 @@ <h6 ng-show="ctrl.panel.locationData === 'table' || ctrl.panel.locationData ===
</div>

<div class="section gf-form-group">
<h5 class="section-heading">Threshold Options</h5>
<h5 class="section-heading">Coloring Options</h5>
<div class="gf-form">
<label class="gf-form-label width-10">Color metric </label>
<input type="text" class="input-small gf-form-input width-10" ng-model="ctrl.panel.colorMetric" ng-change="ctrl.render()"
placeholder="Percent Column" ng-model-onblur />
</div>
<div class="gf-form">
<label class="gf-form-label width-10">Color label </label>
<input type="text" class="input-small gf-form-input width-10" ng-model="ctrl.panel.colorLabel" ng-change="ctrl.render()"
placeholder="Percent Column" ng-model-onblur />
</div>
<div class="gf-form">
<label class="gf-form-label width-10">Color unit </label>
<input type="text" class="input-small gf-form-input width-10" ng-model="ctrl.panel.colorUnit" ng-change="ctrl.render()"
placeholder="%" ng-model-onblur />
</div>
<div class="gf-form">
<label class="gf-form-label width-10">Color value decimals </label>
<input type="text" class="input-small gf-form-input width-10" ng-model="ctrl.panel.colorDecimals" ng-change="ctrl.render()"
placeholder="2" ng-model-onblur />
</div>
<div class="gf-form">
<label class="gf-form-label width-10">Thresholds</label>
<input type="text" class="input-small gf-form-input width-10" ng-model="ctrl.panel.thresholds" ng-change="ctrl.changeThresholds()"
Expand Down
2 changes: 1 addition & 1 deletion src/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
{"name": "USA", "path": "images/worldmap-usa.png"},
{"name": "Light Theme", "path": "images/worldmap-light-theme.png"}
],
"version": "1.0.1",
"version": "1.0.2",
"updated": "Fri May 15 14:40:24 MDT 2020"
},

Expand Down
53 changes: 53 additions & 0 deletions src/worldmap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,59 @@ describe('Worldmap', () => {
});
});

describe('when a second metric is set to the color', () => {
const lowVal = 7;
const avgVal = 50;
const highVal = 99;
beforeEach(() => {
ctrl.data = new DataBuilder()
.withCountryAndValue('SE', 1, highVal)
.withCountryAndValue('IE', 2, avgVal)
.withCountryAndValue('US', 1, lowVal)
.withDataRange(0, 100, 50)
.withThresholdValues([33,66])
.build();
worldMap.drawCircles();
});

it('should create circle popups with the second metrics there', () => {
expect(worldMap.circles[0]._popup._content).toBe(`Sweden: 1<br>${highVal}`);
expect(worldMap.circles[1]._popup._content).toBe(`Ireland: 2<br>${avgVal}`);
expect(worldMap.circles[2]._popup._content).toBe(`United States: 1<br>${lowVal}`);
});

it('should set the right colors using the second metric', () => {
expect(worldMap.circles[0].options.color).toBe('green');
expect(worldMap.circles[1].options.color).toBe('blue');
expect(worldMap.circles[2].options.color).toBe('red');
});
});

describe('when a second metric is set to the color with labels', () => {
const lowVal = 7;
const avgVal = 50;
const highVal = 99;
const label = 'Metric';
const unit = '%';

beforeEach(() => {
ctrl.data = new DataBuilder()
.withCountryAndValue('SE', 1, highVal)
.withCountryAndValue('IE', 2, avgVal)
.withCountryAndValue('US', 1, lowVal)
.build();
ctrl.panel.colorLabel = label;
ctrl.panel.colorUnit = unit;
worldMap.drawCircles();
});

it('should create circle popups with the second metrics there', () => {
expect(worldMap.circles[0]._popup._content).toBe(`Sweden: 1<br>${label}: ${highVal}${unit}`);
expect(worldMap.circles[1]._popup._content).toBe(`Ireland: 2<br>${label}: ${avgVal}${unit}`);
expect(worldMap.circles[2]._popup._content).toBe(`United States: 1<br>${label}: ${lowVal}${unit}`);
});
});

afterEach(() => {
const fixture: HTMLElement = document.getElementById('fixture')!;
document.body.removeChild(fixture);
Expand Down
26 changes: 17 additions & 9 deletions src/worldmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ export default class WorldMap {

filterEmptyAndZeroValues(data) {
return _.filter(data, o => {
return !(this.ctrl.panel.hideEmpty && _.isNil(o.value)) && !(this.ctrl.panel.hideZero && o.value === 0);
return !(this.ctrl.panel.hideEmpty && _.isNil(o.value))
&& !(this.ctrl.panel.hideZero && o.value === 0)
&& !(this.ctrl.panel.minValue && o.value <= this.ctrl.panel.minValue)
});
}

Expand Down Expand Up @@ -148,29 +150,33 @@ export default class WorldMap {
});

if (circle) {
const colorValue = dataPoint.colorValue !== undefined ? dataPoint.colorValue : dataPoint.value;
const color = this.getColor(colorValue);
circle.setRadius(this.calcCircleSize(dataPoint.value || 0));
circle.setStyle({
color: this.getColor(dataPoint.value),
fillColor: this.getColor(dataPoint.value),
color,
fillColor: color,
fillOpacity: 0.5,
location: dataPoint.key,
});
circle.unbindPopup();
this.createPopup(circle, dataPoint.locationName, dataPoint.valueRounded);
this.createPopup(circle, dataPoint.locationName, dataPoint.valueRounded, dataPoint.colorValue);
}
});
}

createCircle(dataPoint) {
const colorValue = dataPoint.colorValue !== undefined ? dataPoint.colorValue : dataPoint.value;
const color = this.getColor(colorValue);
const circle = (<any>window).L.circleMarker([dataPoint.locationLatitude, dataPoint.locationLongitude], {
radius: this.calcCircleSize(dataPoint.value || 0),
color: this.getColor(dataPoint.value),
fillColor: this.getColor(dataPoint.value),
color,
fillColor: color,
fillOpacity: 0.5,
location: dataPoint.key,
});

this.createPopup(circle, dataPoint.locationName, dataPoint.valueRounded);
this.createPopup(circle, dataPoint.locationName, dataPoint.valueRounded, dataPoint.colorValue);
return circle;
}

Expand All @@ -188,9 +194,11 @@ export default class WorldMap {
return circleSizeRange * dataFactor + circleMinSize;
}

createPopup(circle, locationName, value) {
createPopup(circle, locationName, value, colorValue) {
const unit = value && value === 1 ? this.ctrl.panel.unitSingular : this.ctrl.panel.unitPlural;
const label = (locationName + ': ' + value + ' ' + (unit || '')).trim();
const firstLine = `${locationName}: ${value} ${(unit || '')}`.trim();
const secondLine = colorValue !== undefined ? `${(this.ctrl.panel.colorLabel !== undefined ? this.ctrl.panel.colorLabel + ': ':'') + colorValue + (this.ctrl.panel.colorUnit || '')}`.trim(): '';
const label = `${firstLine}${secondLine !== '' ? '<br>' + secondLine : ''}`;
circle.bindPopup(label, {
offset: (<any>window).L.point(0, -2),
className: 'worldmap-popup',
Expand Down
5 changes: 5 additions & 0 deletions src/worldmap_ctrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ const panelDefaults = {
mapCenterLongitude: 0,
initialZoom: 1,
valueName: "total",
minValue: 0,
colorMetric:"",
colorLabel: "",
colorUnit:"%",
colorDecimals: 2,
circleMinSize: 2,
circleMaxSize: 30,
locationData: "countries",
Expand Down
5 changes: 4 additions & 1 deletion test/data_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default class DataBuilder {
this.data.thresholds = [];
}

withCountryAndValue(countryCode, value) {
withCountryAndValue(countryCode, value, colorValue?) {
let dataPoint;
if (countryCode === 'SE') {
dataPoint = {
Expand All @@ -16,6 +16,7 @@ export default class DataBuilder {
locationLongitude: 18,
value: value,
valueRounded: value,
colorValue
};
} else if (countryCode === 'IE') {
dataPoint = {
Expand All @@ -25,6 +26,7 @@ export default class DataBuilder {
locationLongitude: 8,
value: value,
valueRounded: value,
colorValue
};
} else if (countryCode === 'US') {
dataPoint = {
Expand All @@ -34,6 +36,7 @@ export default class DataBuilder {
locationLongitude: -95,
value: value,
valueRounded: value,
colorValue
};
}
this.data.push(dataPoint);
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@
"moduleResolution": "node",
"esModuleInterop": true
},
"exclude": ["node_modules*", ".vscode"]
}