[Maps] Support multiple basemap styles (#36074) (#36857)

This commit is contained in:
Nick Peihl 2019-05-21 21:07:43 -07:00 committed by GitHub
parent c80e48721f
commit 558b624ba1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 444 additions and 101 deletions

View file

@ -29,6 +29,9 @@ import worldJson from './world.json';
import EMS_CATALOGUE from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_manifest.json';
import EMS_FILES from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_files.json';
import EMS_TILES from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_tiles.json';
import EMS_STYLE_ROAD_MAP_BRIGHT from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_bright';
import EMS_STYLE_ROAD_MAP_DESATURATED from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_desaturated';
import EMS_STYLE_DARK_MAP from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_dark';
import initialPng from './initial.png';
import toiso3Png from './toiso3.png';
@ -105,6 +108,14 @@ describe('RegionMapsVisualizationTests', function () {
return EMS_TILES;
} else if (url.startsWith('https://files.foobar')) {
return EMS_FILES;
} else if (url.startsWith('https://raster-style.foobar')) {
if (url.includes('osm-bright-desaturated')) {
return EMS_STYLE_ROAD_MAP_DESATURATED;
} else if (url.includes('osm-bright')) {
return EMS_STYLE_ROAD_MAP_BRIGHT;
} else if (url.includes('dark-matter')) {
return EMS_STYLE_DARK_MAP;
}
}
});

View file

@ -99,7 +99,9 @@ export class EMSClient {
constructor({ kbnVersion, manifestServiceUrl, htmlSanitizer, language, landingPageUrl }) {
this._queryParams = {
my_app_version: kbnVersion
elastic_tile_service_tos: 'agree',
my_app_name: 'kibana',
my_app_version: kbnVersion,
};
this._sanitizer = htmlSanitizer ? htmlSanitizer : x => x;
@ -127,7 +129,7 @@ export class EMSClient {
/**
* this internal method is overridden by the tests to simulate custom manifest.
*/
async _getManifest(manifestUrl) {
async getManifest(manifestUrl) {
let result;
try {
const url = extendUrl(manifestUrl, { query: this._queryParams });
@ -187,7 +189,7 @@ export class EMSClient {
_invalidateSettings() {
this._getManifestWithParams = _.once(
async url => this._getManifest(this.extendUrlWithParams(url)));
async url => this.getManifest(this.extendUrlWithParams(url)));
this._getCatalogueService = async serviceType => {
const catalogueManifest = await this._getManifestWithParams(this._manifestServiceUrl);
@ -200,7 +202,7 @@ export class EMSClient {
};
this._wrapServiceAttribute = async (manifestUrl, attr, WrapperClass) => {
const manifest = await this._getManifest(manifestUrl);
const manifest = await this.getManifest(manifestUrl);
if (_.has(manifest, attr)) {
return manifest[attr].map(config => {
return new WrapperClass(config, this);

View file

@ -17,33 +17,83 @@
* under the License.
*/
import _ from 'lodash';
import { ORIGIN } from './origin';
export class TMSService {
_getTileJson = _.once(
async url => this._emsClient.getManifest(this._emsClient.extendUrlWithParams(url)));
constructor(config, emsClient) {
this._config = config;
this._emsClient = emsClient;
}
getUrlTemplate() {
return this._emsClient.extendUrlWithParams(this._config.url);
_getFormatsOfType(type) {
const formats = this._config.formats.filter(format => {
const language = this._emsClient.getLocale();
return format.locale === language && format.format === type;
});
return formats;
}
_getDefaultStyleUrl() {
const defaultStyle = this._getFormatsOfType('raster')[0];
if (defaultStyle && defaultStyle.hasOwnProperty('url')) {
return defaultStyle.url;
}
}
async getUrlTemplate() {
const tileJson = await this._getTileJson(this._getDefaultStyleUrl());
return this._emsClient.extendUrlWithParams(tileJson.tiles[0]);
}
getDisplayName() {
const serviceName = this._emsClient.getValueInLanguage(this._config.name);
return serviceName;
}
getAttributions() {
const attributions = this._config.attribution.map(attribution => {
const url = this._emsClient.getValueInLanguage(attribution.url);
const label = this._emsClient.getValueInLanguage(attribution.label);
return {
url: url,
label: label
};
});
return attributions;
}
getHTMLAttribution() {
return this._emsClient.sanitizeMarkdown(this._config.attribution);
const attributions = this._config.attribution.map(attribution => {
const url = this._emsClient.getValueInLanguage(attribution.url);
const label = this._emsClient.getValueInLanguage(attribution.label);
const html = url ? `<a rel="noreferrer noopener" href="${url}">${label}</a>` : label;
return this._emsClient.sanitizeHtml(`${html}`);
});
return `<p>${attributions.join(' | ')}</p>`;//!!!this is the current convention used in Kibana
}
getMarkdownAttribution() {
return this._config.attribution;
const attributions = this._config.attribution.map(attribution => {
const url = this._emsClient.getValueInLanguage(attribution.url);
const label = this._emsClient.getValueInLanguage(attribution.label);
const markdown = `[${label}](${url})`;
return markdown;
});
return attributions.join('|');
}
getMinZoom() {
return this._config.minZoom;
async getMinZoom() {
const tileJson = await this._getTileJson(this._getDefaultStyleUrl());
return tileJson.minzoom;
}
getMaxZoom() {
return this._config.maxZoom;
async getMaxZoom() {
const tileJson = await this._getTileJson(this._getDefaultStyleUrl());
return tileJson.maxzoom;
}
getId() {

View file

@ -31,6 +31,9 @@ import heatmapRaw from './heatmap_raw.png';
import EMS_CATALOGUE from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_manifest.json';
import EMS_FILES from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_files.json';
import EMS_TILES from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_tiles.json';
import EMS_STYLE_ROAD_MAP_BRIGHT from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_bright';
import EMS_STYLE_ROAD_MAP_DESATURATED from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_desaturated';
import EMS_STYLE_DARK_MAP from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_dark';
function mockRawData() {
const stack = [dummyESResponse];
@ -84,6 +87,14 @@ describe('CoordinateMapsVisualizationTest', function () {
return EMS_TILES;
} else if (url.startsWith('https://files.foobar')) {
return EMS_FILES;
} else if (url.startsWith('https://raster-style.foobar')) {
if (url.includes('osm-bright-desaturated')) {
return EMS_STYLE_ROAD_MAP_DESATURATED;
} else if (url.includes('osm-bright')) {
return EMS_STYLE_ROAD_MAP_BRIGHT;
} else if (url.includes('dark-matter')) {
return EMS_STYLE_DARK_MAP;
}
}
});
}));

View file

@ -25,6 +25,7 @@ import { filter, first } from 'rxjs/operators';
import 'ui/vis/map/service_settings';
import { toastNotifications } from 'ui/notify';
import { uiModules } from 'ui/modules';
import chrome from 'ui/chrome';
const WMS_MINZOOM = 0;
const WMS_MAXZOOM = 22;//increase this to 22. Better for WMS
@ -145,7 +146,7 @@ export function BaseMapsVisualizationProvider(serviceSettings) {
async _updateBaseLayer() {
const DEFAULT_EMS_BASEMAP = 'road_map';
const emsTileLayerId = chrome.getInjected('emsTileLayerId', true);
if (!this._kibanaMap) {
return;
@ -158,7 +159,7 @@ export function BaseMapsVisualizationProvider(serviceSettings) {
const userConfiguredTmsLayer = tmsServices[0];
const initBasemapLayer = userConfiguredTmsLayer
? userConfiguredTmsLayer
: tmsServices.find(s => s.id === DEFAULT_EMS_BASEMAP);
: tmsServices.find(s => s.id === emsTileLayerId.bright);
if (initBasemapLayer) { this._setTmsLayer(initBasemapLayer); }
} catch (e) {
toastNotifications.addWarning(e.message);
@ -198,14 +199,19 @@ export function BaseMapsVisualizationProvider(serviceSettings) {
if (this._kibanaMap.getZoomLevel() > tmsLayer.maxZoom) {
this._kibanaMap.setZoomLevel(tmsLayer.maxZoom);
}
const url = await (await emsServiceSettings).getUrlTemplateForTMSLayer(tmsLayer);
let isDesaturated = this._getMapsParams().isDesaturated;
if (typeof isDesaturated !== 'boolean') {
isDesaturated = true;
}
const isDarkMode = chrome.getUiSettingsClient().get('theme:darkMode');
const meta = await (await emsServiceSettings).getAttributesForTMSLayer(tmsLayer, isDesaturated, isDarkMode);
const showZoomMessage = serviceSettings.shouldShowZoomMessage(tmsLayer);
const options = _.cloneDeep(tmsLayer);
delete options.id;
delete options.url;
delete options.subdomains;
this._kibanaMap.setBaseLayer({
baseLayerType: 'tms',
options: { url, showZoomMessage, ...options }
options: { ...options, showZoomMessage, ...meta, }
});
}

View file

@ -22,6 +22,7 @@ import * as vega from 'vega-lib';
import { VegaBaseView } from './vega_base_view';
import { VegaMapLayer } from './vega_map_layer';
import { i18n } from '@kbn/i18n';
import chrome from 'ui/chrome';
export class VegaMapView extends VegaBaseView {
@ -35,11 +36,13 @@ export class VegaMapView extends VegaBaseView {
const tmsServices = await this._serviceSettings.getTMSServices();
// In some cases, Vega may be initialized twice, e.g. after awaiting...
if (!this._$container) return;
const mapStyle = mapConfig.mapStyle === 'default' ? 'road_map' : mapConfig.mapStyle;
const emsTileLayerId = chrome.getInjected('emsTileLayerId', true);
const mapStyle = mapConfig.mapStyle === 'default' ? emsTileLayerId.bright : mapConfig.mapStyle;
const isDarkMode = chrome.getUiSettingsClient().get('theme:darkMode');
baseMapOpts = tmsServices.find((s) => s.id === mapStyle);
baseMapOpts = {
url: await this._serviceSettings.getUrlTemplateForTMSLayer(baseMapOpts),
...baseMapOpts
...baseMapOpts,
...await this._serviceSettings.getAttributesForTMSLayer(baseMapOpts, true, isDarkMode),
};
if (!baseMapOpts) {
this.onWarn(i18n.translate('vega.mapView.mapStyleNotFoundWarningMessage', {

View file

@ -249,8 +249,17 @@ export default () => Joi.object({
}))
})).default([])
}).default(),
manifestServiceUrl: Joi.string().default('https://catalogue.maps.elastic.co/v7.0/manifest'),
manifestServiceUrl: Joi.string().default('https://catalogue-staging.maps.elastic.co/v7.2/manifest'),
emsLandingPageUrl: Joi.string().default('https://maps.elastic.co/v7.0'),
emsTileLayerId: Joi.object({
bright: Joi.string().default('road_map'),
desaturated: Joi.string().default('road_map_desaturated'),
dark: Joi.string().default('dark_map'),
}).default({
bright: 'road_map',
desaturated: 'road_map_desaturated',
dark: 'dark_map',
}),
}).default(),
i18n: Joi.object({

View file

@ -28,15 +28,15 @@ describe('ems_client', () => {
const emsClient = getEMSClient();
const tiles = await emsClient.getTMSServices();
expect(tiles.length).to.be(1);
expect(tiles.length).to.be(3);
const tileService = tiles[0];
expect(tileService.getUrlTemplate()).to.be('https://tiles-stage.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=7.x.x');
expect(await tileService.getUrlTemplate()).to.be('https://raster-style.foobar/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=7.x.x');
expect (tileService.getHTMLAttribution()).to.be('<p>© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors | <a href="https://www.elastic.co/elastic-maps-service">Elastic Maps Service</a></p>\n');
expect (tileService.getMinZoom()).to.be(0);
expect (tileService.getMaxZoom()).to.be(10);
expect (tileService.getHTMLAttribution()).to.be('<p><a rel="noreferrer noopener" href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> | <a rel="noreferrer noopener" href="https://openmaptiles.org">OpenMapTiles</a> | <a rel="noreferrer noopener" href="https://www.maptiler.com">MapTiler</a> | <a rel="noreferrer noopener" href="https://www.elastic.co/elastic-maps-service">Elastic Maps Service</a></p>');
expect (await tileService.getMinZoom()).to.be(0);
expect (await tileService.getMaxZoom()).to.be(10);
expect (tileService.hasId('road_map')).to.be(true);
@ -49,23 +49,23 @@ describe('ems_client', () => {
const tilesBefore = await emsClient.getTMSServices();
const urlBefore = tilesBefore[0].getUrlTemplate();
expect(urlBefore).to.be('https://tiles-stage.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=7.x.x');
const urlBefore = await tilesBefore[0].getUrlTemplate();
expect(urlBefore).to.be('https://raster-style.foobar/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=7.x.x');
emsClient.addQueryParams({
'foo': 'bar'
});
let tiles = await emsClient.getTMSServices();
let url = tiles[0].getUrlTemplate();
expect(url).to.be('https://tiles-stage.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=7.x.x&foo=bar');
let url = await tiles[0].getUrlTemplate();
expect(url).to.be('https://raster-style.foobar/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=7.x.x&foo=bar');
emsClient.addQueryParams({
'foo': 'schmoo',
'bar': 'foo'
});
tiles = await emsClient.getTMSServices();
url = tiles[0].getUrlTemplate();
expect(url).to.be('https://tiles-stage.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=7.x.x&foo=schmoo&bar=foo');
url = await tiles[0].getUrlTemplate();
expect(url).to.be('https://raster-style.foobar/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=7.x.x&foo=schmoo&bar=foo');
});
@ -122,7 +122,7 @@ describe('ems_client', () => {
{ name: 'name', description: 'nom', type: 'property' } ]);
expect(layer.getDefaultFormatType()).to.be('geojson');
expect(layer.getDefaultFormatUrl()).to.be('https://vector-staging.maps.elastic.co/files/world_countries_v1.geo.json?elastic_tile_service_tos=agree&my_app_version=7.x.x&foo=bar');
expect(layer.getDefaultFormatUrl()).to.be('https://vector-staging.maps.elastic.co/files/world_countries_v1.geo.json?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=7.x.x&foo=bar');
});

View file

@ -23,6 +23,9 @@ import { EMSClient } from '../../../../../core_plugins/tile_map/common/ems_clien
import EMS_CATALOGUE from './ems_mocks/sample_manifest.json';
import EMS_FILES from './ems_mocks/sample_files.json';
import EMS_TILES from './ems_mocks/sample_tiles.json';
import EMS_STYLE_ROAD_MAP_BRIGHT from './ems_mocks/sample_style_bright';
import EMS_STYLE_ROAD_MAP_DESATURATED from './ems_mocks/sample_style_desaturated';
import EMS_STYLE_DARK_MAP from './ems_mocks/sample_style_dark';
export function getEMSClient(options = {}) {
@ -35,7 +38,7 @@ export function getEMSClient(options = {}) {
...options
});
emsClient._getManifest = async (url) => {
emsClient.getManifest = async (url) => {
//simulate network calls
if (url.startsWith('https://foobar')) {
return EMS_CATALOGUE;
@ -43,6 +46,14 @@ export function getEMSClient(options = {}) {
return EMS_TILES;
} else if (url.startsWith('https://files.foobar')) {
return EMS_FILES;
} else if (url.startsWith('https://raster-style.foobar')) {
if (url.includes('osm-bright-desaturated')) {
return EMS_STYLE_ROAD_MAP_DESATURATED;
} else if (url.includes('osm-bright')) {
return EMS_STYLE_ROAD_MAP_BRIGHT;
} else if (url.includes('dark-matter')) {
return EMS_STYLE_DARK_MAP;
}
}
};
return emsClient;

View file

@ -0,0 +1,12 @@
{
"tilejson": "2.0.0",
"name": "OSM Bright",
"attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>",
"minzoom": 0,
"maxzoom": 10,
"bounds": [-180, -85.0511, 180, 85.0511],
"format": "png",
"type": "baselayer",
"tiles": ["https://raster-style.foobar/styles/osm-bright/{z}/{x}/{y}.png"],
"center": [0, 0, 2]
}

View file

@ -0,0 +1,12 @@
{
"tilejson": "2.0.0",
"name": "Dark Matter",
"attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>",
"minzoom": 0,
"maxzoom": 22,
"bounds": [-180, -85.0511, 180, 85.0511],
"format": "png",
"type": "baselayer",
"tiles": ["https://raster-style.foobar/styles/dark-matter/{z}/{x}/{y}.png"],
"center": [0, 0, 2]
}

View file

@ -0,0 +1,12 @@
{
"tilejson": "2.0.0",
"name": "OSM Bright Desaturated",
"attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>",
"minzoom": 0,
"maxzoom": 18,
"bounds": [-180, -85.0511, 180, 85.0511],
"format": "png",
"type": "baselayer",
"tiles": ["https://raster-style.foobar/styles/osm-bright-desaturated/{z}/{x}/{y}.png"],
"center": [0, 0, 2]
}

View file

@ -2,10 +2,87 @@
"services": [
{
"id": "road_map",
"url": "https://tiles-stage.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana",
"minZoom": 0,
"maxZoom": 10,
"attribution": "© [OpenStreetMap](http://www.openstreetmap.org/copyright) contributors | [Elastic Maps Service](https://www.elastic.co/elastic-maps-service)"
"name": { "en": "Road Map - Bright" },
"attribution": [
{
"label": { "en": "OpenStreetMap contributors" },
"url": { "en": "https://www.openstreetmap.org/copyright" }
},
{ "label": { "en": "OpenMapTiles" }, "url": { "en": "https://openmaptiles.org" } },
{ "label": { "en": "MapTiler" }, "url": { "en": "https://www.maptiler.com" } },
{
"label": { "en": "Elastic Maps Service" },
"url": { "en": "https://www.elastic.co/elastic-maps-service" }
}
],
"formats": [
{
"locale": "en",
"format": "vector",
"url": "https://vector-style.foobar/styles/osm-bright/style.json"
},
{
"locale": "en",
"format": "raster",
"url": "https://raster-style.foobar/styles/osm-bright.json"
}
]
},
{
"id": "road_map_desaturated",
"name": { "en": "Road Map" },
"attribution": [
{
"label": { "en": "OpenStreetMap contributors" },
"url": { "en": "https://www.openstreetmap.org/copyright" }
},
{ "label": { "en": "OpenMapTiles" }, "url": { "en": "https://openmaptiles.org" } },
{ "label": { "en": "MapTiler" }, "url": { "en": "https://www.maptiler.com" } },
{
"label": { "en": "Elastic Maps Service" },
"url": { "en": "https://www.elastic.co/elastic-maps-service" }
}
],
"formats": [
{
"locale": "en",
"format": "vector",
"url": "https://vector-style.foobar/styles/osm-bright-desaturated/style.json"
},
{
"locale": "en",
"format": "raster",
"url": "https://raster-style.foobar/styles/osm-bright-desaturated.json"
}
]
},
{
"id": "dark_map",
"name": { "en": "Dark Map" },
"attribution": [
{
"label": { "en": "OpenStreetMap contributors" },
"url": { "en": "https://www.openstreetmap.org/copyright" }
},
{ "label": { "en": "OpenMapTiles" }, "url": { "en": "https://openmaptiles.org" } },
{ "label": { "en": "MapTiler" }, "url": { "en": "https://www.maptiler.com" } },
{
"label": { "en": "Elastic Maps Service" },
"url": { "en": "https://www.elastic.co/elastic-maps-service" }
}
],
"formats": [
{
"locale": "en",
"format": "vector",
"url": "https://vector-style.foobar/styles/dark-matter/style.json"
},
{
"locale": "en",
"format": "raster",
"url": "https://raster-style.foobar/styles/dark-matter.json"
}
]
}
]
}

View file

@ -24,6 +24,9 @@ import url from 'url';
import EMS_CATALOGUE from './ems_mocks/sample_manifest.json';
import EMS_FILES from './ems_mocks/sample_files.json';
import EMS_TILES from './ems_mocks/sample_tiles.json';
import EMS_STYLE_ROAD_MAP_BRIGHT from './ems_mocks/sample_style_bright';
import EMS_STYLE_ROAD_MAP_DESATURATED from './ems_mocks/sample_style_desaturated';
import EMS_STYLE_DARK_MAP from './ems_mocks/sample_style_dark';
import { ORIGIN } from '../../../../../core_plugins/tile_map/common/origin';
describe('service_settings (FKA tilemaptest)', function () {
@ -40,7 +43,12 @@ describe('service_settings (FKA tilemaptest)', function () {
$provide.decorator('mapConfig', () => {
return {
manifestServiceUrl: manifestUrl,
includeElasticMapsService: true
includeElasticMapsService: true,
emsTileLayerId: {
bright: 'road_map',
desaturated: 'road_map_desaturated',
dark: 'dark_map',
},
};
});
}));
@ -60,6 +68,14 @@ describe('service_settings (FKA tilemaptest)', function () {
return EMS_TILES;
} else if (url.startsWith('https://files.foobar')) {
return EMS_FILES;
} else if (url.startsWith('https://raster-style.foobar')) {
if (url.includes('osm-bright-desaturated')) {
return EMS_STYLE_ROAD_MAP_DESATURATED;
} else if (url.includes('osm-bright')) {
return EMS_STYLE_ROAD_MAP_BRIGHT;
} else if (url.includes('dark-matter')) {
return EMS_STYLE_DARK_MAP;
}
}
});
mapConfig = $injector.get('mapConfig');
@ -90,13 +106,13 @@ describe('service_settings (FKA tilemaptest)', function () {
const tmsService = tmsServices[0];
expect(typeof tmsService.url === 'undefined').to.equal(true);
const mapUrl = await serviceSettings.getUrlTemplateForTMSLayer(tmsService);
expect(mapUrl).to.contain('{x}');
expect(mapUrl).to.contain('{y}');
expect(mapUrl).to.contain('{z}');
const attrs = await serviceSettings.getAttributesForTMSLayer(tmsService);
expect(attrs.url).to.contain('{x}');
expect(attrs.url).to.contain('{y}');
expect(attrs.url).to.contain('{z}');
const urlObject = url.parse(mapUrl, true);
expect(urlObject.hostname).to.be('tiles-stage.elastic.co');
const urlObject = url.parse(attrs.url, true);
expect(urlObject.hostname).to.be('raster-style.foobar');
expect(urlObject.query).to.have.property('my_app_name', 'kibana');
expect(urlObject.query).to.have.property('elastic_tile_service_tos', 'agree');
expect(urlObject.query).to.have.property('my_app_version');
@ -107,7 +123,7 @@ describe('service_settings (FKA tilemaptest)', function () {
const tmsService = tmsServices[0];
expect(tmsService).to.have.property('minZoom');
expect(tmsService).to.have.property('maxZoom');
expect(tmsService).to.have.property('attribution').contain('&#169;');
expect(tmsService).to.have.property('attribution').contain('OpenStreetMap');
});
describe('modify - url', function () {
@ -115,8 +131,8 @@ describe('service_settings (FKA tilemaptest)', function () {
let tilemapServices;
async function assertQuery(expected) {
const mapUrl = await serviceSettings.getUrlTemplateForTMSLayer(tilemapServices[0]);
const urlObject = url.parse(mapUrl, true);
const attrs = await serviceSettings.getAttributesForTMSLayer(tilemapServices[0]);
const urlObject = url.parse(attrs.url, true);
Object.keys(expected).forEach(key => {
expect(urlObject.query).to.have.property(key, expected[key]);
});
@ -172,10 +188,11 @@ describe('service_settings (FKA tilemaptest)', function () {
},
{
'id': 'road_map',
'url': 'https://tiles-stage.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3',
'name': 'Road Map - Bright',
'url': 'https://raster-style.foobar/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3',
'minZoom': 0,
'maxZoom': 10,
'attribution': '<p>&#169; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors | <a href="https://www.elastic.co/elastic-maps-service">Elastic Maps Service</a></p>&#10;',
'attribution': '<p><a rel="noreferrer noopener" href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> | <a rel="noreferrer noopener" href="https://openmaptiles.org">OpenMapTiles</a> | <a rel="noreferrer noopener" href="https://www.maptiler.com">MapTiler</a> | <a rel="noreferrer noopener" href="https://www.elastic.co/elastic-maps-service">Elastic Maps Service</a></p>',
'subdomains': []
}
];
@ -185,14 +202,34 @@ describe('service_settings (FKA tilemaptest)', function () {
const expectedService = expected[index];
expect(actualService.id).to.equal(expectedService.id);
expect(actualService.attribution).to.equal(expectedService.attribution);
const url = await serviceSettings.getUrlTemplateForTMSLayer(actualService);
expect(url).to.equal(expectedService.url);
const attrs = await serviceSettings.getAttributesForTMSLayer(actualService);
expect(attrs.url).to.equal(expectedService.url);
});
return Promise.all(assertions);
});
it('should load appropriate EMS attributes for desaturated and dark theme', async () => {
tilemapServices = await serviceSettings.getTMSServices();
const roadMapService = tilemapServices.find(service => service.id === 'road_map');
const desaturationFalse = await serviceSettings.getAttributesForTMSLayer(roadMapService, false, false);
const desaturationTrue = await serviceSettings.getAttributesForTMSLayer(roadMapService, true, false);
const darkThemeDesaturationFalse = await serviceSettings.getAttributesForTMSLayer(roadMapService, false, true);
const darkThemeDesaturationTrue = await serviceSettings.getAttributesForTMSLayer(roadMapService, true, true);
expect(desaturationFalse.url).to.equal('https://raster-style.foobar/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3');
expect(desaturationFalse.maxZoom).to.equal(10);
expect(desaturationTrue.url).to.equal('https://raster-style.foobar/styles/osm-bright-desaturated/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3');
expect(desaturationTrue.maxZoom).to.equal(18);
expect(darkThemeDesaturationFalse.url).to.equal('https://raster-style.foobar/styles/dark-matter/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3');
expect(darkThemeDesaturationFalse.maxZoom).to.equal(22);
expect(darkThemeDesaturationTrue.url).to.equal('https://raster-style.foobar/styles/dark-matter/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3');
expect(darkThemeDesaturationTrue.maxZoom).to.equal(22);
});
it('should exclude EMS', async () => {
@ -216,8 +253,8 @@ describe('service_settings (FKA tilemaptest)', function () {
expect(tilemapServices.length).to.eql(1);
expect(tilemapServices[0].attribution).to.eql(expected[0].attribution);
expect(tilemapServices[0].id).to.eql(expected[0].id);
const url = await serviceSettings.getUrlTemplateForTMSLayer(tilemapServices[0]);
expect(url).to.equal(expected[0].url);
const attrs = await serviceSettings.getAttributesForTMSLayer(tilemapServices[0]);
expect(attrs.url).to.equal(expected[0].url);
});

View file

@ -24,6 +24,7 @@ import $ from 'jquery';
import _ from 'lodash';
import { zoomToPrecision } from '../../utils/zoom_to_precision';
import { i18n } from '@kbn/i18n';
import { ORIGIN } from '../../../../core_plugins/tile_map/common/origin';
function makeFitControl(fitContainer, kibanaMap) {
@ -650,10 +651,15 @@ export class KibanaMap extends EventEmitter {
_updateDesaturation() {
const tiles = $('img.leaflet-tile-loaded');
if (this._baseLayerIsDesaturated) {
tiles.removeClass('filters-off');
} else if (!this._baseLayerIsDesaturated) {
// Don't apply client-side styling to EMS basemaps
if (_.get(this._baseLayerSettings, 'options.origin') === ORIGIN.EMS) {
tiles.addClass('filters-off');
} else {
if (this._baseLayerIsDesaturated) {
tiles.removeClass('filters-off');
} else if (!this._baseLayerIsDesaturated) {
tiles.addClass('filters-off');
}
}
}

View file

@ -61,14 +61,14 @@ uiModules.get('kibana')
}
__debugStubManifestCalls(manifestRetrieval) {
const oldGetManifest = this._emsClient._getManifest;
this._emsClient._getManifest = manifestRetrieval;
const oldGetManifest = this._emsClient.getManifest;
this._emsClient.getManifest = manifestRetrieval;
return {
removeStub: () => {
delete this._emsClient._getManifest;
delete this._emsClient.getManifest;
//not strictly necessary since this is prototype method
if (this._emsClient._getManifest !== oldGetManifest) {
this._emsClient._getManifest = oldGetManifest;
if (this._emsClient.getManifest !== oldGetManifest) {
this._emsClient.getManifest = oldGetManifest;
}
}
};
@ -118,17 +118,20 @@ uiModules.get('kibana')
if (mapConfig.includeElasticMapsService) {
const servicesFromManifest = await this._emsClient.getTMSServices();
const strippedServiceFromManifest = servicesFromManifest.map((service) => {
const strippedServiceFromManifest = await Promise.all(servicesFromManifest
.filter(tmsService => tmsService.getId() === mapConfig.emsTileLayerId.bright)
.map(async (tmsService) => {
//shim for compatibility
const shim = {
origin: service.getOrigin(),
id: service.getId(),
minZoom: service.getMinZoom(),
maxZoom: service.getMaxZoom(),
attribution: service.getHTMLAttribution()
};
return shim;
});
const shim = {
origin: tmsService.getOrigin(),
id: tmsService.getId(),
minZoom: await tmsService.getMinZoom(),
maxZoom: await tmsService.getMaxZoom(),
attribution: tmsService.getHTMLAttribution()
};
return shim;
})
);
allServices = allServices.concat(strippedServiceFromManifest);
}
@ -154,31 +157,60 @@ uiModules.get('kibana')
return (layer) ? layer.getEMSHotLink() : null;
}
async _getUrlTemplateForEMSTMSLayer(tmsServiceConfig) {
async _getAttributesForEMSTMSLayer(isDesaturated, isDarkMode) {
const tmsServices = await this._emsClient.getTMSServices();
const emsTileLayerId = mapConfig.emsTileLayerId;
let serviceId;
if (isDarkMode) {
serviceId = emsTileLayerId.dark;
} else {
if (isDesaturated) {
serviceId = emsTileLayerId.desaturated;
} else {
serviceId = emsTileLayerId.bright;
}
}
const tmsService = tmsServices.find(service => {
return service.getId() === tmsServiceConfig.id;
return service.getId() === serviceId;
});
return tmsService.getUrlTemplate();
return {
url: await tmsService.getUrlTemplate(),
minZoom: await tmsService.getMinZoom(),
maxZoom: await tmsService.getMaxZoom(),
attribution: await tmsService.getHTMLAttribution(),
origin: ORIGIN.EMS,
};
}
async getUrlTemplateForTMSLayer(tmsServiceConfig) {
async getAttributesForTMSLayer(tmsServiceConfig, isDesaturated, isDarkMode) {
if (tmsServiceConfig.origin === ORIGIN.EMS) {
return this._getUrlTemplateForEMSTMSLayer(tmsServiceConfig);
return this._getAttributesForEMSTMSLayer(isDesaturated, isDarkMode);
} else if (tmsServiceConfig.origin === ORIGIN.KIBANA_YML) {
return tilemapsConfig.deprecated.config.url;
const config = tilemapsConfig.deprecated.config;
const attrs = _.pick(config, [
'url',
'minzoom',
'maxzoom',
'attribution',
]);
return { ...attrs, ...{ origin: ORIGIN.KIBANA_YML } };
} else {
//this is an older config. need to resolve this dynamically.
if (tmsServiceConfig.id === TMS_IN_YML_ID) {
return tilemapsConfig.deprecated.config.url;
const config = tilemapsConfig.deprecated.config;
const attrs = _.pick(config, [
'url',
'minzoom',
'maxzoom',
'attribution',
]);
return { ...attrs, ...{ origin: ORIGIN.KIBANA_YML } };
} else {
//assume ems
return this._getUrlTemplateForEMSTMSLayer(tmsServiceConfig);
return this._getAttributesForEMSTMSLayer(isDesaturated, isDarkMode);
}
}
}
}
async _getFileUrlFromEMS(fileLayerConfig) {

View file

@ -25,8 +25,6 @@ export const SOURCE_DATA_ID_ORIGIN = 'source';
export const DECIMAL_DEGREES_PRECISION = 5; // meters precision
export const ZOOM_PRECISION = 2;
export const DEFAULT_EMS_TILE_LAYER = 'road_map';
export const FEATURE_ID_PROPERTY_NAME = '__kbn__feature_id__';
export const ES_GEO_FIELD_TYPE = {

View file

@ -41,18 +41,19 @@ export async function getEMSResources(emsClient, includeElasticMapsService, lic
};
});
const tmsServices = tmsServicesObjs.map(tmsService => {
const tmsServices = await Promise.all(tmsServicesObjs.map(async tmsService => {
return {
name: tmsService.getDisplayName(),
origin: tmsService.getOrigin(),
id: tmsService.getId(),
minZoom: tmsService.getMinZoom(),
maxZoom: tmsService.getMaxZoom(),
minZoom: await tmsService.getMinZoom(),
maxZoom: await tmsService.getMaxZoom(),
attribution: tmsService.getHTMLAttribution(),
attributionMarkdown: tmsService.getMarkdownAttribution(),
// eslint-disable-next-line max-len
url: useRelativePathToProxy ? `../${GIS_API_PATH}/${EMS_DATA_TMS_PATH}?id=${encodeURIComponent(tmsService.getId())}&x={x}&y={y}&z={z}` : tmsService.getUrlTemplate()
url: useRelativePathToProxy ? `../${GIS_API_PATH}/${EMS_DATA_TMS_PATH}?id=${encodeURIComponent(tmsService.getId())}&x={x}&y={y}&z={z}` : await tmsService.getUrlTemplate()
};
});
}));
return { fileLayers, tmsServices };
}

View file

@ -38,7 +38,7 @@ describe('ems util test', () => {
const useProxy = false;
const resources = await getEMSResources(emsClient, isEmsEnabled, licenseId, useProxy);
expect(resources.tmsServices[0].url.startsWith('https://tiles-stage.elastic.co/')).toBe(true);
expect(resources.tmsServices[0].url.startsWith('https://raster-style.foobar/')).toBe(true);
expect(resources.fileLayers[0].url.startsWith('https://vector-staging.maps.elastic.co/files')).toBe(true);
expect(resources.fileLayers[1].url.startsWith('https://vector-staging.maps.elastic.co/files')).toBe(true);
});

View file

@ -44,6 +44,7 @@ export function maps(kibana) {
return {
showMapsInspectorAdapter: serverConfig.get('xpack.maps.showMapsInspectorAdapter'),
isEmsEnabled: mapConfig.includeElasticMapsService,
emsTileLayerId: mapConfig.emsTileLayerId,
proxyElasticMapsServiceInMaps: mapConfig.proxyElasticMapsServiceInMaps,
emsManifestServiceUrl: mapConfig.manifestServiceUrl,
emsLandingPageUrl: mapConfig.manifestServiceUrl,

View file

@ -6,10 +6,16 @@
import _ from 'lodash';
import { KibanaTilemapSource } from '../shared/layers/sources/kibana_tilemap_source';
import { EMSTMSSource } from '../shared/layers/sources/ems_tms_source';
import chrome from 'ui/chrome';
import { isMetaDataLoaded, getEMSDataSourcesSync, getKibanaTileMap } from '../meta';
import { DEFAULT_EMS_TILE_LAYER } from '../../common/constants';
export function getInitialLayers(savedMapLayerListJSON) {
export function getInitialLayers(savedMapLayerListJSON, isDarkMode) {
const emsTileLayerId = chrome.getInjected('emsTileLayerId', true);
const defaultEmsTileLayer = isDarkMode
? emsTileLayerId.dark
: emsTileLayerId.bright;
if (savedMapLayerListJSON) {
return JSON.parse(savedMapLayerListJSON);
@ -26,7 +32,7 @@ export function getInitialLayers(savedMapLayerListJSON) {
}
if (!isMetaDataLoaded()) {
const descriptor = EMSTMSSource.createDescriptor(DEFAULT_EMS_TILE_LAYER);
const descriptor = EMSTMSSource.createDescriptor(defaultEmsTileLayer);
const source = new EMSTMSSource(descriptor);
const layer = source.createDefaultLayer();
return [

View file

@ -8,6 +8,19 @@ jest.mock('../meta', () => {
return {};
});
jest.mock('ui/chrome', () => ({
getInjected: (key) => {
if (key === 'emsTileLayerId') {
return {
bright: 'road_map',
desaturated: 'road_map_desaturated',
dark: 'dark_map',
};
}
throw new Error(`Unexpected call to chrome.getInjected with key ${key}`);
}
}));
import { getInitialLayers } from './get_initial_layers';
const mockKibanaDataSource = {
@ -85,6 +98,33 @@ describe('Saved object does not have layer list', () => {
}]);
});
it('Should use the default dark EMS layer when Kibana dark theme is set', () => {
mockDataSourceResponse();
require('../meta').isMetaDataLoaded = () => {
return false;
};
const layers = getInitialLayers(null, true);
expect(layers).toEqual([{
alpha: 1,
__dataRequests: [],
id: layers[0].id,
applyGlobalQuery: true,
label: null,
maxZoom: 24,
minZoom: 0,
sourceDescriptor: {
type: 'EMS_TMS',
id: 'dark_map',
},
style: {
properties: {},
type: 'TILE',
},
type: 'TILE',
visible: true,
}]);
});
it('Should get initial layer from Kibana tilemap data source when Kibana tilemap is configured ', () => {

View file

@ -151,7 +151,8 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage
store.dispatch(setOpenTOCDetails(_.get(uiState, 'openTOCDetails', [])));
}
const layerList = getInitialLayers(savedMap.layerListJSON);
const isDarkMode = config.get('theme:darkMode', false);
const layerList = getInitialLayers(savedMap.layerListJSON, isDarkMode);
store.dispatch(replaceLayerList(layerList));
store.dispatch(setRefreshConfig($scope.refreshConfig));

View file

@ -57,7 +57,7 @@ describe('default use without proxy', () => {
const resources = await getEMSDataSources();
expect(resources.ems.tms[0].url.startsWith('https://tiles-stage.elastic.co/')).toBe(true);
expect(resources.ems.tms[0].url.startsWith('https://raster-style.foobar')).toBe(true);
expect(resources.ems.file[0].url.startsWith('https://vector-staging.maps.elastic.co/files')).toBe(true);
expect(resources.ems.file[1].url.startsWith('https://vector-staging.maps.elastic.co/files')).toBe(true);
});

View file

@ -48,7 +48,7 @@ export class EMSTMSCreateSourceEditor extends React.Component {
const emsTileOptions = this.state.emsTmsOptionsRaw.map((service) => ({
value: service.id,
text: service.id //due to not having human readable names
text: service.name || service.id
}));

View file

@ -87,7 +87,12 @@ export class EMSTMSSource extends AbstractTMSSource {
}
async getDisplayName() {
return this._descriptor.id;
try {
const emsTmsMeta = await this._getEmsTmsMeta();
return emsTmsMeta.name;
} catch (error) {
return this._descriptor.id;
}
}
async getAttributions() {

View file

@ -34,7 +34,7 @@ export default function ({ getPageObjects, getService, updateBaselines }) {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.home.addSampleDataSet('ecommerce');
await PageObjects.maps.loadSavedMap('[eCommerce] Orders by Country');
await PageObjects.maps.toggleLayerVisibility('road_map');
await PageObjects.maps.toggleLayerVisibility('Road Map - Bright');
await PageObjects.maps.toggleLayerVisibility('United Kingdom');
await PageObjects.maps.toggleLayerVisibility('France');
await PageObjects.maps.toggleLayerVisibility('United States');
@ -62,7 +62,7 @@ export default function ({ getPageObjects, getService, updateBaselines }) {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.home.addSampleDataSet('flights');
await PageObjects.maps.loadSavedMap('[Flights] Origin and Destination Flight Time');
await PageObjects.maps.toggleLayerVisibility('road_map');
await PageObjects.maps.toggleLayerVisibility('Road Map - Bright');
await setTimerangeToCoverAllSampleData();
await PageObjects.maps.enterFullScreen();
});
@ -87,7 +87,7 @@ export default function ({ getPageObjects, getService, updateBaselines }) {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.home.addSampleDataSet('logs');
await PageObjects.maps.loadSavedMap('[Logs] Total Requests and Bytes');
await PageObjects.maps.toggleLayerVisibility('road_map');
await PageObjects.maps.toggleLayerVisibility('Road Map - Bright');
await PageObjects.maps.toggleLayerVisibility('Total Requests by Country');
await setTimerangeToCoverAllSampleData();
await PageObjects.maps.enterFullScreen();