Skip to content

Commit 2ce6f41

Browse files
authored
Add image overlay option (#84)
Add image overlay feature, with tests and documentation.
1 parent 2a6d382 commit 2ce6f41

File tree

6 files changed

+120
-0
lines changed

6 files changed

+120
-0
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,28 @@ If one value is specified then two colors are used. For example, if the threshol
277277

278278
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.
279279

280+
### Image overlay
281+
282+
Optionally, you can show an image over the base map (but below the data points). This could be useful, for example, to show a more detailed/up-to-date image of the area of interest, in case the map provider only has old or low-resolution images. See below for an example of displaying interpolated sensor data (for example, temperatures in a field while only knowing the temperatures at a few sensors).
283+
284+
![Image overlay example](https://raw.githubusercontent.com/panodata/grafana-map-panel/develop/src/images/overlay_example.png)
285+
286+
**Enable overlay**
287+
288+
Show/hide the overlay.
289+
290+
**Overlay URL**
291+
292+
The URL where the image is available. Please notice that only URLs can be used (no local files!)
293+
294+
**Overlay opacity**
295+
296+
The image overlay's opacity can be controlled (0=completely transparent, 1=completely opaque)
297+
298+
**Latitude and longitude ranges**
299+
300+
Specify the limits of the image in the map. Enter the minimum and maximum latitude, and the minimum and maximum longitude, separated by a comma. These numbers specify the extent of the overlay.
301+
280302
### CHANGELOG
281303

282304
The latest changes can be found here: [CHANGELOG.md](https://github.com/panodata/grafana-map-panel/blob/master/CHANGELOG.md)

src/images/overlay_example.png

144 KB
Loading

src/partials/editor.html

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,48 @@ <h5>Popup labels</h5>
549549
</div>
550550
</div>
551551

552+
<div class="gf-form-group">
553+
<h5>Custom image overlay</h5>
554+
<gf-form-switch class="gf-form" label="Enable" label-class="width-10" checked="ctrl.panel.enableOverlay"
555+
on-change="ctrl.refreshOverlay()">
556+
</gf-form-switch>
557+
<div class="gf-form" ng-show="ctrl.panel.enableOverlay == true">
558+
<label class="gf-form-label width-10">Overlay URL</label>
559+
<input type="url" class="input-small gf-form-input width-10" ng-model="ctrl.panel.overlayUrl"
560+
ng-change="ctrl.refreshOverlay()"
561+
ng-model-onblur/>
562+
</div>
563+
<div class="gf-form" ng-show="ctrl.panel.enableOverlay == true">
564+
<label class="gf-form-label width-10">Overlay opacity</label>
565+
<input type="number" step="0.05" class="input-small gf-form-input width-10"
566+
ng-model="ctrl.panel.overlayOpacity" placeholder="0.8"
567+
ng-change="ctrl.refreshOverlay()"
568+
ng-model-onblur/>
569+
</div>
570+
<div class="gf-form" ng-show="ctrl.panel.enableOverlay == true">
571+
<label class="gf-form-label width-10">
572+
Latitude range
573+
<i class="grafana-tip fa fa-question-circle"
574+
bs-tooltip="'Provide the min and max latitude values, separated by a comma'"></i>
575+
</label>
576+
<input type="text" class="input-small gf-form-input width-10"
577+
ng-model="ctrl.panel.overlayRangeLatitude" placeholder="0,10"
578+
ng-change="ctrl.refreshOverlay()"
579+
ng-model-onblur/>
580+
</div>
581+
<div class="gf-form" ng-show="ctrl.panel.enableOverlay == true">
582+
<label class="gf-form-label width-10">
583+
Longitude range
584+
<i class="grafana-tip fa fa-question-circle"
585+
bs-tooltip="'Provide the min and max longitude values, separated by a comma'"></i>
586+
</label>
587+
<input type="text" class="input-small gf-form-input width-10"
588+
ng-model="ctrl.panel.overlayRangeLongitude" placeholder="0,20"
589+
ng-change="ctrl.refreshOverlay()"
590+
ng-model-onblur/>
591+
</div>
592+
</div>
593+
552594
<div class="gf-form-group">
553595
<h5>Clickthrough links</h5>
554596
<div class="gf-form">

src/worldmap.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,26 @@ describe('Worldmap', () => {
475475
});
476476
});
477477

478+
describe('when an image overlay is requested', () => {
479+
beforeEach(() => {
480+
ctrl.panel.enableOverlay = true;
481+
ctrl.panel.overlayUrl = 'http://foo.bar/overlay.png';
482+
ctrl.panel.overlayRangeLatitude = '0,1.23';
483+
ctrl.panel.overlayRangeLongitude = '-2., 3';
484+
worldMap.createOverlay();
485+
});
486+
487+
it('should create an overlay layer', () => {
488+
expect(worldMap.overlay).toBeDefined();
489+
const bounds = worldMap.overlay.getBounds();
490+
expect(bounds.getNorth()).toBe(1.23);
491+
expect(bounds.getSouth()).toBe(0);
492+
expect(bounds.getEast()).toBe(3);
493+
expect(bounds.getWest()).toBe(-2);
494+
expect(worldMap.overlay._url).toBe('http://foo.bar/overlay.png');
495+
});
496+
});
497+
478498
describe('when the data has two points at the same spot', () => {
479499
beforeEach(() => {
480500
ctrl.data = new DataBuilder()

src/worldmap.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export default class WorldMap {
2929
circles: any[];
3030
map: any;
3131
legend: any;
32+
overlay: any;
3233
circlesLayer: any;
3334

3435
constructor(ctrl, mapContainer) {
@@ -164,6 +165,26 @@ export default class WorldMap {
164165
}
165166
}
166167

168+
createOverlay() {
169+
// See https://leafletjs.com/reference-1.3.4.html#imageoverlay for example usage
170+
const latRange = this.ctrl.settings.overlayRangeLatitude
171+
.split(',')
172+
.slice(0, 2)
173+
.map(x => Number(x)),
174+
lngRange = this.ctrl.settings.overlayRangeLongitude
175+
.split(',')
176+
.slice(0, 2)
177+
.map(x => Number(x));
178+
var imageBounds = [
179+
[latRange[0], lngRange[0]],
180+
[latRange[1], lngRange[1]],
181+
];
182+
this.overlay = (window as any).L.imageOverlay(this.ctrl.settings.overlayUrl, imageBounds, {
183+
opacity: this.ctrl.settings.overlayOpacity,
184+
});
185+
this.overlay.addTo(this.map);
186+
}
187+
167188
needToRedrawCircles(data) {
168189
console.info(`Data points ${data.length}. Circles on map ${this.circles.length}.`);
169190

src/worldmap_ctrl.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ const panelDefaults = {
5858
ignoreEmptyGeohashValues: false,
5959
ignoreInvalidGeohashValues: false,
6060
stickyLabels: false,
61+
enableOverlay: false,
62+
overlayUrl: '',
63+
overlayOpacity: 0.5,
64+
overlayRangeLatitude: '0,10',
65+
overlayRangeLongitude: '0,20',
6166
clickthroughUrl: '',
6267
clickthroughOptions: {
6368
windowName: null,
@@ -457,6 +462,10 @@ export default class WorldmapCtrl extends MetricsPanelCtrl {
457462
ctrl.map.createLegend();
458463
}
459464

465+
if (!ctrl.map.overlay && ctrl.panel.enableOverlay) {
466+
ctrl.map.createOverlay();
467+
}
468+
460469
ctrl.map.drawCircles();
461470

462471
if (ctrl.mapCenterMoved) {
@@ -540,6 +549,12 @@ export default class WorldmapCtrl extends MetricsPanelCtrl {
540549
this.render();
541550
}
542551

552+
refreshOverlay() {
553+
this.map.overlay.remove();
554+
this.map.overlay = null;
555+
this.render();
556+
}
557+
543558
toggleMouseWheelZoom() {
544559
this.map.setMouseWheelZoom();
545560
this.render();

0 commit comments

Comments
 (0)