Skip to content

Commit

Permalink
[Maps] symbolize points with maki icons (elastic#37822)
Browse files Browse the repository at this point in the history
* [Maps] sybmolize points with maki icons

* add from for selecting symbol

* add from for selecting symbol

* get icons working for static colors

* static icon sizing

* refacto symbol_utils

* timing issue

* use SDF icons

* dynamic iconSize

* set icon-anchor

* display symbol icon in map legend

* add unit tests for VectorIcon

* set icon fill based on dark mode

* remove unused file

* fix jest tests

* set icon-opacity

* use size style to configure icon-image small or large

* fix queryRadius error by just hiding other point layer instead of removing

* remove console statement on error

* use maki icon size constants

* add retry around opening set view popover for flaky test
  • Loading branch information
nreese committed Jun 14, 2019
1 parent 0b00499 commit 5f1f069
Show file tree
Hide file tree
Showing 33 changed files with 7,019 additions and 53 deletions.
378 changes: 378 additions & 0 deletions packages/kbn-maki/index.js

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions packages/kbn-maki/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "@kbn/maki",
"version": "6.1.0",
"description": "browser friendly version of @mapbox/maki",
"license": "Apache-2.0",
"main": "index.js",
"devDependencies": {},
"dependencies": {},
"peerDependencies": {}
}
6 changes: 6 additions & 0 deletions packages/kbn-maki/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# @kbn/maki

[@mapbox/maki](https://www.npmjs.com/package/@mapbox/maki) only works in node.js.
See https://github.com/mapbox/maki/issues/462 for details.

@kbn/maki is a browser friendly version of @mapbox/maki
1 change: 1 addition & 0 deletions x-pack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@
"@kbn/es-query": "1.0.0",
"@kbn/i18n": "1.0.0",
"@kbn/interpreter": "1.0.0",
"@kbn/maki": "6.1.0",
"@kbn/ui-framework": "1.0.0",
"@mapbox/mapbox-gl-draw": "^1.1.1",
"@samverschueren/stream-to-observable": "^0.3.0",
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/maps/common/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export const GIS_API_PATH = 'api/maps';
export const EMS_DATA_FILE_PATH = 'ems/file';
export const EMS_DATA_TMS_PATH = 'ems/tms';
export const EMS_META_PATH = 'ems/meta';
export const SPRITE_PATH = '/maps/sprite';
export const MAKI_SPRITE_PATH = `${SPRITE_PATH}/maki`;

export const MAP_SAVED_OBJECT_TYPE = 'map';
export const APP_ID = 'maps';
Expand Down
22 changes: 22 additions & 0 deletions x-pack/plugins/maps/common/parse_xml_string.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { parseString } from 'xml2js';

// promise based wrapper around parseString
export async function parseXmlString(xmlString) {
const parsePromise = new Promise((resolve, reject) => {
parseString(xmlString, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});

return await parsePromise;
}
16 changes: 16 additions & 0 deletions x-pack/plugins/maps/common/parse_xml_string.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { parseXmlString } from './parse_xml_string';

describe('parseXmlString', () => {
it('Should parse xml string into JS object', async () => {
const xmlAsObject = await parseXmlString('<foo>bar</foo>');
expect(xmlAsObject).toEqual({
foo: 'bar'
});
});
});
10 changes: 10 additions & 0 deletions x-pack/plugins/maps/public/components/map/mb/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@

import _ from 'lodash';
import mapboxgl from 'mapbox-gl';
import chrome from 'ui/chrome';
import { MAKI_SPRITE_PATH } from '../../../../common/constants';

function relativeToAbsolute(url) {
const a = document.createElement('a');
a.setAttribute('href', url);
return a.href;
}

export async function createMbMapInstance({ node, initialView, scrollZoom }) {
const makiUrl = relativeToAbsolute(chrome.addBasePath(MAKI_SPRITE_PATH));
return new Promise((resolve) => {
const options = {
attributionControl: false,
Expand All @@ -16,6 +25,7 @@ export async function createMbMapInstance({ node, initialView, scrollZoom }) {
version: 8,
sources: {},
layers: [],
sprite: makiUrl
},
scrollZoom
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export class SetView extends React.Component {
});

return (
<EuiForm>
<EuiForm data-test-subj="mapSetViewForm">

{latFormRow}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import _ from 'lodash';
import { parseString } from 'xml2js';
import { parseXmlString } from '../../../../../common/parse_xml_string';
import fetch from 'node-fetch';
import { parse, format } from 'url';

Expand Down Expand Up @@ -76,16 +76,7 @@ export class WmsClient {
}
const body = await resp.text();

const parsePromise = new Promise((resolve, reject) => {
parseString(body, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
return await parsePromise;
return await parseXmlString(body);
}

async getCapabilities() {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import React from 'react';

export const PointIcon = ({ style }) => (
export const CircleIcon = ({ style }) => (
<svg
className="euiIcon euiIcon--medium mapFillableCircle"
xmlns="http://www.w3.org/2000/svg"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import PropTypes from 'prop-types';
import { styleOptionShapes, rangeShape } from '../style_option_shapes';
import { VectorStyle } from '../../../vector_style';
import { ColorGradient } from '../../color_gradient';
import { PointIcon } from './point_icon';
import { CircleIcon } from './circle_icon';
import { getVectorStyleLabel } from '../get_vector_style_label';
import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui';
import { StyleLegendRow } from '../../style_legend_row';
Expand All @@ -23,9 +23,9 @@ function getLineWidthIcons() {
width: '12px',
};
return [
<PointIcon style={{ ...defaultStyle, strokeWidth: '1px' }}/>,
<PointIcon style={{ ...defaultStyle, strokeWidth: '2px' }}/>,
<PointIcon style={{ ...defaultStyle, strokeWidth: '3px' }}/>,
<CircleIcon style={{ ...defaultStyle, strokeWidth: '1px' }}/>,
<CircleIcon style={{ ...defaultStyle, strokeWidth: '2px' }}/>,
<CircleIcon style={{ ...defaultStyle, strokeWidth: '3px' }}/>,
];
}

Expand All @@ -36,9 +36,9 @@ function getSymbolSizeIcons() {
fill: 'grey',
};
return [
<PointIcon style={{ ...defaultStyle, width: '4px' }}/>,
<PointIcon style={{ ...defaultStyle, width: '8px' }}/>,
<PointIcon style={{ ...defaultStyle, width: '12px' }}/>,
<CircleIcon style={{ ...defaultStyle, width: '4px' }}/>,
<CircleIcon style={{ ...defaultStyle, width: '8px' }}/>,
<CircleIcon style={{ ...defaultStyle, width: '12px' }}/>,
];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import { getMakiSymbolSvg, styleSvg, buildSrcUrl } from '../../../symbol_utils';

export class SymbolIcon extends Component {

state = {
imgDataUrl: undefined,
prevSymbolId: undefined,
prevFill: undefined,
}

componentDidMount() {
this._isMounted = true;
this._loadSymbol(this.props.symbolId, this.props.fill);
}

componentDidUpdate() {
this._loadSymbol(this.props.symbolId, this.props.fill);
}

componentWillUnmount() {
this._isMounted = false;
}

async _loadSymbol(nextSymbolId, nextFill) {
if (nextSymbolId === this.state.prevSymbolId
&& nextFill === this.state.prevFill) {
return;
}

let imgDataUrl;
try {
const svg = getMakiSymbolSvg(nextSymbolId);
const styledSvg = await styleSvg(svg, nextFill);
imgDataUrl = buildSrcUrl(styledSvg);
} catch (error) {
// ignore failures - component will just not display an icon
}

if (this._isMounted) {
this.setState({
imgDataUrl,
prevSymbolId: nextSymbolId,
prevFill: nextFill
});
}
}

render() {
if (!this.state.imgDataUrl) {
return null;
}

return (
<img width="16px" src={this.state.imgDataUrl} alt={this.props.symbolId} />
);
}
}

SymbolIcon.propTypes = {
symbolId: PropTypes.string.isRequired,
fill: PropTypes.string.isRequired,
};
Loading

0 comments on commit 5f1f069

Please sign in to comment.