"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.SYMBOL_OPTIONS = exports.PREFERRED_ICONS = exports.CUSTOM_ICON_PIXEL_RATIO = void 0; exports.buildSrcUrl = buildSrcUrl; exports.createSdfIcon = createSdfIcon; exports.getCustomIconId = getCustomIconId; exports.getIconPalette = getIconPalette; exports.getIconPaletteOptions = getIconPaletteOptions; exports.getMakiSymbol = getMakiSymbol; exports.getMakiSymbolAnchor = getMakiSymbolAnchor; exports.styleSvg = styleSvg; var _react = _interopRequireDefault(require("react")); var _xml2js = _interopRequireDefault(require("xml2js")); var _uuid = require("uuid"); var _canvg = require("canvg"); var _bitmapSdf = _interopRequireDefault(require("bitmap-sdf")); var _constants = require("../../../../common/constants"); var _parse_xml_string = require("../../../../common/parse_xml_string"); var _symbol_icon = require("./components/legend/symbol_icon"); var _kibana_services = require("../../../kibana_services"); var _maki_icons = require("./maki_icons"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ // @ts-expect-error const CUSTOM_ICON_PIXEL_RATIO = Math.floor(window.devicePixelRatio * (_constants.CUSTOM_ICON_SIZE / _constants.MAKI_ICON_SIZE) * 0.75); exports.CUSTOM_ICON_PIXEL_RATIO = CUSTOM_ICON_PIXEL_RATIO; const SYMBOL_OPTIONS = Object.entries(_maki_icons.MAKI_ICONS).map(([value, { svg, label }]) => { return { value, label, svg }; }); /** * Converts a SVG icon to a PNG image using a signed distance function (SDF). * * @param {string} svgString - SVG icon as string * @param {number} [renderSize=64] - size of the output PNG (higher provides better resolution but requires more processing) * @param {number} [cutoff=0.25] - balance between SDF inside 1 and outside 0 of icon * @param {number} [radius=0.25] - size of SDF around the cutoff as percent of output icon size * @return {ImageData} image that can be added to a MapLibre map with option `{ sdf: true }` */ exports.SYMBOL_OPTIONS = SYMBOL_OPTIONS; async function createSdfIcon({ svg, renderSize = 64, cutoff = 0.25, radius = 0.25 }) { const buffer = 3; const size = renderSize + buffer * 4; const svgCanvas = document.createElement('canvas'); svgCanvas.width = size; svgCanvas.height = size; const svgCtx = svgCanvas.getContext('2d'); const v = _canvg.Canvg.fromString(svgCtx, svg, { ignoreDimensions: true, offsetX: buffer / 2, offsetY: buffer / 2 }); v.resize(size - buffer, size - buffer); await v.render(); const distances = (0, _bitmapSdf.default)(svgCtx, { channel: 3, cutoff, radius: radius * size }); const canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; const ctx = canvas.getContext('2d'); if (!ctx) { return null; } const imageData = ctx.createImageData(size, size); for (let i = 0; i < size; i++) { for (let j = 0; j < size; j++) { imageData.data[j * size * 4 + i * 4 + 0] = 0; imageData.data[j * size * 4 + i * 4 + 1] = 0; imageData.data[j * size * 4 + i * 4 + 2] = 0; imageData.data[j * size * 4 + i * 4 + 3] = distances[j * size + i] * 255; } } return imageData; } function getMakiSymbol(symbolId) { return _maki_icons.MAKI_ICONS === null || _maki_icons.MAKI_ICONS === void 0 ? void 0 : _maki_icons.MAKI_ICONS[symbolId]; } function getMakiSymbolAnchor(symbolId) { switch (symbolId) { case 'embassy': case 'marker': case 'marker-stroked': return 'bottom'; default: return 'center'; } } function getCustomIconId() { return `${_constants.CUSTOM_ICON_PREFIX_SDF}${(0, _uuid.v4)()}`; } function buildSrcUrl(svgString) { const domUrl = window.URL || window.webkitURL || window; const svg = new Blob([svgString], { type: 'image/svg+xml' }); return domUrl.createObjectURL(svg); } async function styleSvg(svgString, fill, stroke) { const svgXml = await (0, _parse_xml_string.parseXmlString)(svgString); // Elements nested under svg root may define style attribute // Wildcard descendent selector provides more specificity to ensure root svg style attribute is applied instead of children style attributes svgXml.svg.style = ` svg * { ${fill ? `fill: ${fill}` : '#000'} !important; ${stroke ? `stroke: ${stroke}` : '#000'} !important; stroke-width: 1 !important; vector-effect: non-scaling-stroke !important; } `; const builder = new _xml2js.default.Builder(); return builder.buildObject(svgXml); } const ICON_PALETTES = [{ id: 'filledShapes', icons: ['circle', 'marker', 'square', 'star', 'triangle', 'hospital'] }, { id: 'hollowShapes', icons: ['circle-stroked', 'marker-stroked', 'square-stroked', 'star-stroked', 'triangle-stroked'] }]; // PREFERRED_ICONS is used to provide less random default icon values for forms that need default icon values const PREFERRED_ICONS = []; exports.PREFERRED_ICONS = PREFERRED_ICONS; ICON_PALETTES.forEach(iconPalette => { iconPalette.icons.forEach(icon => { if (!PREFERRED_ICONS.includes(icon)) { PREFERRED_ICONS.push(icon); } }); }); function getIconPaletteOptions() { const isDarkMode = (0, _kibana_services.getIsDarkMode)(); return ICON_PALETTES.map(({ id, icons }) => { const iconsDisplay = icons.map(iconId => { const style = { width: '10%', position: 'relative', height: '100%', display: 'inline-block', paddingTop: '4px' }; return /*#__PURE__*/_react.default.createElement("div", { style: style, key: iconId }, /*#__PURE__*/_react.default.createElement(_symbol_icon.SymbolIcon, { className: "mapIcon", symbolId: iconId, fill: isDarkMode ? 'rgb(223, 229, 239)' : 'rgb(52, 55, 65)', svg: getMakiSymbol(iconId).svg })); }); return { value: id, inputDisplay: /*#__PURE__*/_react.default.createElement("div", null, iconsDisplay) }; }); } function getIconPalette(paletteId) { const palette = ICON_PALETTES.find(({ id }) => id === paletteId); return palette ? [...palette.icons] : []; }