Upgrade kibana to leaflet 1.x (#12367)

* upgrade leaflet modules to 1.x latest

* fix css for leaflet controls

* update draw options so it is easier to see polygon when drawing

* fix chrome touch issues with closing polygon in leaflet draw

* use canvas renderer

* use leaflet-responsive-popup to avoid tooltip cutoff

* remove radius configuration from leaflet heatmap

* make blur a factor of radius. Set maxZoom to map zoom since new values are calculated per precision

* use _.get to avoid error, cannot read property of undefined

* add cluster size slider

* experiments with image differences

* onload not onLoad

* use canvas dimensions

* compare map canvas to stored PNG of map canvas

* remove pixelmatch from project dependecies

* fix broken test - rounding error

* add expected image tests for geohash layers shaded circles and geohash grids

* bump z-index of vis-spy

* update functional test expected data

* update to leaflet 1.2.0

* revert to leaflet 1.0.3 and update expected data set for map functional tests

* test geohash_layer heatmap in unit test

* update region_map functional test since it can not longer pluck map vectors from DOM

* update documentation
This commit is contained in:
Nathan Reese 2017-08-22 17:26:30 -06:00 committed by GitHub
parent 0d281a7566
commit 599c8c4220
22 changed files with 216 additions and 641 deletions

View file

@ -73,9 +73,16 @@ This is no longer the case. Now, only commas are a valid query separator: e.g. `
*Impact:* No change is required for existing Kibana index patterns. Those previously configured with this option will gracefully use the new Elasticsearch optimizations instead, as will all new index patterns.
[float]
=== Replace markdown parser `marked` with `markdown-it`
*Details:* Starting in 6.0.0, Kibana will use `markdown-it` to parse markdown text. Kibana switched to `markdown-it` because `marked` is no longer actively maintained. Markdown-it supports CommonMark and GFM (GitHub Flavored Markdown) Tables and Strikethrough.
*Impact:* There may be slight changes in parsed markdown. Review markdown as needed.
[float]
=== Simplified `Coordinate Map - Heatmap` configuration controls
*Details:* Replaced `Coordinate Map - Heatmap` configuration controls `Radius`, `Blur`, `Maximum Zoom`, and `Minimum opacity` with a single control, `Cluster size`.
*Impact:* No change is required for existing Coordinate Map visualizations, `Cluster size` will be set to the default value. `Cluster size` may need to be adjusted as needed.

View file

@ -34,7 +34,7 @@ Enter a string in the *Custom Label* field to change the display label.
===== Buckets
Coordinate maps use the {es-ref}search-aggregations-bucket-geohashgrid-aggregation.html[_geohash_] aggregation. Select a field, typically coordinates, from the
drop-down.
drop-down.
- The_Change precision on map zoom_ box is checked by default. Uncheck the box to disable this behavior.
The _Precision_ slider determines the granularity of the results displayed on the map. See the documentation
@ -77,11 +77,7 @@ shades based on the metric aggregation's value.
*_Heatmap_*:: A heat map applies blurring to the circle markers and applies shading based on the amount of overlap.
Heatmaps have the following options:
* *Radius*: Sets the size of the individual heatmap dots.
* *Blur*: Sets the amount of blurring for the heatmap dots.
* *Maximum zoom*: Tilemaps in Kibana support 18 zoom levels. This slider defines the maximum zoom level at which the
heatmap dots appear at full intensity.
* *Minimum opacity*: Sets the opacity cutoff for the dots.
* *Cluster size*: Adjust the size of the heatmap clustering.
* *Show Tooltip*: Check this box to have a tooltip with the values for a given dot when the cursor is on that dot.
*Desaturate map tiles*:: Desaturate the map's color in order to make the markers stand out more clearly.

View file

@ -74,8 +74,6 @@
"dependencies": {
"@elastic/datemath": "2.3.0",
"@elastic/filesaver": "1.1.2",
"@elastic/leaflet-draw": "0.2.3",
"@elastic/leaflet-heat": "0.1.3",
"@elastic/numeral": "2.2.1",
"@elastic/test-subj-selector": "0.2.1",
"@elastic/ui-ace": "0.2.3",
@ -145,7 +143,10 @@
"json-loader": "0.5.3",
"json-stringify-safe": "5.0.1",
"jstimezonedetect": "1.0.5",
"leaflet": "0.7.5",
"leaflet": "1.0.3",
"leaflet-draw": "0.4.10",
"leaflet-responsive-popup": "0.2.0",
"leaflet.heat": "0.2.0",
"less": "2.7.1",
"less-loader": "2.2.3",
"lodash": "3.10.1",
@ -268,6 +269,7 @@
"ncp": "2.0.0",
"nock": "8.0.0",
"node-sass": "3.8.0",
"pixelmatch": "4.0.2",
"proxyquire": "1.7.10",
"sass-loader": "4.0.0",
"simple-git": "1.37.0",

View file

@ -1,14 +1,19 @@
import expect from 'expect.js';
import pixelmatch from 'pixelmatch';
import { KibanaMap } from '../kibana_map';
import { GeohashLayer } from '../geohash_layer';
import { GeoHashSampleData } from './geohash_sample_data';
import heatmapPng from './heatmap.png';
import scaledCircleMarkersPng from './scaledCircleMarkers.png';
import shadedCircleMarkersPng from './shadedCircleMarkers.png';
import shadedGeohashGridPng from './shadedGeohashGrid.png';
describe('kibana_map tests', function () {
let domNode;
let expectCanvas;
let kibanaMap;
function setupDOM() {
domNode = document.createElement('div');
domNode.style.top = '0';
@ -18,14 +23,17 @@ describe('kibana_map tests', function () {
domNode.style.position = 'fixed';
domNode.style['pointer-events'] = 'none';
document.body.appendChild(domNode);
expectCanvas = document.createElement('canvas');
document.body.appendChild(expectCanvas);
}
function teardownDOM() {
domNode.innerHTML = '';
document.body.removeChild(domNode);
document.body.removeChild(expectCanvas);
}
describe('GeohashGridLayer', function () {
beforeEach(async function () {
@ -48,443 +56,84 @@ describe('kibana_map tests', function () {
[
{
options: { 'mapType': 'Scaled Circle Markers' },
expected: `[
{
"fill": "#bd0026",
"d": "M343,263.8A19.2,19.2,0,1,1,342.9,263.8 z"
},
{
"fill": "#bd0026",
"d": "M343,225.03843394373595A18.961566056264047,18.961566056264047,0,1,1,342.9,225.03843394373595 z"
},
{
"fill": "#bd0026",
"d": "M283,264.19815701843777A17.80184298156226,17.80184298156226,0,1,1,282.9,264.19815701843777 z"
},
{
"fill": "#f03b20",
"d": "M405,224.2748797495895A16.72512025041049,16.72512025041049,0,1,1,404.9,224.2748797495895 z"
},
{
"fill": "#f03b20",
"d": "M285,223.50180417608374A16.498195823916255,16.498195823916255,0,1,1,284.9,223.50180417608374 z"
},
{
"fill": "#f03b20",
"d": "M343,299.1036928470748A15.896307152925205,15.896307152925205,0,1,1,342.9,299.1036928470748 z"
},
{
"fill": "#f03b20",
"d": "M283,300.2846189453604A15.71538105463958,15.71538105463958,0,1,1,282.9,300.2846189453604 z"
},
{
"fill": "#fd8d3c",
"d": "M148,267.0272116156895A13.972788384310489,13.972788384310489,0,1,1,147.9,267.0272116156895 z"
},
{
"fill": "#feb24c",
"d": "M219,270.4178825645856A11.582117435414355,11.582117435414355,0,1,1,218.9,270.4178825645856 z"
},
{
"fill": "#feb24c",
"d": "M146,189.63311915018554A11.366880849814459,11.366880849814459,0,1,1,145.9,189.63311915018554 z"
},
{
"fill": "#feb24c",
"d": "M281,191.96973262756177A11.030267372438226,11.030267372438226,0,1,1,280.9,191.96973262756177 z"
},
{
"fill": "#feb24c",
"d": "M220,231.85362974571228A10.146370254287714,10.146370254287714,0,1,1,219.9,231.85362974571228 z"
},
{
"fill": "#feb24c",
"d": "M144,231.1923722152369A9.807627784763092,9.807627784763092,0,1,1,143.9,231.1923722152369 z"
},
{
"fill": "#feb24c",
"d": "M387,268.27221854599287A9.72778145400714,9.72778145400714,0,1,1,386.9,268.27221854599287 z"
},
{
"fill": "#feb24c",
"d": "M217,191.09542834646925A8.90457165353074,8.90457165353074,0,1,1,216.9,191.09542834646925 z"
},
{
"fill": "#fed976",
"d": "M218,300.40744573968243A8.592554260317598,8.592554260317598,0,1,1,217.9,300.40744573968243 z"
},
{
"fill": "#fed976",
"d": "M363,339.5411821762003A7.458817823799684,7.458817823799684,0,1,1,362.9,339.5411821762003 z"
},
{
"fill": "#fed976",
"d": "M331,205.43072931381437A6.569270686185644,6.569270686185644,0,1,1,330.9,205.43072931381437 z"
},
{
"fill": "#fed976",
"d": "M163,299.9012571034098A5.098742896590189,5.098742896590189,0,1,1,162.9,299.9012571034098 z"
},
{
"fill": "#fed976",
"d": "M34,77.6735731867532A4.326426813246795,4.326426813246795,0,1,1,33.9,77.6735731867532 z"
},
{
"fill": "#fed976",
"d": "M268,341.7954688958982A4.204531104101819,4.204531104101819,0,1,1,267.9,341.7954688958982 z"
},
{
"fill": "#fed976",
"d": "M71,118.82649906983305A4.173500930166947,4.173500930166947,0,1,1,70.9,118.82649906983305 z"
},
{
"fill": "#fed976",
"d": "M119,235.1169130974434A3.8830869025566206,3.8830869025566206,0,1,1,118.9,235.1169130974434 z"
},
{
"fill": "#fed976",
"d": "M451,396.15053353027315A3.849466469726874,3.849466469726874,0,1,1,450.9,396.15053353027315 z"
},
{
"fill": "#fed976",
"d": "M64,104.18445019554242A3.815549804457569,3.815549804457569,0,1,1,63.9,104.18445019554242 z"
},
{
"fill": "#fed976",
"d": "M7,15.430879972386867A3.5691200276131325,3.5691200276131325,0,1,1,6.9,15.430879972386867 z"
},
{
"fill": "#fed976",
"d": "M434,206.8985557756997A3.1014442243003013,3.1014442243003013,0,1,1,433.9,206.8985557756997 z"
},
{
"fill": "#fed976",
"d": "M119,201.2073035006183A2.792696499381677,2.792696499381677,0,1,1,118.9,201.2073035006183 z"
},
{
"fill": "#fed976",
"d": "M-1,420.89773444794906A2.1022655520509095,2.1022655520509095,0,1,1,-1.1,420.89773444794906 z"
},
{
"fill": "#fed976",
"d": "M443,217.859886428343A1.1401135716569843,1.1401135716569843,0,1,1,442.9,217.859886428343 z"
},
{
"fill": "#fed976",
"d": "M121,260.85988642834303A1.1401135716569843,1.1401135716569843,0,1,1,120.9,260.85988642834303 z"
},
{
"fill": "#fed976",
"d": "M-4,399.27892886445886A0.7210711355411324,0.7210711355411324,0,1,1,-4.1,399.27892886445886 z"
}
]`
options: { mapType: 'Scaled Circle Markers' },
expected: scaledCircleMarkersPng
},
{
options: { 'mapType': 'Shaded Circle Markers' },
expected: `[
{
"fill": "#bd0026",
"d": "M343,267A16,16,0,1,1,342.9,267 z"
},
{
"fill": "#bd0026",
"d": "M343,226A18,18,0,1,1,342.9,226 z"
},
{
"fill": "#bd0026",
"d": "M283,266A16,16,0,1,1,282.9,266 z"
},
{
"fill": "#f03b20",
"d": "M405,223A18,18,0,1,1,404.9,223 z"
},
{
"fill": "#f03b20",
"d": "M285,222A18,18,0,1,1,284.9,222 z"
},
{
"fill": "#f03b20",
"d": "M343,300A15,15,0,1,1,342.9,300 z"
},
{
"fill": "#f03b20",
"d": "M283,301A15,15,0,1,1,282.9,301 z"
},
{
"fill": "#fd8d3c",
"d": "M148,265A16,16,0,1,1,147.9,265 z"
},
{
"fill": "#feb24c",
"d": "M219,266A16,16,0,1,1,218.9,266 z"
},
{
"fill": "#feb24c",
"d": "M146,183A18,18,0,1,1,145.9,183 z"
},
{
"fill": "#feb24c",
"d": "M281,184A19,19,0,1,1,280.9,184 z"
},
{
"fill": "#feb24c",
"d": "M220,225A17,17,0,1,1,219.9,225 z"
},
{
"fill": "#feb24c",
"d": "M144,224A17,17,0,1,1,143.9,224 z"
},
{
"fill": "#feb24c",
"d": "M387,262A16,16,0,1,1,386.9,262 z"
},
{
"fill": "#feb24c",
"d": "M217,181A19,19,0,1,1,216.9,181 z"
},
{
"fill": "#fed976",
"d": "M218,293A16,16,0,1,1,217.9,293 z"
},
{
"fill": "#fed976",
"d": "M363,333A14,14,0,1,1,362.9,333 z"
},
{
"fill": "#fed976",
"d": "M331,194A18,18,0,1,1,330.9,194 z"
},
{
"fill": "#fed976",
"d": "M163,290A15,15,0,1,1,162.9,290 z"
},
{
"fill": "#fed976",
"d": "M34,56A26,26,0,1,1,33.9,56 z"
},
{
"fill": "#fed976",
"d": "M268,332A14,14,0,1,1,267.9,332 z"
},
{
"fill": "#fed976",
"d": "M71,100A23,23,0,1,1,70.9,100 z"
},
{
"fill": "#fed976",
"d": "M119,222A17,17,0,1,1,118.9,222 z"
},
{
"fill": "#fed976",
"d": "M451,387A13,13,0,1,1,450.9,387 z"
},
{
"fill": "#fed976",
"d": "M64,84A24,24,0,1,1,63.9,84 z"
},
{
"fill": "#fed976",
"d": "M7,-7A26,26,0,1,1,6.9,-7 z"
},
{
"fill": "#fed976",
"d": "M434,192A18,18,0,1,1,433.9,192 z"
},
{
"fill": "#fed976",
"d": "M119,185A19,19,0,1,1,118.9,185 z"
},
{
"fill": "#fed976",
"d": "M-1,410A13,13,0,1,1,-1.1,410 z"
},
{
"fill": "#fed976",
"d": "M443,201A18,18,0,1,1,442.9,201 z"
},
{
"fill": "#fed976",
"d": "M121,245A17,17,0,1,1,120.9,245 z"
},
{
"fill": "#fed976",
"d": "M-4,386A14,14,0,1,1,-4.1,386 z"
}
]`
options: { mapType: 'Shaded Circle Markers' },
expected: shadedCircleMarkersPng
},
{
options: { 'mapType': 'Shaded Geohash Grid' },
expected: `[
{
"fill": "#bd0026",
"d": "M313 301L313 261L377 261L377 301z"
},
{
"fill": "#bd0026",
"d": "M313 261L313 218L377 218L377 261z"
},
{
"fill": "#bd0026",
"d": "M249 301L249 261L313 261L313 301z"
},
{
"fill": "#f03b20",
"d": "M377 261L377 218L441 218L441 261z"
},
{
"fill": "#f03b20",
"d": "M249 261L249 218L313 218L313 261z"
},
{
"fill": "#f03b20",
"d": "M313 338L313 301L377 301L377 338z"
},
{
"fill": "#f03b20",
"d": "M249 338L249 301L313 301L313 338z"
},
{
"fill": "#fd8d3c",
"d": "M121 301L121 261L185 261L185 301z"
},
{
"fill": "#feb24c",
"d": "M185 301L185 261L249 261L249 301z"
},
{
"fill": "#feb24c",
"d": "M121 218L121 170L185 170L185 218z"
},
{
"fill": "#feb24c",
"d": "M249 218L249 170L313 170L313 218z"
},
{
"fill": "#feb24c",
"d": "M185 261L185 218L249 218L249 261z"
},
{
"fill": "#feb24c",
"d": "M121 261L121 218L185 218L185 261z"
},
{
"fill": "#feb24c",
"d": "M377 301L377 261L441 261L441 301z"
},
{
"fill": "#feb24c",
"d": "M185 218L185 170L249 170L249 218z"
},
{
"fill": "#fed976",
"d": "M185 338L185 301L249 301L249 338z"
},
{
"fill": "#fed976",
"d": "M313 374L313 338L377 338L377 374z"
},
{
"fill": "#fed976",
"d": "M313 218L313 170L377 170L377 218z"
},
{
"fill": "#fed976",
"d": "M121 338L121 301L185 301L185 338z"
},
{
"fill": "#fed976",
"d": "M-7 116L-7 54L57 54L57 116z"
},
{
"fill": "#fed976",
"d": "M249 374L249 338L313 338L313 374z"
},
{
"fill": "#fed976",
"d": "M57 170L57 116L121 116L121 170z"
},
{
"fill": "#fed976",
"d": "M57 261L57 218L121 218L121 261z"
},
{
"fill": "#fed976",
"d": "M441 408L441 374L505 374L505 408z"
},
{
"fill": "#fed976",
"d": "M57 116L57 54L121 54L121 116z"
},
{
"fill": "#fed976",
"d": "M-7 54L-7 -21L57 -21L57 54z"
},
{
"fill": "#fed976",
"d": "M377 218L377 170L441 170L441 218z"
},
{
"fill": "#fed976",
"d": "M57 218L57 170L121 170L121 218z"
},
{
"fill": "#fed976",
"d": "M-7 441L-7 408L57 408L57 441z"
},
{
"fill": "#fed976",
"d": "M441 261L441 218L505 218L505 261z"
},
{
"fill": "#fed976",
"d": "M57 301L57 261L121 261L121 301z"
},
{
"fill": "#fed976",
"d": "M-7 408L-7 374L57 374L57 408z"
}
]`
options: { mapType: 'Shaded Geohash Grid' },
expected: shadedGeohashGridPng
},
{
options: {
mapType: 'Heatmap',
heatmap: {
heatClusterSize: '2'
}
},
expected: heatmapPng
}
].forEach(function (test) {
it(test.options.mapType, function () {
it(test.options.mapType, function (done) {
const geohashGridOptions = test.options;
const geohashLayer = new GeohashLayer(GeoHashSampleData, geohashGridOptions, kibanaMap.getZoomLevel(), kibanaMap);
kibanaMap.addLayer(geohashLayer);
const markersNodeList = domNode.querySelectorAll('path.leaflet-clickable');
const markerArray = [];
for (let i = 0; i < markersNodeList.length; i++) {
markerArray.push(markersNodeList[i]);
}
const expectedGeohashGridMarkers = test.expected;
const expectedMarkers = JSON.parse(expectedGeohashGridMarkers).map(path => {
return {
fill: path.fill,
coords: path.d.match(/[0-9\.]+/g).map(parseFloat)
// Give time for canvas to render before checking output
window.setTimeout(() => {
// Extract image data from live map
const elementList = domNode.querySelectorAll('canvas');
expect(elementList.length).to.equal(1);
const canvas = elementList[0];
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// convert expect PNG into pixel data by drawing in new canvas element
expectCanvas.id = 'expectCursor';
expectCanvas.width = canvas.width;
expectCanvas.height = canvas.height;
const imageEl = new Image();
imageEl.onload = () => {
const expectCtx = expectCanvas.getContext('2d');
expectCtx.drawImage(imageEl, 0, 0, canvas.width, canvas.height); // draw reference image to size of generated image
const expectImageData = expectCtx.getImageData(0, 0, canvas.width, canvas.height);
// compare live map vs expected pixel data
const diffImage = expectCtx.createImageData(canvas.width, canvas.height);
const mismatchedPixels = pixelmatch(
imageData.data,
expectImageData.data,
diffImage.data,
canvas.width,
canvas.height,
{ threshold: 0.1 });
expect(mismatchedPixels < 16).to.equal(true);
// Display difference image for refernce
expectCtx.putImageData(diffImage, 0, 0);
done();
};
});
const actualMarkers = markerArray.map(a => {
return {
fill: a.getAttribute('fill'),
coords: a.getAttribute('d').match(/[0-9\.]+/g).map(parseFloat)
};
});
expect(actualMarkers.length).to.equal(expectedMarkers.length);
for (let i = 0; i < expectedMarkers.length; i++) {
expect(actualMarkers[i].fill).to.equal(expectedMarkers[i].fill);
actualMarkers[i].coords.forEach((coord, c) => {
closeTo(actualMarkers[i].coords[c], expectedMarkers[i].coords[c]);
});
}
imageEl.src = test.expected;
// Instructions for creating expected image PNGs
// Comment out imageEl creation and image loading
// Comment out teardown line that removes expectCanvas from DOM
// Uncomment out below lines. Run test, right click canvas and select "Save Image As"
// const expectCtx = expectCanvas.getContext('2d');
// expectCtx.putImageData(imageData, 0, 0);
// done();
}, 200);
});
});
it('should not throw when fitting on empty-data layer', function () {
const geohashLayer = new GeohashLayer({
type: 'FeatureCollection',
features: []
@ -495,14 +144,5 @@ describe('kibana_map tests', function () {
kibanaMap.fitToData();
}).to.not.throwException();
});
});
});
function closeTo(actual, expected) {
const epsilon = 1;//allow 2px slack
expect(actual - epsilon < expected && expected < actual + epsilon).to.equal(true);
}

View file

@ -1335,7 +1335,13 @@ const sampleData = `{
"rectangle": [[16.875, -146.25], [16.875, -135], [22.5, -135], [22.5, -146.25]]
}
}],
"properties": {"min": 2, "max": 1418, "zoom": 3, "center": [39.57182223734374, -109.51171875]}
"properties": {
"min": 2,
"max": 1418,
"zoom": 3,
"center": [39.57182223734374, -109.51171875],
"geohashGridDimensionsAtEquator": [1252300, 624100]
}
}`;
export const GeoHashSampleData = JSON.parse(sampleData);

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View file

@ -88,8 +88,8 @@ describe('kibana_map tests', function () {
it('should get untrimmed map bounds', function () {
const bounds = kibanaMap.getUntrimmedBounds(false);
expect(bounds.bottom_right.lon).to.equal(281.25);
expect(bounds.top_left.lon).to.equal(-281.25);
expect(bounds.bottom_right.lon.toFixed(2)).to.equal('281.25');
expect(bounds.top_left.lon.toFixed(2)).to.equal('-281.25');
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View file

@ -15,85 +15,21 @@
<div ng-if="vis.params.mapType === 'Heatmap'" class="form-group">
<div>
<label>
Radius
&nbsp;<kbn-info placement="right" info="Size of heatmap dots. Default: 25"></kbn-info>
Cluster size
</label>
<div class="vis-editor-agg-form-row">
<input
name="heatRadius"
ng-model="vis.params.heatRadius"
name="heatClusterSize"
ng-model="vis.params.heatClusterSize"
required
class="form-control"
type="range"
min="5"
max="50"
step="1"
>
<div class="form-group vis-editor-agg-form-value">
{{vis.params.heatRadius}}
</div>
</div>
</div>
<div>
<label>
Blur
&nbsp;<kbn-info placement="right" info="Amount of blur applied to dots. Default: 15"></kbn-info>
</label>
<div class="vis-editor-agg-form-row">
<input
name="heatBlur"
ng-model="vis.params.heatBlur"
required
class="form-control"
type="range"
min="1"
max="25"
step="1"
>
<div class="form-group vis-editor-agg-form-value">
{{vis.params.heatBlur}}
</div>
</div>
</div>
<div>
<label>
Maximum zoom
&nbsp;<kbn-info placement="right" info="Map zoom at which all dots are displayed at full intensity. Default: 0"></kbn-info>
</label>
<div class="vis-editor-agg-form-row">
<input
name="heatMaxZoom"
ng-model="vis.params.heatMaxZoom"
required
class="form-control"
type="range"
min="1"
max="18"
step="1"
min="1.0"
max="3.0"
step="0.1"
>
<div class="vis-editor-agg-form-value">
{{vis.params.heatMaxZoom}}
</div>
</div>
</div>
<div>
<label>
Minimum opacity
&nbsp;<kbn-info placement="right" info="Minimum opacity of dots. Default: 0.1"></kbn-info>
</label>
<div class="vis-editor-agg-form-row">
<input
name="heatMinOpacity"
ng-model="vis.params.heatMinOpacity"
required
class="form-control"
type="range"
min="0"
max="1.0"
step="0.01"
>
<div class="vis-editor-agg-form-value">
{{vis.params.heatMinOpacity}}
{{vis.params.heatClusterSize}}
</div>
</div>
</div>

View file

@ -40,11 +40,18 @@ export class GeohashLayer extends KibanaMapLayer {
this._geohashMarkers = new GeohashGridMarkers(this._geohashGeoJson, markerOptions, this._zoom, this._kibanaMap);
break;
case 'Heatmap':
let radius = 15;
if (this._geohashGeoJson.properties.geohashGridDimensionsAtEquator) {
const minGridLength = _.min(this._geohashGeoJson.properties.geohashGridDimensionsAtEquator);
const metersPerPixel = this._kibanaMap.getMetersPerPixel();
radius = (minGridLength / metersPerPixel) / 2;
}
radius = radius * parseFloat(this._geohashOptions.heatmap.heatClusterSize);
this._geohashMarkers = new HeatmapMarkers(this._geohashGeoJson, {
radius: parseFloat(this._geohashOptions.heatmap.heatRadius),
blur: parseFloat(this._geohashOptions.heatmap.heatBlur),
maxZoom: parseFloat(this._geohashOptions.heatmap.heatMaxZoom),
minOpacity: parseFloat(this._geohashOptions.heatmap.heatMinOpacity),
radius: radius,
blur: radius,
maxZoom: this._kibanaMap.getZoomLevel(),
minOpacity: 0.1,
tooltipFormatter: this._geohashOptions.tooltipFormatter
}, this._zoom, this._kibanaMap);
break;

View file

@ -100,7 +100,9 @@ export class KibanaMap extends EventEmitter {
minZoom: options.minZoom,
maxZoom: options.maxZoom,
center: options.center ? options.center : [0, 0],
zoom: options.zoom ? options.zoom : 2
zoom: options.zoom ? options.zoom : 2,
renderer: L.canvas(),
zoomAnimation: false // Desaturate map tiles causes animation rendering artifacts
};
this._leafletMap = L.map(containerNode, leafletOptions);
@ -164,7 +166,7 @@ export class KibanaMap extends EventEmitter {
}
});
} else if (drawType === 'polygon') {
const latLongs = event.layer.getLatLngs();
const latLongs = event.layer.getLatLngs()[0];
this.emit('drawCreated:polygon', {
points: latLongs.map(leafletLatLng => {
return {
@ -198,7 +200,7 @@ export class KibanaMap extends EventEmitter {
}
if (!this._popup) {
this._popup = L.popup({ autoPan: false });
this._popup = L.responsivePopup({ autoPan: false });
this._popup.setLatLng(event.position);
this._popup.setContent(event.content);
this._popup.openOn(this._leafletMap);
@ -335,6 +337,20 @@ export class KibanaMap extends EventEmitter {
return this._leafletMap.getBounds();
}
getMetersPerPixel() {
const pointC = this._leafletMap.latLngToContainerPoint(this._leafletMap.getCenter()); // center (pixels)
const pointX = [pointC.x + 1, pointC.y]; // add one pixel to x
const pointY = [pointC.x, pointC.y + 1]; // add one pixel to y
const latLngC = this._leafletMap.containerPointToLatLng(pointC);
const latLngX = this._leafletMap.containerPointToLatLng(pointX);
const latLngY = this._leafletMap.containerPointToLatLng(pointY);
const distanceX = latLngC.distanceTo(latLngX); // calculate distance between c and x (latitude)
const distanceY = latLngC.distanceTo(latLngY); // calculate distance between c and y (longitude)
return _.min([distanceX, distanceY]);
}
getBounds() {
const bounds = this._leafletMap.getBounds();
@ -415,19 +431,23 @@ export class KibanaMap extends EventEmitter {
}
addDrawControl() {
const shapeOptions = {
shapeOptions: {
stroke: false,
color: '#000'
}
};
const drawColor = '#000';
const drawOptions = {
draw: {
polyline: false,
marker: false,
circle: false,
rectangle: shapeOptions,
polygon: shapeOptions
rectangle: {
shapeOptions: {
stroke: false,
color: drawColor
}
},
polygon: {
shapeOptions: {
color: drawColor
}
}
}
};
this._leafletDrawControl = new L.Control.Draw(drawOptions);

View file

@ -145,7 +145,9 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
_recreateGeohashLayer(esResponse) {
if (esResponse === this._chartData) {
// Only recreate geohash layer when there is new aggregation data
// Exception is Heatmap: which needs to be redrawn every zoom level because the clustering is based on meters per pixel
if (this._getMapsParams().mapType !== 'Heatmap' && esResponse === this._chartData) {
return;
}
@ -247,10 +249,7 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
isFilteredByCollar: this._isFilteredByCollar(),
fetchBounds: this.getGeohashBounds.bind(this),
heatmap: {
heatBlur: newParams.heatBlur,
heatMaxZoom: newParams.heatMaxZoom,
heatMinOpacity: newParams.heatMinOpacity,
heatRadius: newParams.heatRadius
heatClusterSize: newParams.heatClusterSize
}
};
}
@ -305,7 +304,7 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
_getGeoHashAgg() {
return this.vis.aggs.find((agg) => {
return agg.type.dslName === 'geohash_grid';
return _.get(agg, 'type.dslName') === 'geohash_grid';
});
}

View file

@ -71,6 +71,11 @@
color: @tilemap-info-header-color;
}
.leaflet-bar {
border: none !important;
box-shadow: 0 1px 5px rgba(0,0,0,0.65) !important;
}
.leaflet-control-fit {
text-align: center;
background: @tilemap-leaflet-control-bg;

View file

@ -29,10 +29,7 @@ VisTypesRegistryProvider.register(function TileMapVisType(Private, getAppState,
mapType: 'Scaled Circle Markers',
isDesaturated: true,
addTooltip: true,
heatMaxZoom: 0,
heatMinOpacity: 0.1,
heatRadius: 25,
heatBlur: 15,
heatClusterSize: 1.5,
legendPosition: 'bottomright',
mapZoom: 2,
mapCenter: [0, 0],

View file

@ -1,6 +1,7 @@
import _ from 'lodash';
import { convertRowsToFeatures } from 'ui/agg_response/geo_json/rows_to_features';
import { TileMapTooltipFormatterProvider } from 'ui/agg_response/geo_json/_tooltip_formatter';
import { gridDimensions } from './grid_dimensions';
export function AggResponseGeoJsonProvider(Private) {
@ -38,7 +39,9 @@ export function AggResponseGeoJsonProvider(Private) {
min: _.min(values),
max: _.max(values),
zoom: geoAgg && geoAgg.vis.uiStateVal('mapZoom'),
center: geoAgg && geoAgg.vis.uiStateVal('mapCenter')
center: geoAgg && geoAgg.vis.uiStateVal('mapCenter'),
geohashPrecision: geoAgg && geoAgg.params.precision,
geohashGridDimensionsAtEquator: geoAgg && gridDimensions(geoAgg.params.precision)
}
}
};

View file

@ -0,0 +1,22 @@
import _ from 'lodash';
// geohash precision mapping of geohash grid cell dimensions (width x height, in meters) at equator.
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geohashgrid-aggregation.html#_cell_dimensions_at_the_equator
const gridAtEquator = {
'1': [5009400, 4992600],
'2': [1252300, 624100],
'3': [156500, 156000],
'4': [39100, 19500],
'5': [4900, 4900],
'6': [1200, 609.4],
'7': [152.9, 152.4],
'8': [38.2, 19],
'9': [4.8, 4.8],
'10': [1.2, 0.595],
'11': [0.149, 0.149],
'12': [0.037, 0.019]
};
export function gridDimensions(precision) {
return _.get(gridAtEquator, precision);
}

View file

@ -81,7 +81,7 @@ visualization {
visualize-spy {
background-color: #ffffff;
z-index: 1;
z-index: 1000;
// this element should flex
flex: 0 1 auto;
@ -188,7 +188,7 @@ visualize-spy {
.visualize-show-spy-tab {
position: absolute;
z-index: 100;
z-index: 1000;
left: 5px;
bottom: 0px;
}

View file

@ -49,47 +49,11 @@ export default function ({ getService, getPageObjects }) {
describe('vector map', function indexPatternCreation() {
it('should show results after clicking play (join on states)', function () {
const expectedColors = [{ color: 'rgb(253,209,109)' }, { color: 'rgb(164,0,37)' }];
return PageObjects.visualize.getVectorMapData()
.then(function (data) {
log.debug('Actual data-----------------------');
log.debug(data);
log.debug('---------------------------------');
expect(data).to.eql(expectedColors);
});
});
it('should change color ramp', function () {
return PageObjects.visualize.clickOptions()
.then(function () {
return PageObjects.visualize.selectFieldById('Blues', 'colorSchema');
})
.then(function () {
return PageObjects.visualize.clickGo();
})
.then(function () {
return PageObjects.header.waitUntilLoadingHasFinished();
})
.then(function () {
//this should visualize right away, without re-requesting data
return PageObjects.visualize.getVectorMapData();
})
.then(function (data) {
log.debug('Actual data-----------------------');
log.debug(data);
log.debug('---------------------------------');
const expectedColors = [{ color: 'rgb(190,215,236)' }, { color: 'rgb(7,67,136)' }];
expect(data).to.eql(expectedColors);
});
it('should show results after clicking play (join on states)', async function () {
const expectedData = 'CN,2,592,IN,2,373,US,1,194,ID,489,BR,415';
await PageObjects.visualize.openSpyPanel();
const data = await PageObjects.visualize.getDataTableData();
expect(data.trim().split('\n').join(',')).to.eql(expectedData);
});
});

View file

@ -118,61 +118,25 @@ export default function ({ getService, getPageObjects }) {
// See https://github.com/elastic/kibana/issues/13137 if this test starts failing intermittently
it('Fit data bounds should zoom to level 3', async function () {
const expectedPrecision2ZoomCircles = [
{ color: '#750000', radius: 192 },
{ color: '#750000', radius: 191 },
{ color: '#750000', radius: 177 },
{ color: '#a40000', radius: 168 },
{ color: '#a40000', radius: 167 },
{ color: '#a40000', radius: 159 },
{ color: '#a40000', radius: 156 },
{ color: '#b45100', radius: 136 },
{ color: '#b67501', radius: 111 },
{ color: '#b67501', radius: 109 },
{ color: '#b67501', radius: 108 },
{ color: '#b67501', radius: 104 },
{ color: '#b67501', radius: 101 },
{ color: '#b67501', radius: 101 },
{ color: '#b99939', radius: 84 },
{ color: '#b99939', radius: 84 },
{ color: '#b99939', radius: 74 },
{ color: '#b99939', radius: 73 },
{ color: '#b99939', radius: 73 },
{ color: '#b99939', radius: 66 },
{ color: '#b99939', radius: 60 },
{ color: '#b99939', radius: 57 },
{ color: '#b99939', radius: 57 },
{ color: '#b99939', radius: 47 },
{ color: '#b99939', radius: 43 },
{ color: '#b99939', radius: 43 },
{ color: '#b99939', radius: 43 },
{ color: '#b99939', radius: 38 },
{ color: '#b99939', radius: 36 },
{ color: '#b99939', radius: 35 },
{ color: '#b99939', radius: 34 },
{ color: '#b99939', radius: 34 },
{ color: '#b99939', radius: 31 },
{ color: '#b99939', radius: 30 },
{ color: '#b99939', radius: 28 },
{ color: '#b99939', radius: 27 },
{ color: '#b99939', radius: 24 },
{ color: '#b99939', radius: 22 },
{ color: '#b99939', radius: 19 },
{ color: '#b99939', radius: 19 },
{ color: '#b99939', radius: 15 },
{ color: '#b99939', radius: 15 },
{ color: '#b99939', radius: 15 },
{ color: '#b99939', radius: 12 },
{ color: '#b99939', radius: 9 },
{ color: '#b99939', radius: 9 }
const expectedPrecision2DataTable = [
'- dn 1,429 { "lat": 36.38058884214008, "lon": -84.78904345856186 }',
'- dp 1,418 { "lat": 41.64735764514311, "lon": -84.89821054446622 }',
'- 9y 1,215 { "lat": 36.45605112115542, "lon": -95.0664575824997 }',
'- 9z 1,099 { "lat": 42.18533764798381, "lon": -95.16736779696697 }',
'- dr 1,076 { "lat": 42.02351013780139, "lon": -73.98091798822212 }',
'- dj 982 { "lat": 31.672735499211466, "lon": -84.50815450245526 }',
'- 9v 938 { "lat": 31.380767446489873, "lon": -95.2705099188121 }',
'- 9q 722 { "lat": 36.51360723008776, "lon": -119.18302692440686 }',
'- 9w 475 { "lat": 36.39264289740669, "lon": -106.91102287667363 }',
'- cb 457 { "lat": 46.70940601270996, "lon": -95.81077801137022 }'
];
await retry.try(async() => {
await PageObjects.visualize.clickMapFitDataBounds();
const data = await PageObjects.visualize.getTileMapData();
expect(data).to.eql(expectedPrecision2ZoomCircles);
screenshots.take('map-at-zoom-3');
});
await PageObjects.visualize.clickMapFitDataBounds();
await PageObjects.visualize.openSpyPanel();
const data = await PageObjects.visualize.getDataTableData();
await compareTableData(expectedPrecision2DataTable, data.trim().split('\n'));
screenshots.take('map-at-zoom-3');
await PageObjects.visualize.closeSpyPanel();
});
/*
@ -191,7 +155,8 @@ export default function ({ getService, getPageObjects }) {
'- 9y7 73 { "lat": 35.87868071952197, "lon": -96.3330221912275 }',
'- 9ys 71 { "lat": 37.31065319536228, "lon": -94.82038319412567 }',
'- 9yn 71 { "lat": 34.57203017311617, "lon": -92.17198946946104 }',
'- 9q9 70 { "lat": 37.327310177098425, "lon": -121.70855726221842 }' ];
'- 9q9 70 { "lat": 37.327310177098425, "lon": -121.70855726221842 }'
];
const expectedTableDataZoomed = [
'- c20g 16 { "lat": 45.59211894578766, "lon": -122.47455075674225 }',
'- c28c 13 { "lat": 48.0181491561234, "lon": -122.43847891688347 }',
@ -202,7 +167,8 @@ export default function ({ getService, getPageObjects }) {
'- c2mq 9 { "lat": 47.547698873095214, "lon": -116.18850083090365 }',
'- c27x 9 { "lat": 47.753206375055015, "lon": -118.7438936624676 }',
'- c25p 9 { "lat": 46.30563497543335, "lon": -119.30418533273041 }',
'- c209 9 { "lat": 45.29028058052063, "lon": -122.9347869195044 }' ];
'- c209 9 { "lat": 45.29028058052063, "lon": -122.9347869195044 }'
];
const vizName1 = 'Visualization TileMap';
return PageObjects.visualize.clickMapZoomIn()

View file

@ -1,7 +1,12 @@
require('node_modules/leaflet/dist/leaflet.css');
window.L = module.exports = require('node_modules/leaflet/dist/leaflet');
window.L.Browser.touch = false;
window.L.Browser.pointer = false;
require('node_modules/@elastic/leaflet-heat/dist/leaflet-heat.js');
require('node_modules/leaflet.heat/dist/leaflet-heat.js');
require('node_modules/@elastic/leaflet-draw/dist/leaflet.draw.css');
require('node_modules/@elastic/leaflet-draw/dist/leaflet.draw.js');
require('node_modules/leaflet-draw/dist/leaflet.draw.css');
require('node_modules/leaflet-draw/dist/leaflet.draw.js');
require('node_modules/leaflet-responsive-popup/leaflet.responsive.popup.css');
require('node_modules/leaflet-responsive-popup/leaflet.responsive.popup.js');