mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[5.x] Backport: Read map service metadata from external service (#8630)
Due to merge conflicts, manual backport of 929b4de122
This modifies the way Kibana reads out metadata about the TMS service used in the Tilemap Visualization.
- Kibana now uses an external service that exposes a 'manifest'. This contains metadata about one or more available TMS services
- Kibana continues to respect the configuration of the kibana.yml file. If a custom TMS service is set here, the manifest will not be consulted
- This also adds an API extension point for other plugins to add additional query parameters to the requests to the manifest service and for the individual tile requests to the TMS.
This commit is contained in:
parent
caba8491e8
commit
1c97dbc41d
18 changed files with 460 additions and 71 deletions
|
@ -22,6 +22,9 @@ module.exports = function (kibana) {
|
|||
},
|
||||
|
||||
uiExports: {
|
||||
|
||||
|
||||
|
||||
app: {
|
||||
id: 'kibana',
|
||||
title: 'Kibana',
|
||||
|
@ -39,10 +42,23 @@ module.exports = function (kibana) {
|
|||
],
|
||||
|
||||
injectVars: function (server, options) {
|
||||
let config = server.config();
|
||||
const serverConfig = server.config();
|
||||
|
||||
//DEPRECATED SETTINGS
|
||||
//if the url is set, the old settings must be used.
|
||||
//keeping this logic for backward compatibilty.
|
||||
const configuredUrl = server.config().get('tilemap.url');
|
||||
const isOverridden = typeof configuredUrl === 'string' && configuredUrl !== '';
|
||||
const tilemapConfig = serverConfig.get('tilemap');
|
||||
return {
|
||||
kbnDefaultAppId: config.get('kibana.defaultAppId'),
|
||||
tilemap: config.get('tilemap')
|
||||
kbnDefaultAppId: serverConfig.get('kibana.defaultAppId'),
|
||||
tilemapsConfig: {
|
||||
deprecated: {
|
||||
isOverridden: isOverridden,
|
||||
config: tilemapConfig,
|
||||
},
|
||||
manifestServiceUrl: serverConfig.get('tilemap.manifestServiceUrl')
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import _ from 'lodash';
|
||||
import Scanner from 'ui/utils/scanner';
|
||||
import 'plugins/kibana/visualize/saved_visualizations/_saved_vis';
|
||||
import RegistryVisTypesProvider from 'ui/registry/vis_types';
|
||||
import uiModules from 'ui/modules';
|
||||
|
|
|
@ -28,13 +28,19 @@ window.__KBN__ = {
|
|||
esShardTimeout: 1500,
|
||||
esApiVersion: '5.x',
|
||||
esRequestTimeout: '300000',
|
||||
tilemap: {
|
||||
url: 'https://tiles.elastic.co/v1/default/{z}/{x}/{y}.png?my_app_name=kibana&my_app_version=1.2.3&elastic_tile_service_tos=agree',
|
||||
options: {
|
||||
minZoom: 1,
|
||||
maxZoom: 10,
|
||||
attribution: '© [Elastic Tile Service](https://www.elastic.co/elastic_tile_service)'
|
||||
}
|
||||
tilemapsConfig: {
|
||||
deprecated: {
|
||||
isOverridden: true,
|
||||
config: {
|
||||
url: 'https://tiles.elastic.co/v1/default/{z}/{x}/{y}.png?my_app_name=kibana&my_app_version=1.2.3&elastic_tile_service_tos=agree',
|
||||
options: {
|
||||
minZoom: 1,
|
||||
maxZoom: 10,
|
||||
attribution: '© [Elastic Tile Service](https://www.elastic.co/elastic_tile_service)'
|
||||
}
|
||||
}
|
||||
},
|
||||
manifestServiceUrl: 'https://proxy-tiles.elastic.co/v1/manifest'
|
||||
}
|
||||
},
|
||||
uiSettings: {
|
||||
|
|
|
@ -131,11 +131,11 @@ module.exports = () => Joi.object({
|
|||
status: Joi.object({
|
||||
allowAnonymous: Joi.boolean().default(false)
|
||||
}).default(),
|
||||
|
||||
tilemap: Joi.object({
|
||||
url: Joi.string().default(`https://tiles.elastic.co/v1/default/{z}/{x}/{y}.png?my_app_name=kibana&my_app_version=${pkg.version}&elastic_tile_service_tos=agree`),
|
||||
manifestServiceUrl: Joi.string().default('https://proxy-tiles.elastic.co/v1/manifest'),
|
||||
url: Joi.string(),
|
||||
options: Joi.object({
|
||||
attribution: Joi.string().default('© [Elastic Tile Service](https://www.elastic.co/elastic-tile-service)'),
|
||||
attribution: Joi.string(),
|
||||
minZoom: Joi.number().min(1, 'Must not be less than 1').default(1),
|
||||
maxZoom: Joi.number().default(10),
|
||||
tileSize: Joi.number(),
|
||||
|
@ -146,7 +146,6 @@ module.exports = () => Joi.object({
|
|||
bounds: Joi.array().items(Joi.array().items(Joi.number()).min(2).required()).min(2)
|
||||
}).default()
|
||||
}).default(),
|
||||
|
||||
uiSettings: Joi.object({
|
||||
// this is used to prevent the uiSettings from initializing. Since they
|
||||
// require the elasticsearch plugin in order to function we need to turn
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import _ from 'lodash';
|
||||
import IndexedArray from 'ui/indexed_array';
|
||||
import RegistryVisTypesProvider from 'ui/registry/vis_types';
|
||||
let notPropsOptNames = IndexedArray.OPT_NAMES.concat('constructor');
|
||||
const notPropsOptNames = IndexedArray.OPT_NAMES.concat('constructor');
|
||||
|
||||
/**
|
||||
* Create a registry, which is just a Private module provider.
|
||||
|
|
|
@ -13,7 +13,6 @@ import AggTypesIndexProvider from 'ui/agg_types/index';
|
|||
import RegistryVisTypesProvider from 'ui/registry/vis_types';
|
||||
import VisAggConfigsProvider from 'ui/vis/agg_configs';
|
||||
import PersistedStateProvider from 'ui/persisted_state/persisted_state';
|
||||
import EventsProvider from 'ui/events';
|
||||
|
||||
export default function VisFactory(Notifier, Private) {
|
||||
let aggTypes = Private(AggTypesIndexProvider);
|
||||
|
|
|
@ -24,14 +24,16 @@ import VislibVisualizationsMapProvider from 'ui/vis_maps/visualizations/_map';
|
|||
// 'Heatmap'
|
||||
// ];
|
||||
|
||||
describe('TileMap Map Tests', function () {
|
||||
describe('tilemaptest - TileMap Map Tests', function () {
|
||||
const $mockMapEl = $('<div>');
|
||||
let TileMapMap;
|
||||
let theTileMapSettings;
|
||||
const leafletStubs = {};
|
||||
const leafletMocks = {};
|
||||
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
beforeEach(ngMock.inject(function (Private, tilemapSettings) {
|
||||
// mock parts of leaflet
|
||||
leafletMocks.tileLayer = { on: sinon.stub() };
|
||||
leafletMocks.map = { on: sinon.stub() };
|
||||
|
@ -41,13 +43,22 @@ describe('TileMap Map Tests', function () {
|
|||
leafletStubs.map = sinon.stub(L, 'map', _.constant(leafletMocks.map));
|
||||
|
||||
TileMapMap = Private(VislibVisualizationsMapProvider);
|
||||
|
||||
theTileMapSettings = tilemapSettings;
|
||||
|
||||
}));
|
||||
|
||||
async function loadTileMapSettings() {
|
||||
await theTileMapSettings.loadSettings();
|
||||
}
|
||||
|
||||
describe('instantiation', function () {
|
||||
let map;
|
||||
let createStub;
|
||||
|
||||
beforeEach(function () {
|
||||
beforeEach(loadTileMapSettings);
|
||||
|
||||
beforeEach(async function () {
|
||||
createStub = sinon.stub(TileMapMap.prototype, '_createMap', _.noop);
|
||||
map = new TileMapMap($mockMapEl, geoJsonData, {});
|
||||
});
|
||||
|
@ -69,6 +80,8 @@ describe('TileMap Map Tests', function () {
|
|||
let map;
|
||||
let mapStubs;
|
||||
|
||||
beforeEach(loadTileMapSettings);
|
||||
|
||||
beforeEach(function () {
|
||||
mapStubs = {
|
||||
destroy: sinon.stub(TileMapMap.prototype, 'destroy'),
|
||||
|
@ -112,6 +125,8 @@ describe('TileMap Map Tests', function () {
|
|||
describe('attachEvents', function () {
|
||||
let map;
|
||||
|
||||
beforeEach(loadTileMapSettings);
|
||||
|
||||
beforeEach(function () {
|
||||
sinon.stub(TileMapMap.prototype, '_createMap', function () {
|
||||
this._tileLayer = leafletMocks.tileLayer;
|
||||
|
@ -148,12 +163,13 @@ describe('TileMap Map Tests', function () {
|
|||
let map;
|
||||
let createStub;
|
||||
|
||||
beforeEach(loadTileMapSettings);
|
||||
|
||||
beforeEach(function () {
|
||||
sinon.stub(TileMapMap.prototype, '_createMap');
|
||||
createStub = sinon.stub(TileMapMap.prototype, '_createMarkers', _.constant({ addLegend: _.noop }));
|
||||
map = new TileMapMap($mockMapEl, geoJsonData, {});
|
||||
});
|
||||
|
||||
it('should pass the map options to the marker', function () {
|
||||
map._addMarkers();
|
||||
|
||||
|
@ -175,6 +191,8 @@ describe('TileMap Map Tests', function () {
|
|||
describe('getDataRectangles', function () {
|
||||
let map;
|
||||
|
||||
beforeEach(loadTileMapSettings);
|
||||
|
||||
beforeEach(function () {
|
||||
sinon.stub(TileMapMap.prototype, '_createMap');
|
||||
map = new TileMapMap($mockMapEl, geoJsonData, {});
|
||||
|
|
|
@ -38,7 +38,7 @@ const mockMap = {
|
|||
getZoom: _.constant(5)
|
||||
};
|
||||
|
||||
describe('Marker Tests', function () {
|
||||
describe('tilemaptest - Marker Tests', function () {
|
||||
let mapData;
|
||||
let markerLayer;
|
||||
|
||||
|
|
|
@ -28,11 +28,10 @@ function createTileMap(handler, chartEl, chartData) {
|
|||
chartEl = chartEl || mockChartEl;
|
||||
chartData = chartData || geoJsonData;
|
||||
|
||||
const tilemap = new TileMap(handler, chartEl, chartData);
|
||||
return tilemap;
|
||||
return new TileMap(handler, chartEl, chartData);
|
||||
}
|
||||
|
||||
describe('TileMap Tests', function () {
|
||||
describe('tilemaptest - TileMap Tests', function () {
|
||||
let tilemap;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import url from 'url';
|
||||
|
||||
describe('tilemaptest - TileMapSettingsTests-deprecated', function () {
|
||||
let theTileMapSettings;
|
||||
let theTilemapsConfig;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private, tilemapSettings, tilemapsConfig) {
|
||||
theTileMapSettings = tilemapSettings;
|
||||
theTilemapsConfig = tilemapsConfig;
|
||||
theTilemapsConfig.deprecated.isOverridden = true;
|
||||
}));
|
||||
|
||||
|
||||
describe('getting settings', function () {
|
||||
|
||||
beforeEach(async function () {
|
||||
await theTileMapSettings.loadSettings();
|
||||
});
|
||||
|
||||
it('should get url', async function () {
|
||||
|
||||
const mapUrl = theTileMapSettings.getUrl();
|
||||
expect(mapUrl.indexOf('{x}') > -1).to.be.ok();
|
||||
expect(mapUrl.indexOf('{y}') > -1).to.be.ok();
|
||||
expect(mapUrl.indexOf('{z}') > -1).to.be.ok();
|
||||
|
||||
const urlObject = url.parse(mapUrl, true);
|
||||
expect(urlObject.host.endsWith('elastic.co')).to.be.ok();
|
||||
expect(urlObject.query).to.have.property('my_app_name');
|
||||
expect(urlObject.query).to.have.property('my_app_version');
|
||||
expect(urlObject.query).to.have.property('elastic_tile_service_tos');
|
||||
|
||||
});
|
||||
|
||||
it('should get options', async function () {
|
||||
const options = theTileMapSettings.getOptions();
|
||||
expect(options).to.have.property('minZoom');
|
||||
expect(options).to.have.property('maxZoom');
|
||||
expect(options).to.have.property('attribution');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,113 @@
|
|||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import url from 'url';
|
||||
|
||||
describe('tilemaptest - TileMapSettingsTests-mocked', function () {
|
||||
let theTileMapSettings;
|
||||
let theTilemapsConfig;
|
||||
let oldGetManifest;
|
||||
|
||||
const mockGetManifest = async function () {
|
||||
const data = JSON.parse(`
|
||||
{
|
||||
"version":"0.0.0",
|
||||
"expiry":"14d",
|
||||
"services":[
|
||||
{
|
||||
"id":"road_map",
|
||||
"url":"https://proxy-tiles.elastic.co/v1/default/{z}/{x}/{y}.png",
|
||||
"minZoom":0,
|
||||
"maxZoom":12,
|
||||
"attribution":"© [Elastic Tile Service](https://www.elastic.co/elastic-tile-service)",
|
||||
"query_parameters":{
|
||||
"elastic_tile_service_tos":"agree",
|
||||
"my_app_name":"kibana"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
`);
|
||||
|
||||
return {
|
||||
data: data
|
||||
};
|
||||
};
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private, tilemapSettings, tilemapsConfig) {
|
||||
theTileMapSettings = tilemapSettings;
|
||||
theTilemapsConfig = tilemapsConfig;
|
||||
|
||||
//mock the use of a manifest
|
||||
theTilemapsConfig.deprecated.isOverridden = false;
|
||||
oldGetManifest = theTileMapSettings._getTileServiceManifest;
|
||||
theTileMapSettings._getTileServiceManifest = mockGetManifest;
|
||||
}));
|
||||
|
||||
afterEach(function () {
|
||||
//restore overrides.
|
||||
theTilemapsConfig.isOverridden = true;
|
||||
theTileMapSettings._getTileServiceManifest = oldGetManifest;
|
||||
});
|
||||
|
||||
|
||||
describe('getting settings', function () {
|
||||
|
||||
beforeEach(function (done) {
|
||||
theTileMapSettings.loadSettings().then(function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should get url', async function () {
|
||||
|
||||
const mapUrl = theTileMapSettings.getUrl();
|
||||
expect(mapUrl.indexOf('{x}') > -1).to.be.ok();
|
||||
expect(mapUrl.indexOf('{y}') > -1).to.be.ok();
|
||||
expect(mapUrl.indexOf('{z}') > -1).to.be.ok();
|
||||
|
||||
const urlObject = url.parse(mapUrl, true);
|
||||
expect(urlObject.host.endsWith('elastic.co')).to.be.ok();
|
||||
expect(urlObject.query).to.have.property('my_app_name');
|
||||
expect(urlObject.query).to.have.property('elastic_tile_service_tos');
|
||||
|
||||
});
|
||||
|
||||
it('should get options', async function () {
|
||||
const options = theTileMapSettings.getOptions();
|
||||
expect(options).to.have.property('minZoom');
|
||||
expect(options).to.have.property('maxZoom');
|
||||
expect(options).to.have.property('attribution');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('modify', function () {
|
||||
|
||||
beforeEach(function (done) {
|
||||
theTileMapSettings.addQueryParams({ foo: 'bar' });
|
||||
theTileMapSettings.addQueryParams({ bar: 'stool' });
|
||||
theTileMapSettings.addQueryParams({ foo: 'tstool' });
|
||||
theTileMapSettings.loadSettings().then(function () {
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('addQueryParameters', async function () {
|
||||
|
||||
const mapUrl = theTileMapSettings.getUrl();
|
||||
const urlObject = url.parse(mapUrl, true);
|
||||
expect(urlObject.query).to.have.property('foo');
|
||||
expect(urlObject.query).to.have.property('bar');
|
||||
expect(urlObject.query.foo).to.equal('tstool');
|
||||
expect(urlObject.query.bar).to.equal('stool');
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
import _ from 'lodash';
|
||||
|
||||
export default function MapsConfigFactory(Private) {
|
||||
export default function MapsConfigFactory() {
|
||||
|
||||
const DEFAULT_VIS_CONFIG = {
|
||||
style: {
|
||||
|
|
174
src/ui/public/vis_maps/lib/tilemap_settings.js
Normal file
174
src/ui/public/vis_maps/lib/tilemap_settings.js
Normal file
|
@ -0,0 +1,174 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import _ from 'lodash';
|
||||
import marked from 'marked';
|
||||
import url from 'url';
|
||||
import uiRoutes from 'ui/routes';
|
||||
|
||||
marked.setOptions({
|
||||
gfm: true, // Github-flavored markdown
|
||||
sanitize: true // Sanitize HTML tags
|
||||
});
|
||||
|
||||
/**
|
||||
* Reloads the setting for each route,
|
||||
* This is to ensure, that if the license changed during the lifecycle of the application,
|
||||
* we get an update.
|
||||
* tilemapSettings itself will take care that the manifest-service is not queried when not necessary.
|
||||
*/
|
||||
uiRoutes.afterSetupWork(function (tilemapSettings) {
|
||||
return tilemapSettings.loadSettings();
|
||||
});
|
||||
|
||||
uiModules.get('kibana')
|
||||
.service('tilemapSettings', function ($http, tilemapsConfig, $sanitize) {
|
||||
|
||||
const attributionFromConfig = $sanitize(marked(tilemapsConfig.deprecated.config.options.attribution || ''));
|
||||
const optionsFromConfig = _.assign({}, tilemapsConfig.deprecated.config.options, { attribution: attributionFromConfig });
|
||||
|
||||
class TilemapSettings {
|
||||
|
||||
constructor() {
|
||||
|
||||
this._queryParams = {};
|
||||
this._error = null;
|
||||
|
||||
//initialize settings with the default of the configuration
|
||||
this._url = tilemapsConfig.deprecated.config.url;
|
||||
this._options = optionsFromConfig;
|
||||
|
||||
this._invalidateSettings();
|
||||
|
||||
}
|
||||
|
||||
|
||||
_invalidateSettings() {
|
||||
|
||||
this._settingsInitialized = false;
|
||||
this._loadSettings = _.once(async() => {
|
||||
|
||||
if (tilemapsConfig.deprecated.isOverridden) {//if settings are overridden, we will use those.
|
||||
this._settingsInitialized = true;
|
||||
}
|
||||
|
||||
if (this._settingsInitialized) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let manifest;
|
||||
try {
|
||||
const response = await this._getTileServiceManifest(tilemapsConfig.manifestServiceUrl, this._queryParams,
|
||||
attributionFromConfig, optionsFromConfig);
|
||||
manifest = response.data;
|
||||
this._error = null;
|
||||
} catch (e) {
|
||||
//request failed. Continue to use old settings.
|
||||
this._settingsInitialized = true;
|
||||
this._error = new Error(`Could not retrieve map service configuration from the manifest-service. ${e.message}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
this._options = {
|
||||
attribution: $sanitize(marked(manifest.services[0].attribution)),
|
||||
minZoom: manifest.services[0].minZoom,
|
||||
maxZoom: manifest.services[0].maxZoom,
|
||||
subdomains: []
|
||||
};
|
||||
|
||||
//additional query params need to be propagated to the TMS endpoint as well.
|
||||
const queryparams = _.assign({ }, manifest.services[0].query_parameters, this._queryParams);
|
||||
const query = url.format({ query: queryparams });
|
||||
this._url = manifest.services[0].url + query;//must preserve {} patterns from the url, so do not format path.
|
||||
|
||||
this._settingsInitialized = true;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called before getUrl/getOptions can be called.
|
||||
*/
|
||||
async loadSettings() {
|
||||
return this._loadSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add optional query-parameters for the request.
|
||||
* These are only applied when requesting dfrom the manifest.
|
||||
*
|
||||
* @param additionalQueryParams
|
||||
*/
|
||||
addQueryParams(additionalQueryParams) {
|
||||
|
||||
//check if there are any changes in the settings.
|
||||
let changes = false;
|
||||
for (const key in additionalQueryParams) {
|
||||
if (additionalQueryParams.hasOwnProperty(key)) {
|
||||
if (additionalQueryParams[key] !== this._queryParams[key]) {
|
||||
changes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changes) {
|
||||
this._queryParams = _.assign({}, this._queryParams, additionalQueryParams);
|
||||
this._invalidateSettings();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url of the default TMS
|
||||
* @return {string}
|
||||
*/
|
||||
getUrl() {
|
||||
if (!this._settingsInitialized) {
|
||||
throw new Error('Cannot retrieve url before calling .loadSettings first');
|
||||
}
|
||||
return this._url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options of the default TMS
|
||||
* @return {{}}
|
||||
*/
|
||||
getOptions() {
|
||||
if (!this._settingsInitialized) {
|
||||
throw new Error('Cannot retrieve options before calling .loadSettings first');
|
||||
}
|
||||
return this._options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there was an error during initialization of the parameters
|
||||
*/
|
||||
hasError() {
|
||||
return this._error !== null;
|
||||
}
|
||||
|
||||
getError() {
|
||||
return this._error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make this instance property to allow for overrides by test code
|
||||
*/
|
||||
async _getTileServiceManifest(manifestUrl, additionalQueryParams) {
|
||||
const manifestServiceTokens = url.parse(manifestUrl);
|
||||
manifestServiceTokens.query = _.assign({}, manifestServiceTokens.query, additionalQueryParams);
|
||||
const requestUrl = url.format(manifestServiceTokens);
|
||||
return await $http({
|
||||
url: requestUrl,
|
||||
method: 'GET'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return new TilemapSettings();
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -7,7 +7,6 @@ export default function MapsVisTypeFactory(Private) {
|
|||
const VisType = Private(VisVisTypeProvider);
|
||||
const MapsRenderbot = Private(MapsVisTypeMapsRenderbotProvider);
|
||||
|
||||
|
||||
_.class(MapsVisType).inherits(VisType);
|
||||
function MapsVisType(opts = {}) {
|
||||
MapsVisType.Super.call(this, opts);
|
||||
|
|
|
@ -1,29 +1,20 @@
|
|||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import L from 'leaflet';
|
||||
import marked from 'marked';
|
||||
marked.setOptions({
|
||||
gfm: true, // Github-flavored markdown
|
||||
sanitize: true // Sanitize HTML tags
|
||||
});
|
||||
|
||||
import VislibVisualizationsMarkerTypesScaledCirclesProvider from './marker_types/scaled_circles';
|
||||
import VislibVisualizationsMarkerTypesShadedCirclesProvider from './marker_types/shaded_circles';
|
||||
import VislibVisualizationsMarkerTypesGeohashGridProvider from './marker_types/geohash_grid';
|
||||
import VislibVisualizationsMarkerTypesHeatmapProvider from './marker_types/heatmap';
|
||||
export default function MapFactory(Private, tilemap, $sanitize) {
|
||||
import '../lib/tilemap_settings';
|
||||
|
||||
export default function MapFactory(Private, tilemapSettings, Notifier) {
|
||||
const defaultMapZoom = 2;
|
||||
const defaultMapCenter = [15, 5];
|
||||
const defaultMarkerType = 'Scaled Circle Markers';
|
||||
|
||||
const tilemapOptions = tilemap.options;
|
||||
const attribution = $sanitize(marked(tilemapOptions.attribution));
|
||||
|
||||
const mapTiles = {
|
||||
url: tilemap.url,
|
||||
options: _.assign({}, tilemapOptions, { attribution })
|
||||
};
|
||||
const notify = new Notifier({
|
||||
location: 'Vis'
|
||||
});
|
||||
|
||||
const markerTypes = {
|
||||
'Scaled Circle Markers': Private(VislibVisualizationsMarkerTypesScaledCirclesProvider),
|
||||
|
@ -43,6 +34,8 @@ export default function MapFactory(Private, tilemap, $sanitize) {
|
|||
*/
|
||||
class TileMapMap {
|
||||
constructor(container, chartData, params) {
|
||||
|
||||
|
||||
this._container = $(container).get(0);
|
||||
this._chartData = chartData;
|
||||
|
||||
|
@ -52,20 +45,26 @@ export default function MapFactory(Private, tilemap, $sanitize) {
|
|||
this._valueFormatter = params.valueFormatter || _.identity;
|
||||
this._tooltipFormatter = params.tooltipFormatter || _.identity;
|
||||
this._geoJson = _.get(this._chartData, 'geoJson');
|
||||
this._mapZoom = Math.max(Math.min(params.zoom || defaultMapZoom, tilemapOptions.maxZoom), tilemapOptions.minZoom);
|
||||
|
||||
const mapOptions = tilemapSettings.getOptions();
|
||||
this._mapZoom = Math.max(Math.min(params.zoom || defaultMapZoom, mapOptions.maxZoom), mapOptions.minZoom);
|
||||
this._mapCenter = params.center || defaultMapCenter;
|
||||
this._attr = params.attr || {};
|
||||
|
||||
const mapOptions = {
|
||||
minZoom: tilemapOptions.minZoom,
|
||||
maxZoom: tilemapOptions.maxZoom,
|
||||
const options = {
|
||||
minZoom: mapOptions.minZoom,
|
||||
maxZoom: mapOptions.maxZoom,
|
||||
noWrap: true,
|
||||
maxBounds: L.latLngBounds([-90, -220], [90, 220]),
|
||||
scrollWheelZoom: false,
|
||||
fadeAnimation: false,
|
||||
};
|
||||
|
||||
this._createMap(mapOptions);
|
||||
if (tilemapSettings.hasError()) {
|
||||
notify.fatal(tilemapSettings.getError());
|
||||
}
|
||||
this._createMap(options, tilemapSettings.getUrl());
|
||||
|
||||
}
|
||||
|
||||
addBoundingControl() {
|
||||
|
@ -210,7 +209,6 @@ export default function MapFactory(Private, tilemap, $sanitize) {
|
|||
const saturateTiles = self.saturateTiles.bind(self);
|
||||
|
||||
this._tileLayer.on('tileload', saturateTiles);
|
||||
|
||||
this._tileLayer.on('load', () => {
|
||||
if (!self._events) return;
|
||||
|
||||
|
@ -300,26 +298,26 @@ export default function MapFactory(Private, tilemap, $sanitize) {
|
|||
});
|
||||
};
|
||||
|
||||
_createMap(mapOptions) {
|
||||
_createMap(options, url) {
|
||||
if (this.map) this.destroy();
|
||||
|
||||
// add map tiles layer, using the mapTiles object settings
|
||||
if (this._attr.wms && this._attr.wms.enabled) {
|
||||
_.assign(mapOptions, {
|
||||
_.assign(options, {
|
||||
minZoom: 1,
|
||||
maxZoom: 18
|
||||
});
|
||||
this._tileLayer = L.tileLayer.wms(this._attr.wms.url, this._attr.wms.options);
|
||||
} else {
|
||||
this._tileLayer = L.tileLayer(mapTiles.url, mapTiles.options);
|
||||
this._tileLayer = L.tileLayer(url, options);
|
||||
}
|
||||
|
||||
// append tile layers, center and zoom to the map options
|
||||
mapOptions.layers = this._tileLayer;
|
||||
mapOptions.center = this._mapCenter;
|
||||
mapOptions.zoom = this._mapZoom;
|
||||
options.layers = this._tileLayer;
|
||||
options.center = this._mapCenter;
|
||||
options.zoom = this._mapZoom;
|
||||
|
||||
this.map = L.map(this._container, mapOptions);
|
||||
this.map = L.map(this._container, options);
|
||||
this._attachEvents();
|
||||
this._addMarkers();
|
||||
};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import d3 from 'd3';
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import VislibVisualizationsChartProvider from './_chart';
|
||||
|
|
|
@ -90,6 +90,12 @@ class UiExports {
|
|||
this.aliases[type] = _.union(this.aliases[type] || [], spec);
|
||||
};
|
||||
|
||||
case 'visTypeEnhancers':
|
||||
return (plugin, spec) => {
|
||||
//used for plugins that augment capabilities of an existing visualization
|
||||
this.aliases.visTypes = _.union(this.aliases.visTypes || [], spec);
|
||||
};
|
||||
|
||||
case 'bundle':
|
||||
return (plugin, spec) => {
|
||||
this.bundleProviders.push(spec);
|
||||
|
|
|
@ -129,16 +129,25 @@ bdd.describe('visualize app', function describeIndexTests() {
|
|||
})
|
||||
// we can tell we're at level 1 because zoom out is disabled
|
||||
.then(function () {
|
||||
return PageObjects.visualize.getMapZoomOutEnabled();
|
||||
})
|
||||
.then(function (enabled) {
|
||||
expect(enabled).to.be(false);
|
||||
return PageObjects.common.try(function tryingForTime() {
|
||||
return PageObjects.visualize.getMapZoomOutEnabled()
|
||||
.then(function (enabled) {
|
||||
//should be able to zoom more as current config has 0 as min level.
|
||||
expect(enabled).to.be(true);
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return PageObjects.visualize.getTileMapData();
|
||||
return PageObjects.common.try(function tryingForTime() {
|
||||
return PageObjects.visualize.getTileMapData()
|
||||
.then(function (data) {
|
||||
expect(data).to.eql(expectedPrecision2Circles);
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function (data) {
|
||||
expect(data).to.eql(expectedPrecision2Circles);
|
||||
.then(function takeScreenshot() {
|
||||
PageObjects.common.debug('Take screenshot (success)');
|
||||
PageObjects.common.saveScreenshot('map-after-zoom-from-1-to-2');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -284,8 +293,7 @@ bdd.describe('visualize app', function describeIndexTests() {
|
|||
});
|
||||
|
||||
|
||||
|
||||
bdd.it('should zoom in to level 10', function () {
|
||||
bdd.it('should zoom in to level 12', function () {
|
||||
// 6
|
||||
return PageObjects.visualize.clickMapZoomIn()
|
||||
.then(function () {
|
||||
|
@ -301,17 +309,28 @@ bdd.describe('visualize app', function describeIndexTests() {
|
|||
return PageObjects.visualize.clickMapZoomIn();
|
||||
})
|
||||
.then(function () {
|
||||
return PageObjects.visualize.getMapZoomInEnabled();
|
||||
// 10
|
||||
return PageObjects.visualize.clickMapZoomIn();
|
||||
})
|
||||
.then(function (enabled) {
|
||||
// we are at zoom level 9 here and zoom out should still be enabled
|
||||
expect(enabled).to.be(true);
|
||||
.then(function () {
|
||||
// 11
|
||||
return PageObjects.visualize.clickMapZoomIn();
|
||||
})
|
||||
.then(function () {
|
||||
return PageObjects.common.try(function tryingForTime() {
|
||||
return PageObjects.visualize.getMapZoomInEnabled()
|
||||
.then(function (enabled) {
|
||||
expect(enabled).to.be(true);
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return PageObjects.visualize.clickMapZoomIn();
|
||||
})
|
||||
.then(function () {
|
||||
return PageObjects.visualize.getMapZoomInEnabled();
|
||||
})
|
||||
// now we're at level 10 and zoom out should be disabled
|
||||
// now we're at level 12 and zoom out should be disabled
|
||||
.then(function (enabled) {
|
||||
expect(enabled).to.be(false);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue