Commit 9b92f67b34d6b6d17bc6c1afe1ae2be4cbce36f6
Committed by
GitHub
Merge pull request #3261 from vvlladd28/improvement/map/create-label-tooltip
[3.0] Optimize created marker label and tooltip
Showing
5 changed files
with
141 additions
and
19 deletions
@@ -77,6 +77,10 @@ export function isUndefined(value: any): boolean { | @@ -77,6 +77,10 @@ export function isUndefined(value: any): boolean { | ||
77 | return typeof value === 'undefined'; | 77 | return typeof value === 'undefined'; |
78 | } | 78 | } |
79 | 79 | ||
80 | +export function isUndefinedOrNull(value: any): boolean { | ||
81 | + return typeof value === 'undefined' || value === null; | ||
82 | +} | ||
83 | + | ||
80 | export function isDefined(value: any): boolean { | 84 | export function isDefined(value: any): boolean { |
81 | return typeof value !== 'undefined'; | 85 | return typeof value !== 'undefined'; |
82 | } | 86 | } |
@@ -452,7 +456,7 @@ export function insertVariable(pattern: string, name: string, value: any): strin | @@ -452,7 +456,7 @@ export function insertVariable(pattern: string, name: string, value: any): strin | ||
452 | const variable = match[0]; | 456 | const variable = match[0]; |
453 | const variableName = match[1]; | 457 | const variableName = match[1]; |
454 | if (variableName === name) { | 458 | if (variableName === name) { |
455 | - result = result.split(variable).join(value); | 459 | + result = result.replace(variable, value); |
456 | } | 460 | } |
457 | match = varsRegex.exec(pattern); | 461 | match = varsRegex.exec(pattern); |
458 | } | 462 | } |
@@ -469,17 +473,17 @@ export function createLabelFromDatasource(datasource: Datasource, pattern: strin | @@ -469,17 +473,17 @@ export function createLabelFromDatasource(datasource: Datasource, pattern: strin | ||
469 | const variable = match[0]; | 473 | const variable = match[0]; |
470 | const variableName = match[1]; | 474 | const variableName = match[1]; |
471 | if (variableName === 'dsName') { | 475 | if (variableName === 'dsName') { |
472 | - label = label.split(variable).join(datasource.name); | 476 | + label = label.replace(variable, datasource.name); |
473 | } else if (variableName === 'entityName') { | 477 | } else if (variableName === 'entityName') { |
474 | - label = label.split(variable).join(datasource.entityName); | 478 | + label = label.replace(variable, datasource.entityName); |
475 | } else if (variableName === 'deviceName') { | 479 | } else if (variableName === 'deviceName') { |
476 | - label = label.split(variable).join(datasource.entityName); | 480 | + label = label.replace(variable, datasource.entityName); |
477 | } else if (variableName === 'entityLabel') { | 481 | } else if (variableName === 'entityLabel') { |
478 | - label = label.split(variable).join(datasource.entityLabel || datasource.entityName); | 482 | + label = label.replace(variable, datasource.entityLabel || datasource.entityName); |
479 | } else if (variableName === 'aliasName') { | 483 | } else if (variableName === 'aliasName') { |
480 | - label = label.split(variable).join(datasource.aliasName); | 484 | + label = label.replace(variable, datasource.aliasName); |
481 | } else if (variableName === 'entityDescription') { | 485 | } else if (variableName === 'entityDescription') { |
482 | - label = label.split(variable).join(datasource.entityDescription); | 486 | + label = label.replace(variable, datasource.entityDescription); |
483 | } | 487 | } |
484 | match = varsRegex.exec(pattern); | 488 | match = varsRegex.exec(pattern); |
485 | } | 489 | } |
@@ -15,7 +15,8 @@ | @@ -15,7 +15,8 @@ | ||
15 | /// | 15 | /// |
16 | 16 | ||
17 | import L, { | 17 | import L, { |
18 | - FeatureGroup, Icon, | 18 | + FeatureGroup, |
19 | + Icon, | ||
19 | LatLngBounds, | 20 | LatLngBounds, |
20 | LatLngTuple, | 21 | LatLngTuple, |
21 | markerClusterGroup, | 22 | markerClusterGroup, |
@@ -32,6 +33,7 @@ import { | @@ -32,6 +33,7 @@ import { | ||
32 | MarkerSettings, | 33 | MarkerSettings, |
33 | PolygonSettings, | 34 | PolygonSettings, |
34 | PolylineSettings, | 35 | PolylineSettings, |
36 | + ReplaceInfo, | ||
35 | UnitedMapSettings | 37 | UnitedMapSettings |
36 | } from './map-models'; | 38 | } from './map-models'; |
37 | import { Marker } from './markers'; | 39 | import { Marker } from './markers'; |
@@ -62,6 +64,10 @@ export default abstract class LeafletMap { | @@ -62,6 +64,10 @@ export default abstract class LeafletMap { | ||
62 | defaultMarkerIconInfo: { size: number[], icon: Icon }; | 64 | defaultMarkerIconInfo: { size: number[], icon: Icon }; |
63 | loadingDiv: JQuery<HTMLElement>; | 65 | loadingDiv: JQuery<HTMLElement>; |
64 | loading = false; | 66 | loading = false; |
67 | + replaceInfoLabelMarker: Array<ReplaceInfo> = []; | ||
68 | + markerLabelText: string; | ||
69 | + replaceInfoTooltipMarker: Array<ReplaceInfo> = []; | ||
70 | + markerTooltipText: string; | ||
65 | 71 | ||
66 | protected constructor(public ctx: WidgetContext, | 72 | protected constructor(public ctx: WidgetContext, |
67 | public $container: HTMLElement, | 73 | public $container: HTMLElement, |
@@ -121,6 +121,12 @@ export interface FormattedData { | @@ -121,6 +121,12 @@ export interface FormattedData { | ||
121 | [key: string]: any | 121 | [key: string]: any |
122 | } | 122 | } |
123 | 123 | ||
124 | +export interface ReplaceInfo { | ||
125 | + variable: string; | ||
126 | + valDec?: number; | ||
127 | + dataKeyName: string | ||
128 | +} | ||
129 | + | ||
124 | export type PolygonSettings = { | 130 | export type PolygonSettings = { |
125 | showPolygon: boolean; | 131 | showPolygon: boolean; |
126 | polygonKeyName: string; | 132 | polygonKeyName: string; |
@@ -15,13 +15,12 @@ | @@ -15,13 +15,12 @@ | ||
15 | /// | 15 | /// |
16 | 16 | ||
17 | import L from 'leaflet'; | 17 | import L from 'leaflet'; |
18 | -import { FormattedData, MarkerSettings, PolygonSettings, PolylineSettings } from './map-models'; | 18 | +import { FormattedData, MarkerSettings, PolygonSettings, PolylineSettings, ReplaceInfo } from './map-models'; |
19 | import { Datasource, DatasourceData } from '@app/shared/models/widget.models'; | 19 | import { Datasource, DatasourceData } from '@app/shared/models/widget.models'; |
20 | import _ from 'lodash'; | 20 | import _ from 'lodash'; |
21 | import { Observable, Observer, of } from 'rxjs'; | 21 | import { Observable, Observer, of } from 'rxjs'; |
22 | import { map } from 'rxjs/operators'; | 22 | import { map } from 'rxjs/operators'; |
23 | -import { createLabelFromDatasource, hashCode, isNumber, isUndefined, padValue } from '@core/utils'; | ||
24 | -import { Form } from '@angular/forms'; | 23 | +import { createLabelFromDatasource, hashCode, isDefinedAndNotNull, isNumber, isUndefined, padValue } from '@core/utils'; |
25 | 24 | ||
26 | export function createTooltip(target: L.Layer, | 25 | export function createTooltip(target: L.Layer, |
27 | settings: MarkerSettings | PolylineSettings | PolygonSettings, | 26 | settings: MarkerSettings | PolylineSettings | PolygonSettings, |
@@ -185,7 +184,7 @@ function parseTemplate(template: string, data: { $datasource?: Datasource, [key: | @@ -185,7 +184,7 @@ function parseTemplate(template: string, data: { $datasource?: Datasource, [key: | ||
185 | } else { | 184 | } else { |
186 | textValue = value; | 185 | textValue = value; |
187 | } | 186 | } |
188 | - template = template.split(variable).join(textValue); | 187 | + template = template.replace(variable, textValue); |
189 | match = /\${([^}]*)}/g.exec(template); | 188 | match = /\${([^}]*)}/g.exec(template); |
190 | } | 189 | } |
191 | 190 | ||
@@ -198,7 +197,7 @@ function parseTemplate(template: string, data: { $datasource?: Datasource, [key: | @@ -198,7 +197,7 @@ function parseTemplate(template: string, data: { $datasource?: Datasource, [key: | ||
198 | while (match !== null) { | 197 | while (match !== null) { |
199 | [actionTags, actionName, actionText] = match; | 198 | [actionTags, actionName, actionText] = match; |
200 | action = createLinkElement(actionName, actionText); | 199 | action = createLinkElement(actionName, actionText); |
201 | - template = template.split(actionTags).join(action); | 200 | + template = template.replace(actionTags, action); |
202 | match = linkActionRegex.exec(template); | 201 | match = linkActionRegex.exec(template); |
203 | } | 202 | } |
204 | 203 | ||
@@ -206,7 +205,7 @@ function parseTemplate(template: string, data: { $datasource?: Datasource, [key: | @@ -206,7 +205,7 @@ function parseTemplate(template: string, data: { $datasource?: Datasource, [key: | ||
206 | while (match !== null) { | 205 | while (match !== null) { |
207 | [actionTags, actionName, actionText] = match; | 206 | [actionTags, actionName, actionText] = match; |
208 | action = createButtonElement(actionName, actionText); | 207 | action = createButtonElement(actionName, actionText); |
209 | - template = template.split(actionTags).join(action); | 208 | + template = template.replace(actionTags, action); |
210 | match = buttonActionRegex.exec(template); | 209 | match = buttonActionRegex.exec(template); |
211 | } | 210 | } |
212 | 211 | ||
@@ -219,6 +218,94 @@ function parseTemplate(template: string, data: { $datasource?: Datasource, [key: | @@ -219,6 +218,94 @@ function parseTemplate(template: string, data: { $datasource?: Datasource, [key: | ||
219 | return res; | 218 | return res; |
220 | } | 219 | } |
221 | 220 | ||
221 | +export function processPattern(template: string, data: { $datasource?: Datasource, [key: string]: any }): Array<ReplaceInfo> { | ||
222 | + const replaceInfo = []; | ||
223 | + try { | ||
224 | + const reg = /\${([^}]*)}/g; | ||
225 | + let match = reg.exec(template); | ||
226 | + while (match !== null) { | ||
227 | + const variableInfo: ReplaceInfo = { | ||
228 | + dataKeyName: '', | ||
229 | + valDec: 2, | ||
230 | + variable: '' | ||
231 | + }; | ||
232 | + const variable = match[0]; | ||
233 | + let label = match[1]; | ||
234 | + let valDec = 2; | ||
235 | + const splitValues = label.split(':'); | ||
236 | + if (splitValues.length > 1) { | ||
237 | + label = splitValues[0]; | ||
238 | + valDec = parseFloat(splitValues[1]); | ||
239 | + } | ||
240 | + | ||
241 | + variableInfo.variable = variable; | ||
242 | + variableInfo.valDec = valDec; | ||
243 | + | ||
244 | + if (label.startsWith('#')) { | ||
245 | + const keyIndexStr = label.substring(1); | ||
246 | + const n = Math.floor(Number(keyIndexStr)); | ||
247 | + if (String(n) === keyIndexStr && n >= 0) { | ||
248 | + variableInfo.dataKeyName = data.$datasource.dataKeys[n].label; | ||
249 | + } | ||
250 | + } else { | ||
251 | + variableInfo.dataKeyName = label; | ||
252 | + } | ||
253 | + replaceInfo.push(variableInfo); | ||
254 | + | ||
255 | + match = reg.exec(template); | ||
256 | + } | ||
257 | + } catch (ex) { | ||
258 | + console.log(ex, template) | ||
259 | + } | ||
260 | + return replaceInfo; | ||
261 | +} | ||
262 | + | ||
263 | +export function fillPattern(markerLabelText: string, replaceInfoLabelMarker: Array<ReplaceInfo>, data: FormattedData) { | ||
264 | + let text = createLabelFromDatasource(data.$datasource, markerLabelText); | ||
265 | + if (replaceInfoLabelMarker) { | ||
266 | + for(const variableInfo of replaceInfoLabelMarker) { | ||
267 | + let txtVal = ''; | ||
268 | + if (variableInfo.dataKeyName && isDefinedAndNotNull(data[variableInfo.dataKeyName])) { | ||
269 | + const varData = data[variableInfo.dataKeyName]; | ||
270 | + if (isNumber(varData)) { | ||
271 | + txtVal = padValue(varData, variableInfo.valDec); | ||
272 | + } else { | ||
273 | + txtVal = varData; | ||
274 | + } | ||
275 | + } | ||
276 | + text = text.replace(variableInfo.variable, txtVal); | ||
277 | + } | ||
278 | + } | ||
279 | + return text; | ||
280 | +} | ||
281 | + | ||
282 | +function prepareProcessPattern(template: string, translateFn?: TranslateFunc): string { | ||
283 | + if (translateFn) { | ||
284 | + template = translateFn(template); | ||
285 | + } | ||
286 | + let actionTags: string; | ||
287 | + let actionText: string; | ||
288 | + let actionName: string; | ||
289 | + let action: string; | ||
290 | + | ||
291 | + let match = linkActionRegex.exec(template); | ||
292 | + while (match !== null) { | ||
293 | + [actionTags, actionName, actionText] = match; | ||
294 | + action = createLinkElement(actionName, actionText); | ||
295 | + template = template.replace(actionTags, action); | ||
296 | + match = linkActionRegex.exec(template); | ||
297 | + } | ||
298 | + | ||
299 | + match = buttonActionRegex.exec(template); | ||
300 | + while (match !== null) { | ||
301 | + [actionTags, actionName, actionText] = match; | ||
302 | + action = createButtonElement(actionName, actionText); | ||
303 | + template = template.replace(actionTags, action); | ||
304 | + match = buttonActionRegex.exec(template); | ||
305 | + } | ||
306 | + return template; | ||
307 | +} | ||
308 | + | ||
222 | export const parseWithTranslation = { | 309 | export const parseWithTranslation = { |
223 | 310 | ||
224 | translateFn: null, | 311 | translateFn: null, |
@@ -233,6 +320,9 @@ export const parseWithTranslation = { | @@ -233,6 +320,9 @@ export const parseWithTranslation = { | ||
233 | parseTemplate(template: string, data: object, forceTranslate = false): string { | 320 | parseTemplate(template: string, data: object, forceTranslate = false): string { |
234 | return parseTemplate(forceTranslate ? this.translate(template) : template, data, this.translate.bind(this)); | 321 | return parseTemplate(forceTranslate ? this.translate(template) : template, data, this.translate.bind(this)); |
235 | }, | 322 | }, |
323 | + prepareProcessPattern(template: string, forceTranslate = false): string { | ||
324 | + return prepareProcessPattern(forceTranslate ? this.translate(template) : template, this.translate.bind(this)); | ||
325 | + }, | ||
236 | setTranslate(translateFn: TranslateFunc) { | 326 | setTranslate(translateFn: TranslateFunc) { |
237 | this.translateFn = translateFn; | 327 | this.translateFn = translateFn; |
238 | } | 328 | } |
@@ -16,7 +16,15 @@ | @@ -16,7 +16,15 @@ | ||
16 | 16 | ||
17 | import L, { LeafletMouseEvent } from 'leaflet'; | 17 | import L, { LeafletMouseEvent } from 'leaflet'; |
18 | import { FormattedData, MarkerSettings } from './map-models'; | 18 | import { FormattedData, MarkerSettings } from './map-models'; |
19 | -import { aspectCache, bindPopupActions, createTooltip, parseWithTranslation, safeExecute } from './maps-utils'; | 19 | +import { |
20 | + aspectCache, | ||
21 | + bindPopupActions, | ||
22 | + createTooltip, | ||
23 | + fillPattern, | ||
24 | + parseWithTranslation, | ||
25 | + processPattern, | ||
26 | + safeExecute | ||
27 | +} from './maps-utils'; | ||
20 | import tinycolor from 'tinycolor2'; | 28 | import tinycolor from 'tinycolor2'; |
21 | import { isDefined } from '@core/utils'; | 29 | import { isDefined } from '@core/utils'; |
22 | import LeafletMap from './leaflet-map'; | 30 | import LeafletMap from './leaflet-map'; |
@@ -74,9 +82,13 @@ export class Marker { | @@ -74,9 +82,13 @@ export class Marker { | ||
74 | } | 82 | } |
75 | 83 | ||
76 | updateMarkerTooltip(data: FormattedData) { | 84 | updateMarkerTooltip(data: FormattedData) { |
85 | + if(!this.map.markerTooltipText || this.settings.useTooltipFunction) { | ||
77 | const pattern = this.settings.useTooltipFunction ? | 86 | const pattern = this.settings.useTooltipFunction ? |
78 | - safeExecute(this.settings.tooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern; | ||
79 | - this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true)); | 87 | + safeExecute(this.settings.tooltipFunction, [this.data, this.dataSources, this.data.dsIndex]) : this.settings.tooltipPattern; |
88 | + this.map.markerTooltipText = parseWithTranslation.prepareProcessPattern(pattern, true); | ||
89 | + this.map.replaceInfoTooltipMarker = processPattern(this.map.markerTooltipText, data); | ||
90 | + } | ||
91 | + this.tooltip.setContent(fillPattern(this.map.markerTooltipText, this.map.replaceInfoTooltipMarker, data)); | ||
80 | if (this.tooltip.isOpen() && this.tooltip.getElement()) { | 92 | if (this.tooltip.isOpen() && this.tooltip.getElement()) { |
81 | bindPopupActions(this.tooltip, this.settings, data.$datasource); | 93 | bindPopupActions(this.tooltip, this.settings, data.$datasource); |
82 | } | 94 | } |
@@ -89,9 +101,13 @@ export class Marker { | @@ -89,9 +101,13 @@ export class Marker { | ||
89 | updateMarkerLabel(settings: MarkerSettings) { | 101 | updateMarkerLabel(settings: MarkerSettings) { |
90 | this.leafletMarker.unbindTooltip(); | 102 | this.leafletMarker.unbindTooltip(); |
91 | if (settings.showLabel) { | 103 | if (settings.showLabel) { |
92 | - const pattern = settings.useLabelFunction ? | 104 | + if(!this.map.markerLabelText || settings.useLabelFunction) { |
105 | + const pattern = settings.useLabelFunction ? | ||
93 | safeExecute(settings.labelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.label; | 106 | safeExecute(settings.labelFunction, [this.data, this.dataSources, this.data.dsIndex]) : settings.label; |
94 | - settings.labelText = parseWithTranslation.parseTemplate(pattern, this.data, true); | 107 | + this.map.markerLabelText = parseWithTranslation.prepareProcessPattern(pattern, true); |
108 | + this.map.replaceInfoLabelMarker = processPattern(this.map.markerLabelText, this.data); | ||
109 | + } | ||
110 | + settings.labelText = fillPattern(this.map.markerLabelText, this.map.replaceInfoLabelMarker, this.data); | ||
95 | this.leafletMarker.bindTooltip(`<div style="color: ${settings.labelColor};"><b>${settings.labelText}</b></div>`, | 111 | this.leafletMarker.bindTooltip(`<div style="color: ${settings.labelColor};"><b>${settings.labelText}</b></div>`, |
96 | { className: 'tb-marker-label', permanent: true, direction: 'top', offset: this.tooltipOffset }); | 112 | { className: 'tb-marker-label', permanent: true, direction: 'top', offset: this.tooltipOffset }); |
97 | } | 113 | } |