diff --git a/src/ui/public/agg_types/__tests__/buckets/_geo_hash.js b/src/ui/public/agg_types/__tests__/buckets/_geo_hash.js new file mode 100644 index 00000000000000..c4b2650cd62193 --- /dev/null +++ b/src/ui/public/agg_types/__tests__/buckets/_geo_hash.js @@ -0,0 +1,64 @@ +import expect from 'expect.js'; +import AggTypesBucketsGeoHashProvider from 'ui/agg_types/buckets/geo_hash'; + +describe('Geohash Agg', function () { + + + describe('write', function () { + + let paramWriter = new AggTypesBucketsGeoHashProvider(function PrivateMock() { + return function BucketMock(geohashProvider) { + return geohashProvider.params[4]; + }; + }, { + get: function () { + return 7;//"visualization:tileMap:maxPrecision" + } + }); + + describe('geohash', function () { + + const zoomToGeoHashPrecision = { + 0: 1, + 1: 2, + 2: 2, + 3: 2, + 4: 3, + 5: 3, + 6: 4, + 7: 4, + 8: 4, + 9: 5, + 10: 5, + 11: 6, + 12: 6, + 13: 6, + 14: 7, + 15: 7, + 16: 7, + 17: 7, + 18: 7, + 19: 7, + 20: 7, + 21: 7 + }; + + Object.keys(zoomToGeoHashPrecision).forEach((zoomLevel) => { + it(`zoom level ${zoomLevel} should correspond to correct geohash-precision`, () => { + const output = {params: {}}; + paramWriter.write({ + vis: { + hasUiState: () => true, + uiStateVal: () => zoomLevel + }, + params: { + autoPrecision: true + } + }, output); + expect(output.params.precision).to.equal(zoomToGeoHashPrecision[zoomLevel]); + }); + }); + }); + + }); +}); diff --git a/src/ui/public/agg_types/__tests__/index.js b/src/ui/public/agg_types/__tests__/index.js index b68dcfc87e2848..6abdc93af24e1c 100644 --- a/src/ui/public/agg_types/__tests__/index.js +++ b/src/ui/public/agg_types/__tests__/index.js @@ -4,6 +4,7 @@ import './agg_type'; import './agg_params'; import './bucket_count_between'; import './buckets/_histogram'; +import './buckets/_geo_hash'; import './buckets/_range'; import AggTypesIndexProvider from 'ui/agg_types/index'; import AggTypesBucketsBucketAggTypeProvider from 'ui/agg_types/buckets/_bucket_agg_type'; diff --git a/src/ui/public/agg_types/buckets/geo_hash.js b/src/ui/public/agg_types/buckets/geo_hash.js index ebc96eae78c426..230683f4514a21 100644 --- a/src/ui/public/agg_types/buckets/geo_hash.js +++ b/src/ui/public/agg_types/buckets/geo_hash.js @@ -1,37 +1,32 @@ import _ from 'lodash'; -import moment from 'moment'; import AggTypesBucketsBucketAggTypeProvider from 'ui/agg_types/buckets/_bucket_agg_type'; import precisionTemplate from 'ui/agg_types/controls/precision.html'; -export default function GeoHashAggDefinition(Private, config) { - let BucketAggType = Private(AggTypesBucketsBucketAggTypeProvider); - let defaultPrecision = 2; +import {geohashColumns} from 'ui/utils/decode_geo_hash'; - // zoomPrecision maps event.zoom to a geohash precision value - // event.limit is the configurable max geohash precision - // default max precision is 7, configurable up to 12 - const zoomPrecision = { - 1: 2, - 2: 2, - 3: 2, - 4: 3, - 5: 3, - 6: 4, - 7: 4, - 8: 5, - 9: 5, - 10: 6, - 11: 6, - 12: 7, - 13: 7, - 14: 8, - 15: 9, - 16: 10, - 17: 11, - 18: 12 - }; +export default function GeoHashAggDefinition(Private, config) { + const BucketAggType = Private(AggTypesBucketsBucketAggTypeProvider); + const defaultPrecision = 2; + const maxPrecision = parseInt(config.get('visualization:tileMap:maxPrecision'), 10) || 12; + /** + * Map Leaflet zoom levels to geohash precision levels. + * The size of a geohash column-width on the map should be at least `minGeohashPixels` pixels wide. + */ + let zoomPrecision = {}; + const minGeohashPixels = 16; + for (let zoom = 0; zoom <= 21; zoom += 1) { + const worldPixels = 256 * Math.pow(2, zoom); + zoomPrecision[zoom] = 1; + for (let precision = 2; precision <= maxPrecision; precision += 1) { + const columns = geohashColumns(precision); + if ((worldPixels / columns) >= minGeohashPixels) { + zoomPrecision[zoom] = precision; + } else { + break; + } + } + } function getPrecision(precision) { - let maxPrecision = _.parseInt(config.get('visualization:tileMap:maxPrecision')); precision = parseInt(precision, 10); @@ -75,8 +70,11 @@ export default function GeoHashAggDefinition(Private, config) { }, write: function (aggConfig, output) { const vis = aggConfig.vis; - const currZoom = vis.hasUiState() && vis.uiStateVal('mapZoom'); - const autoPrecisionVal = zoomPrecision[(currZoom || vis.params.mapZoom)]; + let currZoom; + if (vis.hasUiState()) { + currZoom = parseInt(vis.uiStateVal('mapZoom'), 10); + } + const autoPrecisionVal = zoomPrecision[currZoom >= 0 ? currZoom : parseInt(vis.params.mapZoom)]; output.params.precision = aggConfig.params.autoPrecision ? autoPrecisionVal : getPrecision(aggConfig.params.precision); } } diff --git a/src/ui/public/utils/__tests__/decode_geo_hash.js b/src/ui/public/utils/__tests__/decode_geo_hash.js new file mode 100644 index 00000000000000..a868ac53bd5006 --- /dev/null +++ b/src/ui/public/utils/__tests__/decode_geo_hash.js @@ -0,0 +1,11 @@ +import expect from 'expect.js'; +import {geohashColumns} from 'ui/utils/decode_geo_hash'; + +describe('decode_geo_hash', function () { + it('geohashColumns', function () { + expect(geohashColumns(1)).to.equal(8); + expect(geohashColumns(2)).to.equal(8 * 4); + expect(geohashColumns(3)).to.equal(8 * 4 * 8); + expect(geohashColumns(4)).to.equal(8 * 4 * 8 * 4); + }); +}); diff --git a/src/ui/public/utils/decode_geo_hash.js b/src/ui/public/utils/decode_geo_hash.js index 1a1a375c123792..6f43854bd33f1c 100644 --- a/src/ui/public/utils/decode_geo_hash.js +++ b/src/ui/public/utils/decode_geo_hash.js @@ -56,3 +56,29 @@ function refine_interval(interval, cd, mask) { } export default decodeGeoHash; + + +/** + * Get the number of geohash cells for a given precision + * + * @param {number} precision the geohash precision (1<=precision<=12). + * @param {number} axis constant for the axis 0=lengthwise (ie. columns, along longitude), 1=heightwise (ie. rows, along latitude). + * @returns {number} Number of geohash cells (rows or columns) at that precision + */ +function geohashCells(precision, axis) { + let cells = 1; + for (let i = 1; i <= precision; i += 1) { + //On odd precisions, rows divide by 4 and columns by 8. Vice-versa on even precisions. + cells *= (i % 2 === axis) ? 4 : 8; + } + return cells; +} + +/** + * Get the number of geohash columns (world-wide) for a given precision + * @param precision the geohash precision + * @returns {number} the number of columns + */ +export function geohashColumns(precision) { + return geohashCells(precision, 0); +}