From 439200e594b4318da6c8782eacb143c75082f180 Mon Sep 17 00:00:00 2001 From: nreese Date: Wed, 5 Oct 2016 15:23:36 -0600 Subject: [PATCH] filter geohash_grid agg with geo_bounding_box collar --- README.md | 6 +-- public/callbacks.js | 6 +++ public/options.html | 9 +++++ public/utils.js | 45 +++++++++++++++++++++++ public/vis.js | 1 + public/visController.js | 41 ++++++++++++++++++++- public/vislib/_map.js | 29 +++++++-------- public/vislib/marker_types/base_marker.js | 19 +--------- 8 files changed, 119 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 15f8a49..b60e1ea 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ Kibana ships with a functional tilemap visualization. This plugin provides an ad ### Add Markers to map Allows for the placement of markers on the tilemap. Markers are displayed when in the visualization panel or the dashboard panel. Markers can only be added and removed when in the visualization panel. +### Filter geohash_grid aggregation by geo_bounding_box collar +Resolves issue [Filter geohash_grid aggregation to map view box with collar](https://github.com/elastic/kibana/issues/8087) + ### Geo Polygon Query Click the polygon icon and draw a polygon on the map. The enhanced tilemap plugin will create a geo_polygon filter. @@ -49,9 +52,6 @@ The kibana tilemap plugin has been updated with several pull-requests but none o * https://github.com/elastic/kibana/pull/8000 * https://github.com/elastic/kibana/pull/6835 -### geo aggregation filtered by geo_bounding_box collar -[Filter geohash_grid aggregation to map view box with collar](https://github.com/elastic/kibana/issues/8087) - # Install ## Kibana 4.x ```bash diff --git a/public/callbacks.js b/public/callbacks.js index 9535b9b..d3b8199 100644 --- a/public/callbacks.js +++ b/public/callbacks.js @@ -85,6 +85,12 @@ define(function (require) { editableAgg.params.mapZoom = event.zoom; editableAgg.params.mapCenter = center; } + + //Fetch new data if map bounds are outsize of collar + const bounds = utils.scaleBounds(event.mapBounds, 1); + if(!utils.contains(event.collar, bounds)) { + courier.fetch(); + } }, mapZoomEnd: function (event) { const agg = _.get(event, 'chart.geohashGridAgg'); diff --git a/public/options.html b/public/options.html index 55201b6..7edc5e1 100644 --- a/public/options.html +++ b/public/options.html @@ -10,6 +10,15 @@ +
+ + +
+
diff --git a/public/utils.js b/public/utils.js index 0a2c5fc..6df18d2 100644 --- a/public/utils.js +++ b/public/utils.js @@ -1,6 +1,51 @@ define(function (require) { const _ = require('lodash'); return { + /* + * @param bounds {LatLngBounds} + * @param scale {number} + * @return {object} + */ + scaleBounds: function(bounds, scale) { + let safeScale = scale; + if(safeScale < 1) scale = 1; + if(safeScale > 5) scale = 5; + + const topLeft = bounds.getNorthEast(); + const bottomRight = bounds.getSouthWest(); + let latDiff = Math.abs(topLeft.lat - bottomRight.lat); + let lonDiff = Math.abs(bottomRight.lng - topLeft.lng); + //map height can be zero when vis is first created + if(latDiff === 0) latDiff = lonDiff; + + let topLeftLat = topLeft.lat + (latDiff * safeScale); + if(topLeftLat > 90) topLeftLat = 90; + let bottomRightLat = bottomRight.lat - (latDiff * safeScale); + if(bottomRightLat < -90) bottomRightLat = -90; + let topLeftLon = topLeft.lng - (lonDiff * safeScale); + if(topLeftLon < -180) topLeftLon = -180; + let bottomRightLon = bottomRight.lng + (lonDiff * safeScale); + if(bottomRightLon > 180) bottomRightLon = 180; + + return { + "top_left": {lat: topLeftLat, lon: topLeftLon}, + "bottom_right": {lat: bottomRightLat, lon: bottomRightLon} + }; + }, + contains: function(collar, bounds) { + //test if bounds top_left is inside collar + if(bounds.top_left.lat > collar.top_left.lat + || bounds.top_left.lon < collar.top_left.lon) + return false; + + //test if bounds bottom_right is inside collar + if(bounds.bottom_right.lat < collar.bottom_right.lat + || bounds.bottom_right.lon > collar.bottom_right.lon) + return false; + + //both corners are inside collar so collar contains + return true; + }, getMapStateFromVis: function(vis) { const mapState = { center: [15, 5], diff --git a/public/vis.js b/public/vis.js index 697c3a0..2cff4ab 100644 --- a/public/vis.js +++ b/public/vis.js @@ -21,6 +21,7 @@ define(function (require) { params: { defaults: { mapType: 'Scaled Circle Markers', + collarScale: 1.5, scaleType: 'dynamic', scaleBands: [{ low: 0, diff --git a/public/visController.js b/public/visController.js index 660372c..6d35187 100644 --- a/public/visController.js +++ b/public/visController.js @@ -21,10 +21,48 @@ define(function (require) { let TileMapMap = Private(MapProvider); const geoJsonConverter = Private(AggResponseGeoJsonGeoJsonProvider); let map = null; + let collar = null; appendMap(); + $scope.vis.aggs.origToDsl = $scope.vis.aggs.toDsl; + $scope.vis.aggs.toDsl = function() { + resizeArea(); + const dsl = $scope.vis.aggs.origToDsl(); + + //append map collar filter to geohash_grid aggregation + _.keys(dsl).forEach(function(key) { + if(_.has(dsl[key], "geohash_grid")) { + const origAgg = dsl[key]; + dsl[key] = { + filter: aggFilter(origAgg.geohash_grid.field), + aggs: { + filtered_geohash: origAgg + } + } + } + }); + return dsl; + } + function aggFilter(field) { + collar = utils.scaleBounds( + map.mapBounds(), + $scope.vis.params.collarScale); + var filter = {geo_bounding_box: {}}; + filter.geo_bounding_box[field] = collar; + return filter; + } + //Useful bits of ui/public/vislib_vis_type/buildChartData.js function buildChartData(resp) { + const aggs = resp.aggregations; + _.keys(aggs).forEach(function(key) { + if(_.has(aggs[key], "filtered_geohash")) { + aggs[key].buckets = aggs[key].filtered_geohash.buckets; + delete aggs[key].filtered_geohash; + console.log("geogrids: " + aggs[key].buckets.length); + if(aggs[key].buckets.length === 0) return; + } + }); var tableGroup = aggResponse.tabify($scope.vis, resp, { canSplit: true, asAggConfigResults: true @@ -59,7 +97,8 @@ define(function (require) { chartData, $scope.vis.params, Private(require('ui/agg_response/geo_json/_tooltip_formatter')), - _.get(chartData, 'valueFormatter', _.identity)); + _.get(chartData, 'valueFormatter', _.identity), + collar); } }); diff --git a/public/vislib/_map.js b/public/vislib/_map.js index 9f6b25b..f0952ff 100644 --- a/public/vislib/_map.js +++ b/public/vislib/_map.js @@ -215,24 +215,18 @@ define(function (require) { * * @method _addMarkers */ - TileMapMap.prototype.addMarkers = function (chartData, newParams, tooltipFormatter, valueFormatter) { - if(newParams) { - this._setMarkerType(newParams.mapType); - this._setAttr(newParams); - } - if(chartData) { - this._chartData = chartData; - this._geoJson = _.get(chartData, 'geoJson'); - } - if(tooltipFormatter) this._tooltipFormatter = tooltipFormatter; - if(valueFormatter) this._valueFormatter = valueFormatter; - if(!this._tooltipFormatter) return; + TileMapMap.prototype.addMarkers = function (chartData, newParams, tooltipFormatter, valueFormatter, collar) { + this._setMarkerType(newParams.mapType); + this._setAttr(newParams); + this._chartData = chartData; + this._geoJson = _.get(chartData, 'geoJson'); + this._collar = collar; if (this._markers) this._markers.destroy(); this._markers = this._createMarkers({ - tooltipFormatter: this._tooltipFormatter, - valueFormatter: this._valueFormatter, + tooltipFormatter: tooltipFormatter, + valueFormatter: valueFormatter, attr: this._attr }); @@ -280,6 +274,10 @@ define(function (require) { this._layerControl.addOverlay(this._overlay, name); }; + TileMapMap.prototype.mapBounds = function () { + return this.map.getBounds(); + }; + /** * Create the marker instance using the given options * @@ -326,10 +324,11 @@ define(function (require) { // update internal center and zoom references self._mapCenter = self.map.getCenter(); self._mapZoom = self.map.getZoom(); - self.addMarkers(); self._callbacks.mapMoveEnd({ chart: self._chartData, + collar: self._collar, + mapBounds: self.mapBounds(), map: self.map, center: self._mapCenter, zoom: self._mapZoom, diff --git a/public/vislib/marker_types/base_marker.js b/public/vislib/marker_types/base_marker.js index 1e8d177..16fdd62 100644 --- a/public/vislib/marker_types/base_marker.js +++ b/public/vislib/marker_types/base_marker.js @@ -176,8 +176,7 @@ define(function (require) { style: function (feature) { let value = _.get(feature, 'properties.value'); return self.applyShadingStyle(value); - }, - filter: self._filterToMapBounds() + } }; if(self.geoJson.features.length <= 250) { @@ -209,22 +208,6 @@ define(function (require) { this._addToMap(); }; - /** - * return whether feature is within map bounds - * - * @method _filterToMapBounds - * @param map {Leaflet Object} - * @return {boolean} - */ - BaseMarker.prototype._filterToMapBounds = function () { - let self = this; - return function (feature) { - let mapBounds = self.map.getBounds(); - let bucketRectBounds = _.get(feature, 'properties.rectangle'); - return mapBounds.intersects(bucketRectBounds); - }; - }; - /** * Checks if event latlng is within bounds of mapData * features and shows tooltip for that feature