Clamp the centroid to stay within the box boundaries (#13581) (#13728)

* Clamp the centroid to stay within the box boundaries

If a document has two or more geo fields, then the centroid calculation
will place the centroid in between the two geo fields. This can cause
the centroid to be in the middle of nowhere so clamp it to keep it
closer to where it should be.

* Add geocentroid and clamping tests
This commit is contained in:
Peter Pisljar 2017-08-28 17:00:08 +02:00 committed by GitHub
parent 3095b3bffb
commit a93ee59b5a
2 changed files with 106 additions and 16 deletions

View file

@ -15,6 +15,8 @@ describe('GeoJson Agg Response Converter', function () {
let esResponse;
let expectedAggs;
let createVis;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private) {
const Vis = Private(VisProvider);
@ -24,22 +26,29 @@ describe('GeoJson Agg Response Converter', function () {
tabify = Private(AggResponseTabifyProvider);
convert = Private(AggResponseGeoJsonProvider);
vis = new Vis(indexPattern, {
type: 'tile_map',
aggs: [
{ schema: 'metric', type: 'avg', params: { field: 'bytes' } },
{ schema: 'segment', type: 'geohash_grid', params: { field: 'geo.coordinates', precision: 3, useGeocentroid: false } }
],
params: {
isDesaturated: true,
mapType: 'Scaled%20Circle%20Markers'
}
});
createVis = function (useGeocentroid) {
vis = new Vis(indexPattern, {
type: 'tile_map',
aggs: [
{ schema: 'metric', type: 'avg', params: { field: 'bytes' } },
{ schema: 'segment', type: 'geohash_grid', params: { field: 'geo.coordinates', precision: 3, useGeocentroid: useGeocentroid } }
],
params: {
isDesaturated: true,
mapType: 'Scaled%20Circle%20Markers'
}
});
expectedAggs = {
metric: vis.aggs[0],
geo: vis.aggs[1]
expectedAggs = {
metric: vis.aggs[0],
geo: vis.aggs[1]
};
if (useGeocentroid) {
expectedAggs.centroid = vis.aggs[2];
}
};
createVis(false);
}));
[ { asAggConfigResults: true }, { asAggConfigResults: false } ].forEach(function (tableOpts) {
@ -174,6 +183,80 @@ describe('GeoJson Agg Response Converter', function () {
});
});
});
describe('geocentroid', function () {
const createEsResponse = function (position = { lat: 37, lon: -122 }) {
esResponse = {
took: 1,
timed_out: false,
_shards: {
total: 4,
successful: 4,
failed: 0
},
hits: {
total: 61005,
max_score: 0.0,
hits: []
},
aggregations: {
2: {
buckets: [{
key: '9q',
doc_count: 10307,
1: {
value: 10307
},
3: {
location: position
}
}]
}
}
};
};
beforeEach(function () {
createEsResponse();
createVis(true);
});
it('should use geocentroid', function () {
const chart = makeSingleChart();
expect(chart.geoJson.features[0].geometry.coordinates).to.eql([ -122, 37 ]);
});
// 9q has latitude boundaries of 33.75 to 39.375
// 9q has longituted boundaries of -123.75 to -112.5
[
{
lat: 30,
lon: -122,
expected: [ -122, 33.75 ]
},
{
lat: 45,
lon: -122,
expected: [ -122, 39.375 ]
},
{
lat: 37,
lon: -130,
expected: [ -123.75, 37 ]
},
{
lat: 37,
lon: -110,
expected: [ -112.5, 37 ]
}
].forEach(function (position) {
it('should clamp geocentroid ' + JSON.stringify(position), function () {
createEsResponse(position);
const chart = makeSingleChart();
expect(chart.geoJson.features[0].geometry.coordinates).to.eql(position.expected);
});
});
});
});
});
});

View file

@ -10,6 +10,12 @@ function unwrap(val) {
return getAcr(val) ? val.value : val;
}
function clampGrid(val, min, max) {
if (val > max) val = max;
else if (val < min) val = min;
return val;
}
export function convertRowsToFeatures(table, geoI, metricI, centroidI) {
return _.transform(table.rows, function (features, row) {
@ -28,9 +34,10 @@ export function convertRowsToFeatures(table, geoI, metricI, centroidI) {
let point = centerLatLng;
const centroid = unwrap(row[centroidI]);
if (centroid) {
// see https://github.com/elastic/elasticsearch/issues/24694 for why clampGrid is used
point = [
centroid.lat,
centroid.lon
clampGrid(centroid.lat, location.latitude[0], location.latitude[1]),
clampGrid(centroid.lon, location.longitude[0], location.longitude[1])
];
}