Skip to content

Commit

Permalink
Geocentroid / tilemap bug fixes (#10871)
Browse files Browse the repository at this point in the history
- adds the geo-centroid metric as the new default for visualizations
- various bug fixes and improvements
    - avoid unnecessary calls to manifest
    - avoid map flicker when zooming
    - enable scroll/pinch/touch zooming
    - avoid heatmap errors
    - ensure map fills screen in dashboard
    - ensure fit works consistently
    - relax tilemap constraints
    - remove support for multi-maps
- this refactor sets the stage for new vector map visualization which will reuse the same map components
  • Loading branch information
thomasneirynck authored Mar 29, 2017
1 parent d41042a commit 556bfab
Show file tree
Hide file tree
Showing 51 changed files with 3,502 additions and 3,150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
<div>
<label>
Maximum zoom
&nbsp;<kbn-info placement="right" info="Map zoom at which all dots are displayed at full intensity. Default: 16"></kbn-info>
&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
Expand Down
50 changes: 6 additions & 44 deletions src/core_plugins/kbn_vislib_vis_types/public/tile_map.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import _ from 'lodash';
import supports from 'ui/utils/supports';
import MapsVisTypeVislibVisTypeProvider from 'ui/vis_maps/maps_vis_type';
import VisSchemasProvider from 'ui/vis/schemas';
import AggResponseGeoJsonGeoJsonProvider from 'ui/agg_response/geo_json/geo_json';
import FilterBarPushFilterProvider from 'ui/filter_bar/push_filter';
import tileMapTemplate from 'plugins/kbn_vislib_vis_types/editors/tile_map.html';

export default function TileMapVisType(Private, getAppState, courier, config) {
Expand All @@ -22,7 +20,7 @@ export default function TileMapVisType(Private, getAppState, courier, config) {
mapType: 'Scaled Circle Markers',
isDesaturated: true,
addTooltip: true,
heatMaxZoom: 16,
heatMaxZoom: 0,
heatMinOpacity: 0.1,
heatRadius: 25,
heatBlur: 15,
Expand All @@ -45,41 +43,14 @@ export default function TileMapVisType(Private, getAppState, courier, config) {
value: 'topright',
text: 'top right',
}],
mapTypes: ['Scaled Circle Markers', 'Shaded Circle Markers', 'Shaded Geohash Grid', 'Heatmap'],
mapTypes: ['Scaled Circle Markers',
'Shaded Circle Markers',
'Shaded Geohash Grid',
'Heatmap'
],
canDesaturate: !!supports.cssFilters,
editor: tileMapTemplate
},
listeners: {
rectangle: function (event) {
const agg = _.get(event, 'chart.geohashGridAgg');
if (!agg) return;

const pushFilter = Private(FilterBarPushFilterProvider)(getAppState());
const indexPatternName = agg.vis.indexPattern.id;
const field = agg.fieldName();
const filter = { geo_bounding_box: {} };
filter.geo_bounding_box[field] = event.bounds;

pushFilter(filter, false, indexPatternName);
},
mapMoveEnd: function (event) {
const vis = _.get(event, 'chart.geohashGridAgg.vis');
if (vis && vis.hasUiState()) {
vis.getUiState().set('mapCenter', event.center);
}
},
mapZoomEnd: function (event) {
const vis = _.get(event, 'chart.geohashGridAgg.vis');
if (vis && vis.hasUiState()) {
vis.getUiState().set('mapZoom', event.zoom);
}

const autoPrecision = _.get(event, 'chart.geohashGridAgg.params.autoPrecision');
if (autoPrecision) {
courier.fetch();
}
}
},
responseConverter: geoJsonConverter,
implementsRenderComplete: true,
schemas: new Schemas([
Expand All @@ -101,15 +72,6 @@ export default function TileMapVisType(Private, getAppState, courier, config) {
aggFilter: 'geohash_grid',
min: 1,
max: 1
},
{
group: 'buckets',
name: 'split',
title: 'Split Chart',
deprecate: true,
deprecateMessage: 'The Split Chart feature for Tile Maps has been deprecated.',
min: 0,
max: 1
}
])
});
Expand Down
2 changes: 1 addition & 1 deletion src/core_plugins/kibana/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ module.exports = function (kibana) {
config: tilemapConfig,
},
manifestServiceUrl: serverConfig.get('tilemap.manifestServiceUrl')
},
}
};
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,15 @@
vis-editor-agg-group {
.flex-parent(0, 1, auto);
}


}


.indented {
margin-left: 2em;
}

.vis-editor-content {
.flex-parent();
z-index: 0;
Expand Down
3 changes: 1 addition & 2 deletions src/server/config/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ module.exports = () => Joi.object({
}),
profile: Joi.boolean().default(false)
}).default(),

status: Joi.object({
allowAnonymous: Joi.boolean().default(false)
}).default(),
Expand All @@ -156,7 +155,7 @@ module.exports = () => Joi.object({
url: Joi.string(),
options: Joi.object({
attribution: Joi.string(),
minZoom: Joi.number().min(1, 'Must not be less than 1').default(1),
minZoom: Joi.number().min(0, 'Must be 0 or higher').default(0),
maxZoom: Joi.number().default(10),
tileSize: Joi.number(),
subdomains: Joi.array().items(Joi.string()).single(),
Expand Down
20 changes: 9 additions & 11 deletions src/ui/public/agg_response/geo_json/__tests__/geo_json.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('GeoJson Agg Response Converter', function () {
let tabify;
let convert;
let esResponse;
let aggs;
let expectedAggs;

beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private) {
Expand All @@ -28,26 +28,24 @@ describe('GeoJson Agg Response Converter', function () {
type: 'tile_map',
aggs: [
{ schema: 'metric', type: 'avg', params: { field: 'bytes' } },
{ schema: 'split', type: 'terms', params: { field: '@tags' } },
{ schema: 'segment', type: 'geohash_grid', params: { field: 'geo.coordinates', precision: 3 } }
{ schema: 'segment', type: 'geohash_grid', params: { field: 'geo.coordinates', precision: 3, useGeocentroid: false } }
],
params: {
isDesaturated: true,
mapType: 'Scaled%20Circle%20Markers'
}
});

aggs = {
expectedAggs = {
metric: vis.aggs[0],
split: vis.aggs[1],
geo: vis.aggs[2]
geo: vis.aggs[1]
};
}));

[ { asAggConfigResults: true }, { asAggConfigResults: false } ].forEach(function (tableOpts) {

function makeTable() {
return _.sample(_.sample(tabify(vis, esResponse, tableOpts).tables).tables);
return _.sample(tabify(vis, esResponse, tableOpts).tables);
}

function makeSingleChart(table) {
Expand All @@ -72,8 +70,8 @@ describe('GeoJson Agg Response Converter', function () {

expect(chart.title).to.be(table.title());
expect(chart.tooltipFormatter).to.be.a('function');
expect(chart.valueFormatter).to.be(aggs.metric.fieldFormatter());
expect(chart.geohashGridAgg).to.be(aggs.geo);
expect(chart.valueFormatter).to.be(expectedAggs.metric.fieldFormatter());
expect(chart.geohashGridAgg).to.be(expectedAggs.geo);
expect(chart.geoJson).to.be.an('object');
});

Expand Down Expand Up @@ -116,8 +114,8 @@ describe('GeoJson Agg Response Converter', function () {
before(function () {
table = makeTable();
chart = makeSingleChart(table);
geoColI = _.findIndex(table.columns, { aggConfig: aggs.geo });
metricColI = _.findIndex(table.columns, { aggConfig: aggs.metric });
geoColI = _.findIndex(table.columns, { aggConfig: expectedAggs.geo });
metricColI = _.findIndex(table.columns, { aggConfig: expectedAggs.metric });
});

it('should be geoJson format', function () {
Expand Down
4 changes: 3 additions & 1 deletion src/ui/public/agg_response/geo_json/geo_json.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ export default function TileMapConverterFn(Private) {

const geoI = columnIndex('segment');
const metricI = columnIndex('metric');
const centroidI = _.findIndex(table.columns, (col) => col.aggConfig.type.name === 'geo_centroid');

const geoAgg = _.get(table.columns, [geoI, 'aggConfig']);
const metricAgg = _.get(table.columns, [metricI, 'aggConfig']);

const features = rowsToFeatures(table, geoI, metricI);
const features = rowsToFeatures(table, geoI, metricI, centroidI);
const values = features.map(function (feature) {
return feature.properties.value;
});
Expand Down
15 changes: 13 additions & 2 deletions src/ui/public/agg_response/geo_json/rows_to_features.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ function unwrap(val) {
return getAcr(val) ? val.value : val;
}

function convertRowsToFeatures(table, geoI, metricI) {
function convertRowsToFeatures(table, geoI, metricI, centroidI) {

return _.transform(table.rows, function (features, row) {
const geohash = unwrap(row[geoI]);
if (!geohash) return;
Expand All @@ -23,6 +24,16 @@ function convertRowsToFeatures(table, geoI, metricI) {
location.longitude[2]
];

//courtsey of @JacobBrandt: https://github.com/elastic/kibana/pull/9676/files#diff-c7c9f237e673ff486654f6cc6caa89f6
let point = centerLatLng;
const centroid = unwrap(row[centroidI]);
if (centroid) {
point = [
centroid.lat,
centroid.lon
];
}

// order is nw, ne, se, sw
const rectangle = [
[location.latitude[0], location.longitude[0]],
Expand All @@ -37,7 +48,7 @@ function convertRowsToFeatures(table, geoI, metricI) {
type: 'Feature',
geometry: {
type: 'Point',
coordinates: centerLatLng.slice(0).reverse()
coordinates: point.slice(0).reverse()
},
properties: {
geohash: geohash,
Expand Down
2 changes: 1 addition & 1 deletion src/ui/public/agg_types/__tests__/buckets/_geo_hash.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('Geohash Agg', function () {

const paramWriter = new AggTypesBucketsGeoHashProvider(function PrivateMock() {
return function BucketMock(geohashProvider) {
return geohashProvider.params[4];
return geohashProvider.params[5];
};
}, {
get: function () {
Expand Down
2 changes: 0 additions & 2 deletions src/ui/public/agg_types/agg_params.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import AggTypesParamTypesStringProvider from 'ui/agg_types/param_types/string';
import AggTypesParamTypesRawJsonProvider from 'ui/agg_types/param_types/raw_json';
import AggTypesParamTypesBaseProvider from 'ui/agg_types/param_types/base';
export default function AggParamsFactory(Private) {


const paramTypeMap = {
field: Private(AggTypesParamTypesFieldProvider),
optioned: Private(AggTypesParamTypesOptionedProvider),
Expand Down
11 changes: 11 additions & 0 deletions src/ui/public/agg_types/agg_type.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ export default function AggTypeFactory(Private) {
this.params = new AggParams(this.params);
}

/**
* Designed for multi-value metric aggs, this method can return a
* set of AggConfigs that should replace this aggConfig in requests
*
* @method getRequestAggs
* @returns {array[AggConfig]|undefined} - an array of aggConfig objects
* that should replace this one,
* or undefined
*/
this.getRequestAggs = config.getRequestAggs || _.noop;

/**
* Designed for multi-value metric aggs, this method can return a
* set of AggConfigs that should replace this aggConfig in result sets
Expand Down
27 changes: 26 additions & 1 deletion src/ui/public/agg_types/buckets/geo_hash.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import _ from 'lodash';
import AggTypesBucketsBucketAggTypeProvider from 'ui/agg_types/buckets/_bucket_agg_type';
import VisAggConfigProvider from 'ui/vis/agg_config';
import precisionTemplate from 'ui/agg_types/controls/precision.html';
import { geohashColumns } from 'ui/utils/decode_geo_hash';

export default function GeoHashAggDefinition(Private, config) {
const BucketAggType = Private(AggTypesBucketsBucketAggTypeProvider);
const AggConfig = Private(VisAggConfigProvider);

const defaultPrecision = 2;
const maxPrecision = parseInt(config.get('visualization:tileMap:maxPrecision'), 10) || 12;
/**
Expand Down Expand Up @@ -54,6 +57,11 @@ export default function GeoHashAggDefinition(Private, config) {
default: true,
write: _.noop
},
{
name: 'useGeocentroid',
default: true,
write: _.noop
},
{
name: 'mapZoom',
write: _.noop
Expand All @@ -79,6 +87,23 @@ export default function GeoHashAggDefinition(Private, config) {
output.params.precision = aggConfig.params.autoPrecision ? autoPrecisionVal : getPrecision(aggConfig.params.precision);
}
}
]
],
getRequestAggs: function (agg) {
if (!agg.params.useGeocentroid) {
return agg;
}

/**
* By default, add the geo_centroid aggregation
*/
return [agg, new AggConfig(agg.vis, {
type: 'geo_centroid',
enabled:true,
params: {
field: agg.getField()
},
schema: 'metric'
})];
}
});
}
11 changes: 10 additions & 1 deletion src/ui/public/agg_types/controls/precision.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
</div>
</div>

<div class="vis-option-item">
<div class="vis-option-item indented">
<label>
<input type="checkbox"
name="autoPrecision"
Expand All @@ -27,3 +27,12 @@
</label>
</div>

<div class="vis-option-item indented">
<label>
<input type="checkbox"
name="useGeocentroid"
ng-model="agg.params.useGeocentroid">
Place markers off grid (use geocentroid)
</label>
</div>

6 changes: 5 additions & 1 deletion src/ui/public/agg_types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import AggTypesMetricsTopHitProvider from 'ui/agg_types/metrics/top_hit';
import AggTypesMetricsStdDeviationProvider from 'ui/agg_types/metrics/std_deviation';
import AggTypesMetricsCardinalityProvider from 'ui/agg_types/metrics/cardinality';
import AggTypesMetricsPercentilesProvider from 'ui/agg_types/metrics/percentiles';
import AggTypesMetricsGeoCentroidProvider from 'ui/agg_types/metrics/geo_centroid';
import AggTypesMetricsPercentileRanksProvider from 'ui/agg_types/metrics/percentile_ranks';
import AggTypesMetricsDerivativeProvider from 'ui/agg_types/metrics/derivative';
import AggTypesMetricsCumulativeSumProvider from 'ui/agg_types/metrics/cumulative_sum';
Expand All @@ -28,6 +29,8 @@ import AggTypesMetricsBucketSumProvider from 'ui/agg_types/metrics/bucket_sum';
import AggTypesMetricsBucketAvgProvider from 'ui/agg_types/metrics/bucket_avg';
import AggTypesMetricsBucketMinProvider from 'ui/agg_types/metrics/bucket_min';
import AggTypesMetricsBucketMaxProvider from 'ui/agg_types/metrics/bucket_max';


export default function AggTypeService(Private) {

const aggs = {
Expand All @@ -51,6 +54,7 @@ export default function AggTypeService(Private) {
Private(AggTypesMetricsBucketSumProvider),
Private(AggTypesMetricsBucketMinProvider),
Private(AggTypesMetricsBucketMaxProvider),
Private(AggTypesMetricsGeoCentroidProvider)
],
buckets: [
Private(AggTypesBucketsDateHistogramProvider),
Expand All @@ -61,7 +65,7 @@ export default function AggTypeService(Private) {
Private(AggTypesBucketsTermsProvider),
Private(AggTypesBucketsFiltersProvider),
Private(AggTypesBucketsSignificantTermsProvider),
Private(AggTypesBucketsGeoHashProvider)
Private(AggTypesBucketsGeoHashProvider),
]
};

Expand Down
Loading

0 comments on commit 556bfab

Please sign in to comment.