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

Filter geohash_grid aggregation to map view box with collar #12806

Merged
merged 20 commits into from
Jul 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5416bd2
filter geo hash agg - merged with vis refactor
nreese Jul 7, 2017
4f8c8d5
get collar size from geohash aggregation
nreese Jul 11, 2017
b077117
fix getBounds
nreese Jul 11, 2017
073272d
add test for _buckets to handle single bucket case
nreese Jul 11, 2017
15bc2cc
use _getGeoHashAgg to update precision
nreese Jul 11, 2017
88407f0
call reload when map bounds outside of map collar
nreese Jul 12, 2017
07fe8da
update fit control to use geo_bounds aggregation metric
nreese Jul 13, 2017
93cb54a
remove geo_hash param collarScale
nreese Jul 13, 2017
99180a5
fix precision parameter test
nreese Jul 13, 2017
5ab61d0
refactor _geo_hash.js test for readability
nreese Jul 13, 2017
8742f23
do not trim map bounds
nreese Jul 14, 2017
75de435
only change aggregation state when data needs to be refected because …
nreese Jul 17, 2017
17a76b3
remove extra geo_utils.js file from tile_map plugin
nreese Jul 17, 2017
4059aac
only filter geoJson layer on client when results are not filtered on …
nreese Jul 17, 2017
c04367f
set up layer options before creating geoJson layer
nreese Jul 17, 2017
6bfe87f
add updateExtent back
nreese Jul 17, 2017
a4009e2
add getUntrimmedBounds, clean up filter option verbage, remove scale …
nreese Jul 19, 2017
cb27ed0
move check for server-side filtering for updateExtent into geohash_layer
nreese Jul 19, 2017
58cc57f
move fetchBounds to geohash_layer
nreese Jul 19, 2017
98cc386
fix _tile_map functional test, response data is different because of …
nreese Jul 20, 2017
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
29 changes: 26 additions & 3 deletions src/core_plugins/tile_map/public/__tests__/kibana_map.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,33 @@ describe('kibana_map tests', function () {

expect(kibanaMap.getCenter().lon).to.equal(0);
expect(kibanaMap.getCenter().lat).to.equal(0);
const bounds = kibanaMap.getBounds();
expect(bounds.bottom_right.lon).to.equal(180);
expect(bounds.top_left.lon).to.equal(-180);
});

});

describe('KibanaMap - getUntrimmedBounds', function () {

beforeEach(async function () {
setupDOM();
domNode.style.width = '1600px';
domNode.style.height = '1024px';
kibanaMap = new KibanaMap(domNode, {
minZoom: 1,
maxZoom: 10,
center: [0,0],
zoom: 2
});
});

afterEach(function () {
kibanaMap.destroy();
teardownDOM();
});

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);
});
});

Expand Down
31 changes: 22 additions & 9 deletions src/core_plugins/tile_map/public/geohash_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class GeohashLayer extends KibanaMapLayer {

_createGeohashMarkers() {
const markerOptions = {
isFilteredByCollar: this._geohashOptions.isFilteredByCollar,
valueFormatter: this._geohashOptions.valueFormatter,
tooltipFormatter: this._geohashOptions.tooltipFormatter
};
Expand Down Expand Up @@ -65,22 +66,34 @@ export class GeohashLayer extends KibanaMapLayer {
this._geohashMarkers.movePointer(...args);
}

getBounds() {
async getBounds() {
if (this._geohashOptions.fetchBounds) {
const geoHashBounds = await this._geohashOptions.fetchBounds();
if (geoHashBounds) {
const northEast = L.latLng(geoHashBounds.top_left.lat, geoHashBounds.bottom_right.lon);
const southWest = L.latLng(geoHashBounds.bottom_right.lat, geoHashBounds.top_left.lon);
const leaftetBounds = L.latLngBounds(southWest, northEast);
return leaftetBounds;
}
}

return this._bounds;
}

updateExtent() {
const bounds = this._kibanaMap.getLeafletBounds();
if (!this._lastBounds || !this._lastBounds.equals(bounds)) {
//this removal is required to trigger the bounds filter again
this._kibanaMap.removeLayer(this);
this._createGeohashMarkers();
this._kibanaMap.addLayer(this);
// Client-side filtering is only enabled when server-side filter is not used
if (!this._geohashOptions.isFilteredByCollar) {
const bounds = this._kibanaMap.getLeafletBounds();
if (!this._lastBounds || !this._lastBounds.equals(bounds)) {
//this removal is required to trigger the bounds filter again
this._kibanaMap.removeLayer(this);
this._createGeohashMarkers();
this._kibanaMap.addLayer(this);
}
this._lastBounds = bounds;
}
this._lastBounds = bounds;
}


isReusable(options) {

if (_.isEqual(this._geohashOptions, options)) {
Expand Down
43 changes: 37 additions & 6 deletions src/core_plugins/tile_map/public/kibana_map.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ export class KibanaMap extends EventEmitter {
this._listeners = [];
this._showTooltip = false;


const leafletOptions = {
minZoom: options.minZoom,
maxZoom: options.maxZoom,
Expand Down Expand Up @@ -178,8 +177,6 @@ export class KibanaMap extends EventEmitter {
});

this.resize();


}

setShowTooltip(showTooltip) {
Expand Down Expand Up @@ -377,6 +374,36 @@ export class KibanaMap extends EventEmitter {
};
}

getUntrimmedBounds() {
const bounds = this._leafletMap.getBounds();
if (!bounds) {
return null;
}

const southEast = bounds.getSouthEast();
const northWest = bounds.getNorthWest();
const southEastLng = southEast.lng;
const northWestLng = northWest.lng;
const southEastLat = southEast.lat;
const northWestLat = northWest.lat;

//Bounds cannot be created unless they form a box with larger than 0 dimensions
//Invalid areas are rejected by ES.
if (southEastLat === northWestLat || southEastLng === northWestLng) {
return;
}

return {
bottom_right: {
lat: southEastLat,
lon: southEastLng
},
top_left: {
lat: northWestLat,
lon: northWestLng
}
};
}

setDesaturateBaseLayer(isDesaturated) {
if (isDesaturated === this._baseLayerIsDesaturated) {
Expand Down Expand Up @@ -513,15 +540,18 @@ export class KibanaMap extends EventEmitter {
return mapBounds.intersects(bucketRectBounds);
}

fitToData() {
async fitToData() {

if (!this._leafletMap) {
return;
}

const boundsArray = await Promise.all(this._layers.map(async (layer) => {
return await layer.getBounds();
}));

let bounds = null;
this._layers.forEach(layer => {
const b = layer.getBounds();
boundsArray.forEach(async (b) => {
if (bounds) {
bounds.extend(b);
} else {
Expand Down Expand Up @@ -582,6 +612,7 @@ export class KibanaMap extends EventEmitter {
if (!centerFromUIState || centerFromMap.lon !== centerFromUIState[1] || centerFromMap.lat !== centerFromUIState[0]) {
visualization.uiStateVal('mapCenter', [centerFromMap.lat, centerFromMap.lon]);
}
uiState.set('mapBounds', this.getUntrimmedBounds());
}

this.on('dragend', persistMapStateInUiState);
Expand Down
2 changes: 1 addition & 1 deletion src/core_plugins/tile_map/public/kibana_map_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class KibanaMapLayer extends EventEmitter {
this._leafletLayer = null;
}

getBounds() {
async getBounds() {
return this._leafletLayer.getBounds();
}

Expand Down
56 changes: 53 additions & 3 deletions src/core_plugins/tile_map/public/maps_visualization.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import $ from 'jquery';
import _ from 'lodash';
import { KibanaMap } from './kibana_map';
import { GeohashLayer } from './geohash_layer';
import { SearchSourceProvider } from 'ui/courier/data_source/search_source';
import { VisAggConfigProvider } from 'ui/vis/agg_config';
// import './lib/service_settings';
import 'ui/vis/map/service_settings';
import './styles/_tilemap.less';


export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState) {
export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState, Private) {

const AggConfig = Private(VisAggConfigProvider);
const notify = new Notifier({ location: 'Coordinate Map' });
const SearchSource = Private(SearchSourceProvider);

class MapsVisualization {

Expand Down Expand Up @@ -43,6 +47,7 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
if (esResponse && typeof esResponse.geohashGridAgg === 'undefined') {
return resolve();
}

if (status.data) {
this._recreateGeohashLayer(esResponse);
}
Expand All @@ -52,6 +57,7 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
if (status.resize) {
this._kibanaMap.resize();
}

this._doRenderComplete(resolve);

});
Expand Down Expand Up @@ -82,6 +88,8 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
options.center = centerFromUIState ? centerFromUIState : this.vis.type.visConfig.defaults.mapCenter;

this._kibanaMap = new KibanaMap(containerElement, options);
uiState.set('mapZoom', this._kibanaMap.getZoomLevel());
uiState.set('mapBounds', this._kibanaMap.getUntrimmedBounds());
this._kibanaMap.addDrawControl();
this._kibanaMap.addFitControl();
this._kibanaMap.addLegendControl();
Expand All @@ -92,7 +100,10 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
this._kibanaMap.on('zoomchange', () => {
precisionChange = (previousPrecision !== this._kibanaMap.getAutoPrecision());
previousPrecision = this._kibanaMap.getAutoPrecision();
this.vis.aggs[1].params.precision = previousPrecision;
const agg = this._getGeoHashAgg();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this new guard is breaking these tests: https://github.com/thomasneirynck/kibana/blob/bf63cac183e54f64a054eb781ffba5708270710e/src/ui/public/agg_types/__tests__/buckets/_geo_hash.js#L52-L52

In that mock vis object in the test, will need to add mock aggregation.

if (agg) {
agg.params.precision = previousPrecision;
}
});
this._kibanaMap.on('zoomend', () => {

Expand Down Expand Up @@ -233,6 +244,8 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
valueFormatter: this._chartData ? this._chartData.valueFormatter : null,
tooltipFormatter: this._chartData ? this._chartData.tooltipFormatter : null,
mapType: newParams.mapType,
isFilteredByCollar: this._isFilteredByCollar(),
fetchBounds: this.getGeohashBounds.bind(this),
heatmap: {
heatBlur: newParams.heatBlur,
heatMaxZoom: newParams.heatMaxZoom,
Expand Down Expand Up @@ -266,10 +279,47 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState
this.vis.updateState();
}

}
async getGeohashBounds() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this.

Each layer should determine its bounds. We're throwing that away when creating this function that needs to be supplied to the FitControl.

I'd move this method to geohash_bounds, and modify fitToData so it can work with async results.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function is now passed to the geohash_layer. I did this instead of migrating the code to geohash_layer because several dependencies are not available in geohash_layer, like Private and vis index pattern.

const agg = this._getGeoHashAgg();
if (agg) {
const searchSource = new SearchSource();
searchSource.index(this.vis.indexPattern);
searchSource.size(0);
searchSource.aggs(function () {
const geoBoundsAgg = new AggConfig(agg.vis, {
type: 'geo_bounds',
enabled:true,
params: {
field: agg.getField()
},
schema: 'metric'
});
return {
'1': geoBoundsAgg.toDsl()
};
});
const esResp = await searchSource.fetch();
return _.get(esResp, 'aggregations.1.bounds');
}
}

_getGeoHashAgg() {
return this.vis.aggs.find((agg) => {
return agg.type.dslName === 'geohash_grid';
});
}

_isFilteredByCollar() {
const DEFAULT = false;

const agg = this._getGeoHashAgg();
if (agg) {
return _.get(agg, 'params.isFilteredByCollar', DEFAULT);
} else {
return DEFAULT;
}
}
}

return MapsVisualization;
}
Expand Down
16 changes: 11 additions & 5 deletions src/core_plugins/tile_map/public/markers/scaled_circles.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,23 @@ export class ScaledCirclesMarkers extends EventEmitter {
this._legendQuantizer = null;

this._popups = [];
this._leafletLayer = L.geoJson(null, {

const layerOptions = {
pointToLayer: this.getMarkerFunction(),
style: this.getStyleFunction(),
onEachFeature: (feature, layer) => {
this._bindPopup(feature, layer);
},
filter: (feature) => {
}
};
// Filter leafletlayer on client when results are not filtered on the server
if (!options.isFilteredByCollar) {
layerOptions.filter = (feature) => {
const bucketRectBounds = _.get(feature, 'properties.rectangle');
return kibanaMap.isInside(bucketRectBounds);
}
});
};
}
this._leafletLayer = L.geoJson(null, layerOptions);

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

Expand Down
11 changes: 11 additions & 0 deletions src/ui/public/agg_response/tabify/__tests__/_buckets.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,15 @@ describe('Buckets wrapper', function () {

test(aggResp, count, keys);
});

describe('with single bucket aggregations (filter)', function () {
it('creates single bucket from agg content', function () {
const aggResp = {
single_bucket: {},
doc_count: 5
};
const buckets = new Buckets(aggResp);
expect(buckets).to.have.length(1);
});
});
});
12 changes: 9 additions & 3 deletions src/ui/public/agg_response/tabify/_buckets.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import _ from 'lodash';

export function AggResponseBucketsProvider() {

function Buckets(aggResp) {
aggResp = aggResp || false;
this.buckets = aggResp.buckets || [];
this.objectMode = _.isPlainObject(this.buckets);
if (_.has(aggResp, 'buckets')) {
this.buckets = aggResp.buckets;
} else {
// Some Bucket Aggs only return a single bucket (like filter).
// In those instances, the aggResp is the content of the single bucket.
this.buckets = [aggResp];
}

this.objectMode = _.isPlainObject(this.buckets);
if (this.objectMode) {
this._keys = _.keys(this.buckets);
this.length = this._keys.length;
Expand Down
Loading